iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

派對動物嗨起來!系列 第 27

D27 - 一人一隻才公平

  • 分享至 

  • xImage
  •  

本系列文已改編成書「甚麼?網頁也可以做派對遊戲?使用 Vue 和 babylon.js 打造 3D 派對遊戲吧!」

書中不只重構了程式架構、改善了介面設計,還新增了 2 個新遊戲呦!ˋ( ° ▽、° )

新遊戲分別使用了陀螺儀與震動回饋,趕快買書來研究研究吧!ლ(╹∀╹ლ)

在此感謝深智數位的協助,歡迎大家前往購書,鱈魚感謝大家 (。・∀・)。

助教:「所以到底差在哪啊?沒圖沒真相,被你坑了都不知道。(´。_。`)」

鱈魚:「你對我是不是有甚麼很深的偏見啊 (っ °Д °;)っ,來人啊,上連結!」

Yes


讓我們實際使用搖桿訊號控制器動作看看吧,先控制第一隻企鵝看看。

src\games\the-first-penguin\game-scene.vue

...
<script setup lang="ts">
...
import { curry } from 'lodash-es';
...
/** 根據 key 取得資料 */
const findSingleData = curry((keys: SingleData[], name: `${KeyName}`) =>
  keys.find((key) => key.name === name)
);

/** 控制指定企鵝 */
function ctrlPenguin(penguin: Penguin, data: GamepadData) {
  const { keys } = data;
  const findData = findSingleData(keys);

  // 攻擊按鍵
  const attackData = findData('a');
  if (attackData) {
    penguin.attack();
    return;
  }

  // 移動按鍵
  const xData = findData('x-axis');
  const yData = findData('y-axis');

  const x = xData?.value ?? 0;
  const y = yData?.value ?? 0;

  if (x === 0 && y === 0) return;
  if (typeof x === 'number' && typeof y === 'number') {
    // 企鵝往上(正 Z 方向)是螢幕的 -Y 方向,所以要反轉
    penguin.walk(new Vector3(x, 0, -y));
  }
}
/** 初始化搖桿 */
function initGamepadEvent() {
  gameConsole.onGamepadData((data) => {
    console.log('[ gameConsole.onGamepadData ] data : ', data);

    const penguin = penguins[0];
    if (!penguin) return;

    ctrlPenguin(penguin, data);
  });
}
async function init() {
  ...
  initGamepadEvent();

  /** 持續運行指定事件 */
  ...
}
...
</script>

其中 curry 是 Functional Programing 的重要概念之一,利用 closure(閉包)特性,可以讓 function 會形成一個鏈,變成可以依序傳入參數,最終完成運算。

個人覺得很適合用來處理資料,可以看到取得控制訊號的程式可以變得更為簡潔易讀。

想知道更多的讀者參考以下連結:

Curry(柯里化)

現在我們可以用搖桿控制企鵝了!

ezgif-1-2abc4f8a5e.gif

現在讓我們實際依照玩家資料建立對應的企鵝,一人一隻企鵝吧。( ´ ▽ ` )ノ

首先我們需要一個可以依照玩家 ID 取得玩家代號的功能。

src\composables\use-client-game-console.ts

...
export function useClientGameConsole() {
  ...
  function getPlayerCodeName(id: string) {
    const index = gameConsoleStore.players.findIndex(({ clientId }) =>
      clientId === id
    );

    if (index < 0) {
      return 'unknown ';
    }

    return `${index + 1}P`;
  }

  return {
    ...
    getPlayerCodeName,
  }
}

接著調整 createPenguin() 內容。

src\games\the-first-penguin\game-scene.vue

...
<script setup lang="ts">
...
import { getPlayerColor } from '../../common/utils';
import { colors } from 'quasar';
...
async function createPenguin(id: string, index: number) {
  // 依照玩家 ID 取得對應顏色
  const codeName = gameConsole.getPlayerCodeName(id);
  const color = getPlayerColor({ codeName });
  const hex = getPaletteColor(color);
  const rgb = textToRgb(hex);

  const penguin = await new Penguin(`penguin-${index}`, scene, {
    position: new Vector3(5 * index, 10, 0),
    color: new Color3(rgb.r / 255, rgb.g / 255, rgb.b / 255),
    ownerId: id,
  }).init();

  return penguin;
}
...
</script>
...

然後不能讓每隻企鵝出現的位置都一樣,這樣會卡成一團企球。( ゚ ∀ 。)

src\games\the-first-penguin\game-scene.vue

...
<script setup lang="ts">
...
const penguinInitPositions = [
  new Vector3(-5, 0, 5),
  new Vector3(5, 0, 5),
  new Vector3(-5, 0, -5),
  new Vector3(-5, 0, -5)
];
...
async function createPenguin(id: string, index: number) {
  // 依照玩家 ID 取得對應顏色
  const codeName = gameConsole.getPlayerCodeName(id);
  const color = getPlayerColor({ codeName });
  const hex = getPaletteColor(color);
  const rgb = textToRgb(hex);

  const position = penguinInitPositions[index % penguinInitPositions.length];
  // 超過 4 人時調整高度
  position.y = Math.round(index / penguinInitPositions.length) * 10 + 5;

  const penguin = await new Penguin(`penguin-${index}`, scene, {
    position,
    color: new Color3(rgb.r / 255, rgb.g / 255, rgb.b / 255),
    ownerId: id,
  }).init();

  return penguin;
}
...
</script>
...

最後調整建立企鵝的部分,改為依玩家資料建立企鵝。

src\games\the-first-penguin\game-scene.vue

...
<script setup lang="ts">
...
const gameConsoleStore = useGameConsoleStore();
...
async function init() {
  ...
  const result = await Promise.allSettled(
    gameConsoleStore.players.map(({ clientId }, index) =>
      createPenguin(clientId, index)
    )
  );
  result.forEach((data) => {
    if (data.status !== 'fulfilled') return;
    penguins.push(data.value);
  });
  ...
}
...
</script>
...

現在讓我們想辦法變出兩個玩家,可以再開一個不同的瀏覽器,或是使用手機透過區網連線。

現在是一個玩家一隻企鵝了!

Untitled

最後最重要的部分,就是讓玩家只能控制自己的企鵝!◝( •ω• )◟

調整 initGamepadEvent() 內容。

src\games\the-first-penguin\game-scene.vue

...
<script setup lang="ts">
...
const gameConsoleStore = useGameConsoleStore();
...
/** 初始化搖桿 */
function initGamepadEvent() {
  gameConsole.onGamepadData((data) => {
    const { playerId } = data;

    const penguin = penguins.find((penguin) => penguin.params.ownerId === playerId);
    if (!penguin) return;

    ctrlPenguin(penguin, data);
  });
}
...
</script>
...

現在玩家只能控制自己的企鵝了!✧*。٩(ˊᗜˋ*)و✧*。

總結

  • 使用搖桿控制訊號控制企鵝
  • 依照玩家資料產生對應企鵝

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

GitLab - D27


上一篇
D26 - 遊戲機網頁取得搖桿控制訊號
下一篇
D28 - 第一隻企鵝
系列文
派對動物嗨起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言