ふくふくHukuhuku Inc.
EP.06LLM Gamedev 10分公開: 2026-05-26

Bevy (Rust) × LLM ── ECS パターンと型安全性が LLM 生成にどう効くか

Rust 製ゲームエンジン Bevy と LLM の組合せ。ECS (Entity Component System) パターン、Rust の型安全性が LLM 生成の hallucination を compile error で弾く仕組み、API 変更が早い課題への対処。

#Bevy#Rust#ECS#型安全性
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

EP.06 は (Rust 製ゲームエンジン) × 。前 5 EP のエンジンと比べると独特ですが、Rust の型安全性と の関数型設計 生成と相性抜群です。 の hallucination をコンパイラが弾く 「二人三脚」 を体験する EP。 / Cursor との組合せが特に効きます。

1. Bevy の哲学

  • ECS (Entity Component System) ファースト: OOP の継承を使わず、データとロジックを分離
  • Data-Oriented Design: キャッシュ局所性を意識したメモリレイアウト
  • Rust の所有権モデル: メモリ安全・データ競合フリー
  • プラグインアーキテクチャ: 全機能が plugin、必要なものだけ組み合わせる
  • Hot Reload (一部): 開発時のフィードバックループを高速化中

2. プロジェクトのセットアップ

Bevy プロジェクト作成
Bash
# Rust ツールチェインがなければcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 新規プロジェクトcargo new my_bevy_gamecd my_bevy_game
# Cargo.toml に Bevy 追加# [dependencies]# bevy = "0.14"
cargo build  # 初回は依存解決で 5-10 分
# 開発時の高速ビルド (dynamic linking)# [features]# default = ["bevy/dynamic_linking"]

3. Hello, Bevy

main.rs ── ウィンドウを開いてキューブを表示
Rust
use bevy::prelude::*;
fn main() {    App::new()        .add_plugins(DefaultPlugins)        .add_systems(Startup, setup)        .add_systems(Update, rotate_cube)        .run();}
#[derive(Component)]struct RotatingCube;
fn setup(    mut commands: Commands,    mut meshes: ResMut<Assets<Mesh>>,    mut materials: ResMut<Assets<StandardMaterial>>,) {    // カメラ    commands.spawn(Camera3dBundle {        transform: Transform::from_xyz(0.0, 2.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),        ..default()    });    // ライト    commands.spawn(PointLightBundle {        point_light: PointLight {            intensity: 1500.0,            shadows_enabled: true,            ..default()        },        transform: Transform::from_xyz(4.0, 8.0, 4.0),        ..default()    });    // キューブ    commands.spawn((        PbrBundle {            mesh: meshes.add(Cuboid::default()),            material: materials.add(Color::srgb(0.3, 0.8, 0.3)),            ..default()        },        RotatingCube,    ));}
fn rotate_cube(time: Res<Time>, mut query: Query<&mut Transform, With<RotatingCube>>) {    for mut transform in &mut query {        transform.rotate_y(time.delta_seconds() * 1.0);    }}

4. ECS の三要素

要素Bevy での書き方役割
EntityCommands::spawn() が返す Entity IDゲームオブジェクトの ID
Component`#[derive(Component)] struct Health(f32)`Entity に紐付くデータ
System`fn damage_system(query: Query<&mut Health>)`Entity 群を処理する関数
Resource`#[derive(Resource)] struct Score(u32)`グローバル単一データ
Event`#[derive(Event)] struct ScoredEvent(u32)`System 間のメッセージ

5. LLM が書きやすい System の例

プレイヤー移動 + 衝突 + ゲームオーバー
Rust
use bevy::prelude::*;
#[derive(Component)]struct Player;
#[derive(Component)]struct Enemy;
#[derive(Component)]struct Velocity(Vec2);
#[derive(Resource, Default)]struct GameState {    game_over: bool,}
// System 1: プレイヤー入力で速度更新fn handle_input(    keys: Res<ButtonInput<KeyCode>>,    mut query: Query<&mut Velocity, With<Player>>,) {    let speed = 200.0;    for mut vel in &mut query {        vel.0 = Vec2::ZERO;        if keys.pressed(KeyCode::ArrowLeft) { vel.0.x = -speed; }        if keys.pressed(KeyCode::ArrowRight) { vel.0.x = speed; }        if keys.pressed(KeyCode::ArrowUp) { vel.0.y = speed; }        if keys.pressed(KeyCode::ArrowDown) { vel.0.y = -speed; }    }}
// System 2: 速度に応じて位置更新fn apply_velocity(time: Res<Time>, mut query: Query<(&mut Transform, &Velocity)>) {    for (mut transform, vel) in &mut query {        transform.translation.x += vel.0.x * time.delta_seconds();        transform.translation.y += vel.0.y * time.delta_seconds();    }}
// System 3: プレイヤーと敵の衝突検出fn check_collisions(    player_query: Query<&Transform, With<Player>>,    enemy_query: Query<&Transform, With<Enemy>>,    mut game_state: ResMut<GameState>,) {    for player in &player_query {        for enemy in &enemy_query {            let dist = player.translation.distance(enemy.translation);            if dist < 30.0 {                game_state.game_over = true;            }        }    }}

6. Rust 型安全性 × LLM の二人三脚

  1. 1Claude に Rust コードを書かせる
  2. 2`cargo check` で型エラーが出る (Bevy の SystemParam 制約違反など)
  3. 3エラーメッセージを Claude に貼る
  4. 4Claude が修正案を返す (大抵 2-3 往復で通る)
  5. 5コンパイルが通れば、メモリ安全 + データ競合フリー + 期待した型で動くことが保証される
「クラッシュしない」 ことの心理的安心

Python/GDScript で LLM が書いたコードは「動いたけど null 参照で死ぬ」 が起きやすい。Rust + Bevy では `Option<T>` / `Result<T, E>` が強制されるので、null 系のランタイムエラーがほぼ存在しない。ゲームを長時間プレイテストする心理的負担が劇的に下がる

7. Bevy のバージョン差異への対処

  • Cargo.toml を Claude に毎回見せる: Bevy のバージョンを明確化
  • docs.rs の該当バージョン URL も貼る: docs.rs/bevy/0.14.0
  • migration guide を併用: bevyengine.org/learn/migration-guides/
  • `cargo doc --open` でローカル ドキュメント生成: バージョン整合性が確実

8. Bevy エコシステム

カテゴリクレート用途
物理`bevy_rapier2d/3d`, `avian2d/3d`物理シミュレーション
UI`bevy_egui`, `bevy_lunex`デバッグ UI / ゲーム UI
Asset`bevy_asset_loader`アセット非同期読込
Audio`bevy_audio` (内蔵), `bevy_kira_audio`音声再生
Editor`bevy_editor_pls`, `bevy_inspector_egui`ランタイム inspector
Tilemap`bevy_ecs_tilemap`2D タイルマップ

9. WebAssembly ターゲット

Bevy ゲームをブラウザで動かす
Bash
# WASM ビルドツールcargo install wasm-bindgen-clirustup target add wasm32-unknown-unknown
# ビルドcargo build --release --target wasm32-unknown-unknownwasm-bindgen --no-typescript --target web \  --out-dir ./out/ --out-name "my_bevy_game" \  ./target/wasm32-unknown-unknown/release/my_bevy_game.wasm
# 配信用 HTML を out/ に置いて、任意の Web サーバから配信# itch.io や Vercel 等にデプロイ可能

10. 次の話

EP.07 は アセット生成統合 ── Stable Diffusion / Meshy / Suno / ElevenLabs をゲーム開発フローに組込むパターン。コード生成だけでなくアセット生成も LLM/生成 AI で完結させる流れ。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp