iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Modern Web

你渴望連結嗎?將 Web 與硬體連上線吧!系列 第 24

D23 - 「不斷線的侏儸紀」:有一隻小恐龍在跑步(使用 GSAP 動畫)

本系列文已改編成書「Arduino 自造趣:結合 JavaScript x Vue x Phaser 輕鬆打造個人遊戲機」,本書改用 Vue3 與 TypeScript 全面重構且加上更詳細的說明,

在此感謝 iT 邦幫忙、博碩文化與編輯小 p 的協助,歡迎大家前往購書,鱈魚在此感謝大家 (。・∀・)。

若想 DIY 卻不知道零件去哪裡買的讀者,可以參考此連結 。( •̀ ω •́ )✧


來建立我們的主角小恐龍吧。

建立組件

建立小恐龍組件 dino.vue

src\components\window-app-google-dino\dino.vue

<template lang="pug">
.dino
</template>

<style scoped lang="sass">
.dino
  position: absolute
  width: 80px
  bottom: 30px
  left: 30px
</style>

<script>
export default {
  name: 'Dino',
  components: {},
  props: {},
  data() {
    return {};
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  beforeDestroy() {},
  methods: {},
};
</script>

game-scene.vue 引入 dino.vue

src\components\window-app-google-dino\game-scene.vue <script>

// ...

import { mapState } from 'vuex';

import Dino from './dino.vue';

export default {
  name: 'GameScene',
  components: {
    dino: Dino,
  },
  // ...
};

src\components\window-app-google-dino\game-scene.vue <template lang="pug">

.game-scene(@click='start')
  .ground
  dino(ref='dino')

  // ...

注入靈魂

再來幫小恐龍加入圖片、動畫等等效果,注入靈魂。

將準備好的圖片放入 src\assets\google-dino 目錄並新增以下變數。

src\components\window-app-google-dino\dino.vue <script>

/**
 * @typedef {Object} Status 人物狀態
 * @property {Boolean} jumping 跳躍
 * @property {Boolean} squatting 蹲下
 */

import imgJump from '@/assets/google-dino/dino-jump.png';

export default {
  name: 'Dino',
  // ...
  data() {
    return {
      /** @type {Status} */
      status: {
        jumping: false,
        squatting: false,
      },

      /** 目前顯示圖片 */
      imgSrc: imgJump,

      /** 計時器,處理圖片動畫 */
      timer: null,
    };
  },
  // ...
};

methods 新增功能。

import imgRun01 from '@/assets/google-dino/dino-run-1.png';
import imgRun02 from '@/assets/google-dino/dino-run-2.png';
import imgJump from '@/assets/google-dino/dino-jump.png';
import imgDie from '@/assets/google-dino/dino-die.png';
import imgSquat01 from '@/assets/google-dino/dino-squat-1.png';
import imgSquat02 from '@/assets/google-dino/dino-squat-2.png';

export default {
  name: 'Dino',
  // ...
	methods: {
    /** 開始 */
    start() {
      this.status.jumping = false;
      this.status.squatting = false;

      this.timer = setInterval(() => {
        this.processSpriteImg();
      }, 150);
    },
    /** 結束 */
    over() {
      this.imgSrc = imgDie;
      clearInterval(this.timer);
    },

    /** 透過切換圖片的方式動畫 */
    processSpriteImg() {
      /** @type {Status} */
      const status = this.status;

      // 跑步
      if (this.imgSrc === imgRun01) {
        this.imgSrc = imgRun02;
      } else {
        this.imgSrc = imgRun01;
      }
    },
  },
  // ...
};

透過 props 取得遊戲狀態並偵測狀態變化,執行對應動作。

src\components\window-app-google-dino\dino.vue <script>

// ...

import { GameStatus } from './game-scene.vue';

export default {
  name: 'Dino',
	// ...
	props: {
    gameStatus: {
      type: String,
      default: '',
    },
  },
  // ...
	watch: {
    gameStatus(status) {
      if (status === GameStatus.START) {
        this.start();
        return;
      }

      if (status === GameStatus.GAME_OVER) {
        this.over();
        return;
      }
    },
  },
  // ...
};

記得 game-scene.vue 模板中的 dino 也要加入參數。

src\components\window-app-google-dino\game-scene.vue <template lang="pug">

.game-scene(@click='start')
  .ground
  dino(ref='dino', :game-status='gameStatus')

	// ...

最後在 .dino 下方加個 img 顯示小恐龍圖片。

src\components\window-app-google-dino\dino.vue <template lang="pug">

.dino
  img(:src='imgSrc')

目前應該會長這樣。

Untitled

點一下畫面,看看遊戲開始後小恐龍會不會開始跑步。

D23 - 小恐龍跑步.gif

小恐龍成功開跑!

更多動作

再來就是讓小恐龍跳躍和蹲下了。

跳躍

跳躍動畫使用 GSAP 實現。

GSAP 是一套專門處理動畫與特效的 JS 套件,可以實現動畫時間軸管理等等功能,功能非常強大。

概念為「透過 GSAP 控制跳躍高度數值,並將高度數值綁定至 CSS 樣式,呈現 DOM 移動效果」。

實作過程如下:

  • data()

    • yOffset:目前跳躍高度。
    • gsapAni:儲存 GSAP 動畫變數。
  • methods

    • processSpriteImg():加入跳躍圖片
    • initGsapAni():初始化 GSAP 物件
    • jump():開始跳躍。呼叫 GSAP 跳躍動畫物件
  • computed

    • style:將變數轉為 CSS 屬性
  • created()

    呼叫 initGsapAni()

src\components\window-app-google-dino\dino.vue <script>

// ...

import { gsap } from 'gsap';

export default {
  name: 'Dino',
  // ...
  data() {
    return {
      // ...

      /** 跳躍量 */
      yOffset: 0,

			/** GSAP 動畫 */
      gsapAni: {
        jump: null,
      },
    };
  },
	computed: {
    style() {
      return {
        transform: `translateY(-${this.yOffset}px)`,
      };
    },
  },
	created() {
    // 初始化 GSAP 動畫物件
    this.initGsapAni();
  },
	// ...
	methods: {
    // ...

		/** 透過切換圖片的方式動畫 */
    processSpriteImg() {
      /** @type {Status} */
      const status = this.status;

			// 跳躍
      if (status.jumping) {
        this.imgSrc = imgJump;
        return;
      }

      // 跑步
      if (this.imgSrc === imgRun01) {
        this.imgSrc = imgRun02;
      } else {
        this.imgSrc = imgRun01;
      }
    },

    /** 初始化 GSAP 動畫 */
    initGsapAni() {
      /** @type {Status} */
      const status = this.status;

      const jumpTimeline = gsap.timeline();

			// 動畫持續時間
      const duration = 0.32;

      jumpTimeline
        .to(this, {
          duration,
          yOffset: 150,
          ease: 'power1.out',
          onStart() {
            status.jumping = true;
          },
        })
        .to(this, {
          duration,
          yOffset: 0,
          ease: 'power1.in',
          onComplete() {
            status.jumping = false;
          },
        })
        .pause();

      this.gsapAni.jump = jumpTimeline;
    },

    /** 跳躍 */
    jump() {
			if (this.status.jumping) return;

      this.gsapAni.jump.restart();
    },
  // ...
};

除了綁定 style 外,加入 @click,測試跳躍動畫。

src\components\window-app-google-dino\dino.vue <template lang="pug">

.dino(:style='style', @click='jump')
  img(:src='imgSrc')

點擊看看小恐龍會不會跳躍。

D23 - 小恐龍跳躍.gif

成功起跳!

蹲下

蹲下動作就簡單很多,單純就是狀態切換而已。

實作過程如下:

  • methods
    • processSpriteImg():加入蹲下圖片
    • setSquat():設定是否蹲下

src\components\window-app-google-dino\dino.vue <script>

// ...

export default {
  name: 'Dino',
	// ...
	methods: {
    // ...

		/** 透過切換圖片的方式動畫 */
    processSpriteImg() {
			/** @type {Status} */
      const status = this.status;

			// 跳躍
      if (status.jumping) {
        this.imgSrc = imgJump;
        return;
      }

			// 蹲下
      if (status.squatting) {
        if (this.imgSrc === imgSquat01) {
          this.imgSrc = imgSquat02;
        } else {
          this.imgSrc = imgSquat01;
        }
        return;
      }

      // 跑步
      if (this.imgSrc === imgRun01) {
        this.imgSrc = imgRun02;
      } else {
        this.imgSrc = imgRun01;
      }
    },

    // ...

		/** 設定是否蹲下 */
    setSquat(status = true) {
      this.status.squatting = status;
    },
};

這次使用右鍵測試「蹲下」動作,按著蹲下,放開回復動作。

src\components\window-app-google-dino\dino.vue <template lang="pug">

.dino(
  :style='style',
  @click='jump',
  @mousedown.right='setSquat(true)',
  @mouseup.right='setSquat(false)',
  @contextmenu.prevent
)
  img(:src='imgSrc')

嘗試看看是否左鍵跳躍,右鍵蹲下。

D23 - 小恐龍蹲下.gif

成功!恭喜復活恐龍惹!(´,,•ω•,,)

最後是最容易忘記也最重要的一步,就是銷毀計時器。利用 beforeDestroy() 呼叫 over() 達成效果。

src\components\window-app-google-dino\dino.vue <script>

export default {
  name: 'Dino',
  // ...
  beforeDestroy() {
    this.over();
  },
  // ...
};

總結

  • 完成「小恐龍」組件。
  • 完成小恐龍動作動畫。
  • 成功讓小恐龍跳躍、蹲下。

以上程式碼已同步至 GitLab,大家可以前往下載:

GitLab - D23


上一篇
D22 - 「不斷線的侏儸紀」:很久很久以前的侏儸紀
下一篇
D24 - 「不斷線的侏儸紀」:天上好多雲、地上一堆仙人掌
系列文
你渴望連結嗎?將 Web 與硬體連上線吧!33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言