本系列文已改編成書「Arduino 自造趣:結合 JavaScript x Vue x Phaser 輕鬆打造個人遊戲機」,本書改用 Vue3 與 TypeScript 全面重構且加上更詳細的說明,
在此感謝 iT 邦幫忙、博碩文化與編輯小 p 的協助,歡迎大家前往購書,鱈魚在此感謝大家 (。・∀・)。
若想 DIY 卻不知道零件去哪裡買的讀者,可以參考此連結 。( •̀ ω •́ )✧
本篇來實際建立遊戲場景!
建立遊戲場景組件 game-scene.vue
,並提供「跳躍按鈕腳位」與「蹲下按鈕腳位」之 props
。
src\components\window-app-google-dino\game-scene.vue <script>
/**
* @typedef {import('@/script/modules/port-transceiver').default} PortTransceiver
*
* @typedef {import('@/types/type').PinInfo} PinInfo
* @typedef {import('@/types/type').PinCapability} PinCapability
*/
import { mapState } from 'vuex';
export default {
name: 'GameScene',
components: {},
props: {
/** 跳躍按鈕腳位 */
jumpPin: {
type: Object,
default() {
return null;
},
},
/** 蹲下按鈕腳位 */
squatPin: {
type: Object,
default() {
return null;
},
},
},
data() {
return {};
},
computed: {
...mapState({
/** @type {PortTransceiver} */
portTransceiver: (state) => state.core.transceiver,
}),
},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {},
};
src\components\window-app-google-dino\game-scene.vue <template lang="pug">
.game-scene
src\components\window-app-google-dino\game-scene.vue <style scoped lang="sass">
@import '@/styles/quasar.variables.sass'
.game-scene
width: 100%
height: 100%
user-select: none
接著在 window-app-google-dino.vue
引入 game-scene.vue
。
src\components\window-app-google-dino\window-app-google-dino.vue <script>
// ...
import GameScene from './game-scene.vue';
// ...
export default {
name: 'WindowAppGoogleDino',
components: {
'base-window': BaseWindow,
'base-select-pin': BaseSelectPin,
'game-scene': GameScene,
},
// ...
};
src\components\window-app-google-dino\window-app-google-dino.vue <template lang="pug">
base-window.window-app-google-dino(
// ...
)
.h-full.overflow-hidden
// 遊戲場景
game-scene(:jump-pin='jumpPin', :squat-pin='squatPin')
// 設定欄位
// ...
接下來讓我們依序加入更多內容吧!╰( *´ ︶ *` ** )╯
首先加入遊戲狀態列舉。
src\components\window-app-google-dino\game-scene.vue <script>
/**
* @typedef {import('@/script/modules/port-transceiver').default} PortTransceiver
*
* @typedef {import('@/types/type').PinInfo} PinInfo
* @typedef {import('@/types/type').PinCapability} PinCapability
*/
/**
* @enum {string}
*/
export const GameStatus = {
/** 等待開始 */
STANDBY: 'standby',
/** 遊戲開始 */
START: 'start',
/** 遊戲結束 */
GAME_OVER: 'gameOver',
};
import { mapState } from 'vuex';
export default {
name: 'GameScene',
// ...
};
將
GameStatus
export
出去,讓別的檔案也能夠使用。
接著新增遊戲場景相關變數。
src\components\window-app-google-dino\game-scene.vue <script>
// ...
export default {
name: 'GameScene',
// ...
data() {
return {
gameStatus: GameStatus.STANDBY,
timer: null,
timeCounter: 0,
score: 0,
};
},
// ...
};
再來新增遊戲基本功能,在 methods
新增以下 Method:
start()
:開始遊戲。over()
:結束遊戲。tick()
:更新遊戲內容,透過計時器呼叫。畫面更新動畫最佳的實踐方式是使用
requestAnimationFrame()
,不過為了簡單實現,在此使用setInterval()
,大家可以嘗試使用requestAnimationFrame()
實作看看 ( ´ ▽ ` )ノ
src\components\window-app-google-dino\game-scene.vue <script>
// ...
export default {
name: 'GameScene',
// ...
methods: {
start() {
if (this.gameStatus === GameStatus.START) {
return;
}
// 初始化變數
this.gameStatus = GameStatus.START;
this.score = 0;
this.timeCounter = 0;
// 計時器啟動
this.timer = setInterval(() => {
this.tick();
}, 10);
},
over() {
this.gameStatus = GameStatus.GAME_OVER;
clearInterval(this.timer);
},
tick() {
this.timeCounter++;
// score 每 0.1 秒增加一次
if (this.timeCounter % 10 === 0) {
this.score++;
}
},
},
};
大家可能會有一個問題,為甚麼 if (this.timeCounter % 10 === 0)
,這段程式會是每 0.1 秒執行一次呢?
因為 timer 是 0.01 秒呼叫一次,而 this.timeCounter % 10 === 0
剛好就是每 10 次才成立一次。
結果就會是 0.01 * 10 = 0.1 秒執行一次了,以此類推:
this.timeCounter % 50 === 0
this.timeCounter % 100 === 0
為了讓遊戲整體更 8 位元一點,我們加入 8 位元風格字體。
字體依舊來自 Google Font,讚嘆偉大的 Google ◝( •∀• )◟
src\components\window-app-google-dino\game-scene.vue <style scoped lang="sass">
@import url('https://fonts.googleapis.com/css2?family=DotGothic16&display=swap')
@import '@/styles/quasar.variables.sass'
.game-scene
width: 100%
height: 100%
user-select: none
font-family: 'DotGothic16', sans-serif
接著在場景中新增「地面」、「分數」與「提示文字」。
src\components\window-app-google-dino\game-scene.vue <template lang="pug">
.game-scene
.ground
.scoreboard
| 00000
.prompt-text
template(v-if='gameStatus === "standby"')
.text-30px
| 按任意按鈕開始遊戲
template(v-if='gameStatus === "gameOver"')
.text-34px.mb-10px
| GAME OVER
.text-20px
| 按任意按鈕重新開始
src\components\window-app-google-dino\game-scene.vue <style scoped lang="sass">
@import url('https://fonts.googleapis.com/css2?family=DotGothic16&display=swap')
@import '@/styles/quasar.variables.sass'
.game-scene
width: 100%
height: 100%
user-select: none
font-family: 'DotGothic16', sans-serif
.ground
position: absolute
bottom: 0px
left: 0px
width: 100%
height: 50px
border-top: 2px solid $grey-4
.scoreboard
position: absolute
top: 20px
right: 26px
font-size: 26px
letter-spacing: 2px
.prompt-text
position: absolute
top: 46%
left: 50%
transform: translate(-50%, -50%)
letter-spacing: 4px
font-size: 30px
user-select: none
text-align: center
目前應該長這樣。
讓提示文字有閃爍特效,增加點電玩感,增加 .blink
Class。
利用 animation step 達成補幀上跳躍感
src\components\window-app-google-dino\game-scene.vue <style scoped lang="sass">
@import url('https://fonts.googleapis.com/css2?family=DotGothic16&display=swap')
@import '@/styles/quasar.variables.sass'
.game-scene
width: 100%
height: 100%
user-select: none
font-family: 'DotGothic16', sans-serif
// ...
.blink
animation: blink 2s infinite steps(3)
@keyframes blink
0%, 100%
opacity: 1
50%
opacity: 0
在要閃爍的文字加上 .blink
src\components\window-app-google-dino\game-scene.vue <template lang="pug">
.game-scene
// ...
.prompt-text
template(v-if='gameStatus === "standby"')
.text-30px.blink
| 按任意按鈕開始遊戲
template(v-if='gameStatus === "gameOver"')
// ...
有點大型電玩的感覺了。
最後是分數的部分,透過 computed
提供補 0 後的結果。
src\components\window-app-google-dino\game-scene.vue <script>
// ...
export default {
name: 'GameScene',
// ...
computed: {
// ...
scoreboard() {
return `${this.score}`.padStart(5, '0');
},
},
};
將 scoreboard
加入 Pug。
src\components\window-app-google-dino\game-scene.vue <template lang="pug">
.game-scene
.ground
.scoreboard
| {{ scoreboard }}
.prompt-text
// ...
最後呼叫看看 start()
,看看遊戲開始的感覺。
在 .game-scene
加上 @click
,點擊看看遊戲有沒有開始。
src\components\window-app-google-dino\game-scene.vue <template lang="pug">
.game-scene(@click='start')
// ...
基本場景內容完成!再來就是加入角色了!
以上程式碼已同步至 GitLab,大家可以前往下載: