第1章: プロジェクト構築とWebGPU初期化
この10章のチュートリアルでは、ブラウザ上で**数千〜数万体ものモンスターがプレイヤーに押し寄せる大群サバイバーゲーム(Swarm Survivor)**を、PlutoEngine v1.2.1 を使ってゼロから完成させます。
PlutoEngine のデータ指向(SoA)とFlyweightパターンを用いたゼロアロケーション設計、そしてWebGPUバックエンドを用いれば、5,000体以上の敵が画面を埋め尽くしても、安定して動作します。物理演算には XPBDPlugin、空間分割には MortonPlugin を使用していきます。
第1章では、開発環境のセットアップと、WebGPU・超大容量インスタンスバッファアリーナを持つゲームエンジンの初期化を行います。
1. プロジェクトの作成
まずは Vite と TypeScript で新しいプロジェクトを作成します。
# Bun を使用する場合 (推奨)
bun create vite swarm-survivor --template vanilla-ts
cd swarm-survivor
# 依存パッケージのインストール
bun add pluto-engineTIP
npm や pnpm をお使いの場合は、npm create vite@latest swarm-survivor -- --template vanilla-ts を実行し、npm install pluto-engine を実行してください。
2. HTML と WebGPU の準備 (index.html)
ゲーム画面を中央に配置し、暗黒の宇宙空間を演出するCSSを設定します。index.html を以下のように編集します:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swarm Survivor - PlutoEngine</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
background-color: #030712;
color: #f3f4f6;
font-family: system-ui, -apple-system, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8), 0 0 30px rgba(56, 189, 248, 0.15);
border-radius: 12px;
overflow: hidden;
border: 1px solid #1f2937;
}
canvas {
display: block;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game-canvas"></canvas>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>3. シーンとエンジンの初期化 (src/main.ts)
src/main.ts に最初のゲームシーン SwarmSurvivorScene を作成し、PlutoEngine を初期化します。v1.2.1のアーキテクチャに合わせて、WebGPUバックエンドを有効化し、InstanceBufferArenaによって一括管理される構成を設定します。
import { PlutoEngine, ScaleMode, Scene } from 'pluto-engine';
/**
* スウォーム・サバイバーのメインゲームシーン
*/
export class SwarmSurvivorScene extends Scene {
/**
* シーンの初期化時に一度だけ実行される
*/
async create(): Promise<void> {
console.log('SwarmSurvivorScene が初期化されました');
// v1.2.1では InstanceBufferArena でエンティティを管理します
console.log(`アリーナ最大収容量: ${this.engine.arena.capacity} インスタンス`);
// アリーナの中央にテスト用の目印スプライトを1つ配置
const centerMarker = this.add.sprite(960 / 2, 540 / 2, 'textureKey').setDisplaySize(16, 16);
centerMarker.setTint(0x38bdf8); // スカイブルー (0xRRGGBB)
}
/**
* 毎フレーム呼ばれる更新ロジック (ゼロアロケーション、Flyweightパターンを使用)
* ※更新ループ内では new を絶対に使用しないでください
* @param dt 前フレームからの経過時間 (秒)
*/
update(dt: number): void {
// 次章でプレイヤーの入力処理を実装します
}
}
// PlutoEngine インスタンスの作成と起動
const engine = new PlutoEngine({
canvas: 'game-canvas',
width: 960,
height: 540,
scaleMode: ScaleMode.FIT,
autoCenter: true,
backend: 'webgpu', // WebGPUバックエンドを指定
maxInstances: 50000, // 5万体のエンティティを収容可能なSoAベースのInstanceBufferArenaを確保
scene: [SwarmSurvivorScene],
});4. なぜ maxInstances: 50000 で WebGPU・SoA なのか?
v1.2.1 アーキテクチャの核心は、ゲームが起動した瞬間に必要なメモリ領域を一括確保(InstanceBufferArena)することにあります。
Float32Array(SoA: 構造体の配列ではなく、配列の構造体)により構成され、キャッシュ効率が最適化されています。RenderGraphと連携し、WebGPUバックエンドにインスタンス描画データを効率的に送ります。- 起動時にこの領域を確保しておくことで、ゲームプレイ中に数千体のモンスターが出現しても、ブラウザのV8エンジンはオブジェクト生成(ヒープアロケーション)を一切行いません。また、Flyweightパターンを用いてインスタンスへアクセスします。これがガベージコレクション(GC)によるカクつきをゼロにする仕組みです。
5. 動作確認
ローカル開発サーバーを立ち上げます:
bun run devブラウザで開くと、深みのある宇宙色(ダークブルーグレー)のキャンバスの中央に、スカイブルー色の小さなマーカーが表示されます。
第1章はこれで完了です。 次の第2章では、主人公キャラクター(プレイヤー)を登場させ、WASD / 矢印キーで滑らかに操作できるようにします。