本題について
この記事は、自作シーティングゲーム・編集ツールのアップデート内容を毎年投稿しており、
2024年も数多くのアップデートしましたが、そのうちの一部を振り返りたいと思います
過去記事は以下になります
自作シューティングゲームとは
TypeScriptで実装したグラディウス風の横スクロールシューティングのブラウザゲームです
一部制約はありますが、基本的にPC・スマホでゲームを楽しめます
GamepadAPIを使っており、幾つかのゲームパッドやアーケードコントローラーなどと接続してゲームを楽しむこともできます
この制作目的は
- 好きなシューティングゲームを自分で楽しむ
- 変遷の大きいフロントエンドの技術等を学習する
です
フレームワーク | React |
---|---|
バージョン管理 | Proto |
ビルド環境 | Vite |
テストフレームワーク | Bun |
言語 | TypeScript Scss |
WebAPI | CanvasAPI WebAudioAPI GamepadAPI |
lintツール | Biome Stylelint |
対応機種 | Windows PC(Windows 11) Mac(latest macOS) iPhone(latest iOS) iPad(latest iPadOS) |
対応ブラウザ | Chrome Edge Safari |
接続可能外部コントローラー | 8BitDo Arcade Stick 8BitDo ZEROゲームパッド コントローラー Xbox Eliteワイヤレス コントローラー シリーズ 2 Backbone One PlayStation®版 |
構成
このシューティングゲームは、Nuxtで実装したステージ編集ツールと連携してます
今年も幾つか改修してますが、主だった内容だけピックアップします
1-1. 装備追加
1-2. ChatGPTを使った既存装備の改修
今年からChatGPTを使って、プロンプトから生成されたコードを参考に、
自分の実装と比較して簡潔で管理しやすいソースであればそちらに改修しました
以下の装備はChatGPTを参考に実装を変更しました
1-3. 開発環境をBunに切替え
パッケージマネージャー: pnpm -> Bun
{
..(略)..
"scripts": {
"dev": "vite",
"preview": "vite preview",
"build": "vite build",
}
..(略)..
}
# 開発環境起動
$ bun dev
# プレビュー
$ bun preview
# ビルド
$ bun run build
ユニットテスト: Vitest -> Bun
現時点でユニットテストのテストケースは2000以上ありますが、
実行は約1秒ほどでした
$ bun test
...
...
2115 pass
3 skip
0 fail
3708 expect() calls
Ran 2118 tests across 210 files. [1266.00ms]
テストソースの換装
- 必要なメソッドをimport
-
vi.
は全て削除 -
it.each()
はBunに併せて修正(後述)
+ import { spyOn, describe, it } from 'bun:test';
describe('case', () => {
it('case 1', () => {
- vi.spyOn(className, 'classMethod');
+ spyOn(className, 'classMethod'); // クラスメソッドの動作をモック
+ expect(true).toBe(true); // Bunではexpectedではなくexpect
});
});
it.each()
の変更点
Vitestでeach()メソッドはテンプレートリテラルの実装が可能で、
項目名を使ってまとまりのある見易いコードが、Bunではなぜかできない...
(調べる限り、テンプレートリテラル未対応のため、オブジェクト配列形式で記述が必要みたいです)
試行錯誤の末、オブジェクト配列に変更しましたが、
Bunにもテンプレートリテラル対応が実現すれば、より便利になると感じました
+ import { describe, expect, it } from 'bun:test';
describe('getDistance', () => {
- it.each`
- a | b | result
- ${{ x: 1, y: 0 }} | ${{ x: 0, y: 0 }} | ${1}
- `('getDistance $a, $b, $result', ({ a, b, result }) => {
+ it.each([
+ [{ x: 1, y: 0 }, { x: 0, y: 0 }, 1],
+ ])('getDistance %p, %p, %p', (a, b, result) => {
expect(COMMON_GAME.getDistance(a, b)).toEqual(result);
});
});
GitHub Actionsにて設定ファイルもBunに切替
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/setup@master
- uses: moonrepo/setup-toolchain@v0
with:
auto-install: true
- - name: pnpm install
- run: pnpm install
- - name: pnpm check:all
- run: pnpm check:all
- - name: pnpm knip
- run: pnpm knip
- - name: pnpm test
- run: pnpm test
+ - name: bun install
+ run: bun install
+ - name: bun check:all
+ run: bun check:all
+ - name: bun knip
+ run: bun knip
+ - name: bun test
+ run: bun test
先に記載したBun切替によるユニットテストがかなり貢献してくれて、
全体の実行時間も1分ほど短縮できました
Amplifyのホスティングにおけるビルド設定
こちらもBunに対応できたようで、以下に設定しました
(編集ツールも同様です)
version: 1.1
frontend:
phases:
preBuild:
commands:
- curl -fsSL https://bun.sh/install | bash
- source /root/.bashrc
- bun install
build:
commands:
- bun run build
artifacts:
# IMPORTANT - Please verify your build output directory
baseDirectory: dist
files:
- '**/*'
2. ステージ編集ツール
Nuxtで実装したシューティングゲームのステージを作成・編集するツールです
現時点ではPCのみの対応です
フレームワーク | Nuxt3 |
---|---|
テストフレームワーク | Vitest |
lintツール | Eslint Prettier Stylelint |
対応機種 | Windows PC(Windows 11) Mac(latest macOS) |
対応ブラウザ | Chrome Edge Safari |
編集の主な機能です
機能 | 操作イメージ |
---|---|
ステージの作成 | ![]() |
ステージの編集 | ![]() |
ステージの削除 | ![]() |
ステージの並替え | ![]() |
このステージ編集ツールは、シューティングゲームと連携しており、
ゲームを起動して、編集したステージをプレーすることができます
また、ステージの保存先は現時点で以下を対応しており、ゲーム側でクエリパラメータを使って編集したステージをプレーすることができます
リポジトリ | URL |
---|---|
firebase | https://(ゲームのパス)?repository=firebase |
microCMS | https://(ゲームのパス)?repository=microcms |
MovableType | https://(ゲームのパス)?repository=movabletype |
以下に今年改修した内容を記載します
2-1. プレビュー機能
編集中のステージを実際にプレーして検証するプレビュー機能を追加しました
プレビュー機能の概要
- 編集中のステージをプレイして確認可能
- Loop(難易度)や復帰位置を切り替えながらリアルタイムで確認可能
- ゲームコントローラーを利用した操作もサポート
iframe
とpostMessage
を選択した理由
- ゲームと編集ツールを一緒にするとプロダクトサイズが大きくなるし、総合的に管理が大変(な気がした)のを踏まえ
-
iframe
を使用することで、ゲームと編集ツールを疎結合に保ちながら連携可能 -
postMessage
を使うことで、異なるオリジン間でも安全にデータをやり取り可能
-
実装の仕組み
iframeを使ってシューティングゲームを起動し、編集ツールからステージデータをpostMessageで送信することで、プレビューを実現しています
以下図はプレビュー起動、Loop・復帰位置選択時のフローです
- 初回プレビュー: iframeをロードし、編集中のステージデータを送信してプレビューを開始
- Loop/復帰位置変更: 編集ツールから変更データを送信し、リアルタイムでプレビューを更新
実装例
Edit(編集ツール側)
<template>
<!-- シューティングゲームを表示するiframe -->
<iframe
ref="iframedom"
:src="iFrameURL"
/>
<!-- メッセージ送信ボタン -->
<button @click.stop="sendMessage" />
</template>
<script type="ts" setup>
import { useEventListener } from '@vueuse/core'
const iframedom = ref<HTMLIFrameElement | null>(null)
const iFrameURL = computed(() => `(シーティングゲーム側のパス)`)
// 編集中のステージデータをiframeに送信
const sendMessage = () => {
iframedom.value?.contentWindow?.postMessage(
JSON.stringify('(送信するステージデータ)'),
'(ゲーム側のホスト)'
)
}
onMounted(() => {
// iframeが読み込まれた際にメッセージを送信
useEventListener(iframedom, 'load', () => {
sendMessage()
})
})
</script>
Game(ゲーム側)
// ゲーム側で受信したメッセージを処理
let recievedMessage = ''
window.addEventListener('message', (event: MessageEvent<stringifiedStages>) => {
if (event.origin === '(ステージ編集側のホスト)') {
// 編集機能からのメッセージの場合、受信データをセット
recievedMessage = event.data
}
})
3. まとめ
今年も自作シーティングゲーム、ステージ編集ツールを紹介しました
2017年からシーティングゲームを作成してたので、気づいたらもう7年以上も続けてました
フロントエンドの激しい変遷の中で、
今回の体験を通じ、実務に活かせればと思ってます