iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Vue.js

邊學邊做:Vue.js 實戰養成計畫系列 第 29

Day 29:生物探測器 — 小遊戲(v-if / v-for / methods / emit)

  • 分享至 

  • xImage
  •  

我們今天要延續昨天的Final Mission — 5 日實作計畫,設計一個Galactic Explorer!
今日目標:做一個「探索宇宙生物」的互動頁。

我們要讓「宇宙生物探測器」動起來,做一個小小的互動遊戲~!
你會使用到 Vue 的

  • v-if / v-for
  • methods(邏輯處理)
  • Math.random()(隨機抽取)
  • localStorage 保存遊戲進度
  • <transition> 動畫效果

1)使用新頁面 /scanner

src/views/ 下新建的 Scanner.vue

<template>
  <main class="scanner">
    <h1>🛸 生物探測器</h1>
    <p class="desc">探索宇宙未知生物!每次探索會消耗 10 點燃料。</p>

    <!-- 燃料條 -->
    <div class="fuel-bar">
      <div class="fuel" :style="{ width: fuel + '%' }"></div>
    </div>
    <p>🔥 燃料:{{ fuel }}/100</p>

    <!-- 按鈕 -->
    <el-button type="primary" @click="explore" :disabled="fuel <= 0">
      🚀 探索宇宙
    </el-button>

    <p v-if="fuel <= 0" class="warn">⚠️ 燃料不足,請返回基地補給!</p>

    <!-- 探測到的生物 -->
    <transition name="fade">
      <section v-if="latestCreature" class="creature-card">
        <h2>🪐 探測到新生物:{{ latestCreature.name }}</h2>
        <img :src="latestCreature.image" :alt="latestCreature.name" />
        <p>{{ latestCreature.desc }}</p>
      </section>
    </transition>

    <!-- 歷史紀錄 -->
    <h3>📜 探測紀錄</h3>
    <ul class="creature-list">
      <li v-for="(c, index) in creatures" :key="index" class="creature-item">
        <img :src="c.image" :alt="c.name" />
        <div>
          <strong>{{ c.name }}</strong>
          <p>{{ c.desc }}</p>
        </div>
      </li>
    </ul>
  </main>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue'

// 燃料與生物列表
const fuel = ref(100)
const creatures = ref([])
const latestCreature = ref(null)

// 初始載入 localStorage
onMounted(() => {
  const savedFuel = localStorage.getItem('fuel')
  const savedCreatures = localStorage.getItem('creatures')
  if (savedFuel) fuel.value = Number(savedFuel)
  if (savedCreatures) creatures.value = JSON.parse(savedCreatures)
})

// 自動保存
watch([fuel, creatures], () => {
  localStorage.setItem('fuel', fuel.value)
  localStorage.setItem('creatures', JSON.stringify(creatures.value))
}, { deep: true })

// 生物資料庫(可擴充!)
const allCreatures = [
  { name: '光之鯨', desc: '漂浮在星雲之間,以能量為食的巨大生物。', image: '/img/whale.png' },
  { name: '星塵狐', desc: '夜行於銀河邊緣,毛皮閃爍如流星。', image: '/img/fox.png' },
  { name: '重力球體', desc: '由暗物質凝聚而成,能扭曲周圍時空。', image: '/img/gravity.png' },
  { name: '晶核鳥', desc: '體內蘊含晶石能量的飛行生物,能吸收恆星光。', image: '/img/bird.png' },
  { name: '冥環蛇', desc: '藏身於行星環帶的神秘物種,能自由穿越物質。', image: '/img/snake.png' }
]

// 探索函式
function explore() {
  if (fuel.value <= 0) return

  // 消耗燃料
  fuel.value -= 10

  // 隨機抽生物
  const randomIndex = Math.floor(Math.random() * allCreatures.length)
  const found = allCreatures[randomIndex]

  // 顯示動畫生物
  latestCreature.value = found

  // 若沒有重複才新增
  if (!creatures.value.find(c => c.name === found.name)) {
    creatures.value.unshift(found)
  }
}
</script>

<style scoped>
.scanner {
  max-width: 800px;
  margin: auto;
  padding: 24px;
  color: #e0e7ff;
  font-family: 'Orbitron', system-ui;
  text-align: center;
}
.desc { color: #94a3b8; margin-bottom: 12px; }

.fuel-bar {
  width: 100%;
  height: 20px;
  background: #1e293b;
  border-radius: 10px;
  overflow: hidden;
  margin: 8px 0;
}
.fuel {
  height: 100%;
  background: linear-gradient(90deg, #22d3ee, #a78bfa);
  transition: width 0.5s ease;
}
.warn { color: #fca5a5; margin: 8px 0; }

.creature-card {
  margin-top: 20px;
  padding: 16px;
  border: 1px solid rgba(167,139,250,0.4);
  border-radius: 12px;
  background: rgba(15,23,42,0.8);
}
.creature-card img {
  width: 200px;
  margin: 12px auto;
  display: block;
}
.creature-list {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  list-style: none;
  margin-top: 16px;
  padding: 0;
}
.creature-item {
  display: flex;
  gap: 12px;
  align-items: center;
  background: rgba(30,41,59,0.7);
  border-radius: 12px;
  padding: 10px;
}
.creature-item img {
  width: 60px;
  height: 60px;
  object-fit: cover;
  border-radius: 50%;
}
.fade-enter-active, .fade-leave-active { transition: opacity .5s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>

2)在 router 註冊 /scanner

打開 src/router/index.js:

import Scanner from '../views/Scanner.vue'

{ path: '/scanner', name: 'scanner', component: Scanner },

https://ithelp.ithome.com.tw/upload/images/20251010/20178644CXWK5A0iMb.png
如此我們就完成探測生物的小遊戲,重新整理後仍保留探測進度!圖片的部分大家可以學習day27的部分,自行上網挑選加入。

參考資源
https://vuejs.org/guide
https://www.runoob.com/vue3


上一篇
Day 28:宇宙觀察日誌 — localStorage 資料保存
下一篇
Day 30:星際總結篇 — 美術與體驗優化
系列文
邊學邊做:Vue.js 實戰養成計畫30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言