メインコンテンツへスキップ
Tech Playground
ゲーム開発

Bevy 0.19 ECS新クエリシステム破壊的変更への移行ガイド|既存プロジェクト対応の完全版【2026年5月最新】

Bevy 0.19で導入された新クエリシステムの破壊的変更を徹底解説。Query型の再設計、With/Without制約の変更、マイグレーション手順を実装例とともに紹介します。

約12分で読めます

Bevy 0.19が2026年5月にリリースされ、ECS(Entity Component System)の根幹をなすクエリシステムに大幅な破壊的変更が導入されました。この変更により、既存プロジェクトの多くでコンパイルエラーが発生しています。

本記事では、Bevy 0.19の新クエリシステムの技術的詳細と、既存プロジェクトを移行するための完全なガイドを提供します。公式リリースノートとマイグレーションガイドに基づき、実践的な実装例とともに解説します。

Bevy 0.19クエリシステムの破壊的変更の全貌

Bevy 0.19では、クエリシステムの型安全性とパフォーマンスを向上させるため、Query<T>の内部実装が完全に再設計されました。この変更は2026年4月から5月にかけて段階的にマージされ、最終的に0.19リリースに含まれました。

主要な変更点:

1. Query型のシグネチャ変更

従来のQuery<'w, 's, Q, F>Query<'w, Q, F>に簡略化されました。システムパラメータの世代('s)が内部管理されるようになり、ユーザーコードでライフタイム指定が不要になりました。

// Bevy 0.18 以前
fn old_system(query: Query<'_, '_, &Transform, With<Player>>) {
    for transform in query.iter() {
        // 処理
    }
}

// Bevy 0.19 以降
fn new_system(query: Query<&Transform, With<Player>>) {
    for transform in &query {
        // 処理(iter()も省略可能に)
    }
}

この変更により、コンパイル時の型推論が改善され、複雑なシステムでのビルド時間が15〜20%短縮されました。

2. With/Without制約の型レベル分離

With<T>Without<T>フィルタがクエリの第2型パラメータ(フィルタパラメータF)に統合され、データアクセス(Q)とフィルタリング(F)が型レベルで明確に分離されました。

// Bevy 0.18: データとフィルタが混在
fn mixed_query(query: Query<(&Transform, With<Visible>)>) { }

// Bevy 0.19: 型パラメータで分離
fn separated_query(query: Query<&Transform, With<Visible>>) { }

// 複数フィルタの組み合わせ
fn complex_query(
    query: Query<
        (&Transform, &Velocity),
        (With<Player>, Without<Frozen>)
    >
) {
    for (transform, velocity) in &query {
        // プレイヤーで凍結していないエンティティのみ
    }
}

この分離により、クエリのコンパイル時最適化が可能になり、実行時のフィルタリングオーバーヘッドが約30%削減されました。

3. QueryState APIの刷新

手動でクエリ状態を管理するQueryStateのAPIが変更され、SystemStateとの一貫性が向上しました。

// Bevy 0.18
let mut query_state = world.query_filtered::<&Transform, With<Enemy>>();
for transform in query_state.iter(&world) {
    // 処理
}

// Bevy 0.19
let mut query_state = world.query::<&Transform>()
    .with_filter::<With<Enemy>>()
    .build();
    
for transform in query_state.iter(&world) {
    // 処理
}

以下のダイアグラムは、新旧クエリシステムの型構造の違いを示しています。

graph TD
    A["Bevy 0.18 Query"] --> B["Query<'w, 's, Q, F>"]
    B --> C["データ Q"]
    B --> D["フィルタ F"]
    B --> E["ワールドライフタイム 'w"]
    B --> F["システムライフタイム 's"]
    
    G["Bevy 0.19 Query"] --> H["Query<'w, Q, F>"]
    H --> I["データ Q(型レベル分離)"]
    H --> J["フィルタ F(型レベル分離)"]
    H --> K["ワールドライフタイム 'w(内部管理)"]
    
    style G fill:#4a9eff
    style A fill:#ffaa44

新しい型構造では、システムライフタイムが内部管理されることで、ユーザーコードの複雑性が大幅に軽減されています。

既存コードの移行パターン:実践的な書き換え例

Bevy 0.18から0.19への移行で頻出する具体的なパターンと解決策を示します。

パターン1: 基本的なクエリの移行

// Bevy 0.18
fn player_movement(
    mut query: Query<'_, '_, (&mut Transform, &Velocity), With<Player>>
) {
    for (mut transform, velocity) in query.iter_mut() {
        transform.translation += velocity.0 * TIME_STEP;
    }
}

// Bevy 0.19
fn player_movement(
    mut query: Query<(&mut Transform, &Velocity), With<Player>>
) {
    for (mut transform, velocity) in &mut query {
        transform.translation += velocity.0 * TIME_STEP;
    }
}

変更点:

  • ライフタイム指定を削除
  • iter_mut()&mut queryに変更(イテレータが暗黙的に生成される)

パターン2: 複雑なフィルタの移行

// Bevy 0.18
fn process_entities(
    query: Query<
        '_,
        '_,
        (&Health, &Position),
        (With<Alive>, Without<Invulnerable>, Or<(With<Player>, With<Enemy>)>)
    >
) { }

// Bevy 0.19
fn process_entities(
    query: Query<
        (&Health, &Position),
        (With<Alive>, Without<Invulnerable>, Or<(With<Player>, With<Enemy>)>)
    >
) { }

フィルタの論理構造は変わらず、型パラメータの位置のみ調整されます。

パターン3: Option型コンポーネントの移行

// Bevy 0.18
fn optional_component(
    query: Query<'_, '_, (Entity, Option<&Armor>), With<Character>>
) {
    for (entity, armor) in query.iter() {
        if let Some(armor) = armor {
            println!("Entity {:?} has armor: {}", entity, armor.value);
        }
    }
}

// Bevy 0.19
fn optional_component(
    query: Query<(Entity, Option<&Armor>), With<Character>>
) {
    for (entity, armor) in &query {
        if let Some(armor) = armor {
            println!("Entity {:?} has armor: {}", entity, armor.value);
        }
    }
}

Option<&T>の扱いは変更なし。ライフタイムの削除のみで対応できます。

パターン4: 複数クエリの組み合わせ

// Bevy 0.18
fn multi_query_system(
    players: Query<'_, '_, &Transform, With<Player>>,
    enemies: Query<'_, '_, &Transform, (With<Enemy>, Without<Player>)>
) {
    for player_transform in players.iter() {
        for enemy_transform in enemies.iter() {
            // 処理
        }
    }
}

// Bevy 0.19
fn multi_query_system(
    players: Query<&Transform, With<Player>>,
    enemies: Query<&Transform, (With<Enemy>, Without<Player>)>
) {
    for player_transform in &players {
        for enemy_transform in &enemies {
            // 処理
        }
    }
}

複数のクエリを同一システムで使用する場合、それぞれ独立して移行します。Without<Player>は引き続き必要(クエリの重複アクセスを防ぐため)。

以下のシーケンス図は、新しいクエリシステムの実行フローを示しています。

sequenceDiagram
    participant S as System
    participant Q as Query<Q, F>
    participant W as World
    participant A as Archetype Storage
    
    S->>Q: クエリ実行(for item in &query)
    Q->>W: フィルタ条件をチェック
    W->>A: 該当Archetypeを検索
    A-->>W: マッチしたArchetype一覧
    W->>Q: 型安全なイテレータを生成
    Q-->>S: データへの参照を返却
    
    Note over Q,W: Bevy 0.19では型レベルで<br/>データとフィルタが分離

この図は、新しいクエリシステムがコンパイル時に型情報を活用し、実行時の型チェックを最小化していることを示しています。

コンパイルエラーの診断と修正戦略

Bevy 0.19への移行時に頻出するコンパイルエラーとその解決方法を示します。

エラー1: ライフタイム指定の誤り

error[E0107]: this struct takes 3 generic arguments but 4 were supplied
  --> src/systems.rs:12:15
   |
12 |     query: Query<'_, '_, &Transform, With<Player>>
   |            ^^^^^ expected 3 arguments

原因: Queryの型パラメータが4つから3つに減少

修正:

// 修正前
query: Query<'_, '_, &Transform, With<Player>>

// 修正後
query: Query<&Transform, With<Player>>

エラー2: iter()メソッドの非推奨警告

warning: use of deprecated method `bevy_ecs::system::Query::<Q, F>::iter`
  --> src/systems.rs:15:25
   |
15 |     for transform in query.iter() {
   |                             ^^^^
   |
   = note: `iter()` is deprecated, use `&query` instead

原因: イテレータAPIの簡略化

修正:

// 修正前
for transform in query.iter() { }
for mut transform in query.iter_mut() { }

// 修正後
for transform in &query { }
for mut transform in &mut query { }

エラー3: QueryState APIの変更

error[E0599]: no method named `query_filtered` found for type `World`
  --> src/manual_query.rs:8:27
   |
8  |     let mut state = world.query_filtered::<&Health, With<Alive>>();
   |                           ^^^^^^^^^^^^^^ method not found

原因: query_filteredメソッドの廃止

修正:

// 修正前
let mut state = world.query_filtered::<&Health, With<Alive>>();

// 修正後
let mut state = world.query::<&Health>()
    .with_filter::<With<Alive>>()
    .build();

エラー4: SystemParamの型推論失敗

error[E0282]: type annotations needed
  --> src/complex_system.rs:20:5
   |
20 |     query.get(entity).unwrap();
   |     ^^^^^ cannot infer type

原因: ライフタイム簡略化による型推論の変化

修正:

// 修正前(曖昧)
let result = query.get(entity).unwrap();

// 修正後(型を明示)
let result: &Transform = query.get(entity).unwrap();

以下は、典型的なマイグレーションフローを示したダイアグラムです。

flowchart TD
    A["Bevy 0.18プロジェクト"] --> B["依存関係を0.19に更新"]
    B --> C{"cargo check実行"}
    C -->|エラー| D["エラー種別を特定"]
    D --> E{"ライフタイムエラー?"}
    E -->|Yes| F["'_, '_を削除"]
    E -->|No| G{"iter()警告?"}
    G -->|Yes| H["&query / &mut queryに変更"]
    G -->|No| I{"QueryState API?"}
    I -->|Yes| J["query().with_filter()に書き換え"]
    I -->|No| K["公式マイグレーションガイド参照"]
    F --> C
    H --> C
    J --> C
    K --> C
    C -->|成功| L["テスト実行"]
    L --> M{"動作確認"}
    M -->|Pass| N["移行完了"]
    M -->|Fail| O["ロジック修正"]
    O --> L
    
    style N fill:#44ff88
    style A fill:#ffaa44

このフローに従うことで、段階的かつ確実に移行作業を進められます。

パフォーマンスへの影響と最適化手法

Bevy 0.19の新クエリシステムは、型システムを活用した最適化により、実行時パフォーマンスが大幅に向上しています。

ベンチマーク結果(公式発表データ)

Bevy開発チームが2026年5月に公開したベンチマークでは、以下の改善が確認されています:

テストケースBevy 0.18Bevy 0.19改善率
単純クエリ(10万エンティティ)2.3ms1.9ms17%向上
複雑フィルタ(5万エンティティ)4.1ms2.8ms32%向上
複数クエリ並列実行6.8ms5.1ms25%向上
QueryStateビルド時間180μs95μs47%向上

最適化の技術的根拠

型レベルのフィルタ分離によるコンパイル時最適化:

// 0.19では、コンパイラがフィルタ条件を静的に解析可能
fn optimized_query(
    query: Query<&Transform, (With<Visible>, Without<Hidden>)>
) {
    // フィルタ条件がコンパイル時に確定しているため、
    // 実行時の分岐予測が効率化される
}

Archetypeテーブルアクセスの最適化:

Bevy 0.19では、クエリのフィルタ条件がコンパイル時に完全に解決されるため、該当するArchetypeテーブル(同一のコンポーネント構成を持つエンティティ群)への直接アクセスが可能になりました。これにより、実行時の条件分岐が削減され、CPUキャッシュ効率が向上しています。

実践的な最適化テクニック

1. フィルタの順序最適化

// 非効率: 広範なフィルタを先に評価
query: Query<&Transform, (With<Entity>, With<Rare>)>

// 効率的: 絞り込みの強いフィルタを先に
query: Query<&Transform, (With<Rare>, With<Entity>)>

With<Rare>のような選択性の高いフィルタを先に配置することで、早期にエンティティ数を絞り込めます。

2. Optionの適切な使用

// 非効率: すべてのエンティティを走査してフィルタリング
fn inefficient(query: Query<(Entity, Option<&Armor>)>) {
    for (entity, armor) in &query {
        if armor.is_some() {
            // 処理
        }
    }
}

// 効率的: フィルタで事前に絞り込み
fn efficient(query: Query<(Entity, &Armor), With<Armor>>) {
    for (entity, armor) in &query {
        // すでに Armor を持つエンティティのみ
    }
}

With<T>フィルタを使うことで、Archetypeレベルでの絞り込みが可能になります。

3. 並列クエリの活用

use bevy::tasks::ParallelIterator;

fn parallel_processing(
    query: Query<(&mut Transform, &Velocity)>
) {
    query.par_iter_mut().for_each(|(mut transform, velocity)| {
        transform.translation += velocity.0;
    });
}

Bevy 0.19では、並列イテレータのオーバーヘッドが約15%削減されており、マルチコアCPUでのスケーラビリティが向上しています。

以下のダイアグラムは、新旧クエリシステムのArchetypeアクセスパターンの違いを示しています。

graph LR
    subgraph Bevy018["Bevy 0.18(実行時フィルタリング)"]
        A1["Query実行"] --> B1["全Archetypeを走査"]
        B1 --> C1["各エンティティでフィルタ評価"]
        C1 --> D1["条件一致のみ処理"]
    end
    
    subgraph Bevy019["Bevy 0.19(コンパイル時最適化)"]
        A2["Query実行"] --> B2["型情報から該当Archetypeを特定"]
        B2 --> C2["該当Archetypeのみ直接アクセス"]
        C2 --> D2["全エンティティを処理"]
    end
    
    style Bevy019 fill:#4a9eff
    style Bevy018 fill:#ffaa44

新しいシステムでは、不要なArchetypeへのアクセスが完全に排除されています。

マイグレーションツールと自動化戦略

大規模なBevy 0.18プロジェクトを効率的に移行するためのツールとスクリプトを紹介します。

cargo-bevyマイグレーションツール

Bevyコミュニティが開発したcargo-bevyツールは、0.19への自動マイグレーション機能を提供します。

インストール:

cargo install cargo-bevy --version 0.19.0

使用方法:

# プロジェクトルートで実行
cargo bevy migrate --from 0.18 --to 0.19

# 変更内容のプレビュー(実際の変更は行わない)
cargo bevy migrate --from 0.18 --to 0.19 --dry-run

# 特定のディレクトリのみ処理
cargo bevy migrate --from 0.18 --to 0.19 --path src/systems

このツールは以下の変更を自動で行います:

  • Query<'_, '_, Q, F>Query<Q, F>に書き換え
  • iter()/iter_mut()&query/&mut queryに置換
  • query_filteredquery().with_filter()に変換

正規表現ベースの一括置換スクリプト

cargo-bevyでカバーされないエッジケースには、以下のsedスクリプトが有効です。

#!/bin/bash
# bevy_019_migrate.sh

# Query型のライフタイム削除
find src -name "*.rs" -exec sed -i \
  "s/Query<'[^,]*,\s*'[^,]*,\s*\([^,>]*\),\s*\([^>]*\)>/Query<\1, \2>/g" {} +

# iter() の置換
find src -name "*.rs" -exec sed -i \
  "s/\.iter()/\&query/g" {} +

# iter_mut() の置換
find src -name "*.rs" -exec sed -i \
  "s/\.iter_mut()/\&mut query/g" {} +

# query_filtered の置換(単純なケースのみ)
find src -name "*.rs" -exec sed -i \
  "s/query_filtered::<\([^,]*\),\s*\([^>]*\)>()/query::<\1>().with_filter::<\2>().build()/g" {} +

echo "Migration script completed. Please run 'cargo check' to verify."

実行:

chmod +x bevy_019_migrate.sh
./bevy_019_migrate.sh
cargo check

RustRoverとrust-analyzerのサポート

2026年5月時点で、主要なRust IDEはBevy 0.19のクエリシステムをサポートしています。

rust-analyzer設定(.vscode/settings.jsonまたは~/.config/rust-analyzer/config.toml):

{
  "rust-analyzer.cargo.features": ["bevy/dynamic_linking"],
  "rust-analyzer.procMacro.enable": true,
  "rust-analyzer.imports.granularity.group": "module"
}

これにより、Query型の自動補完と型推論が正しく機能します。

段階的移行戦略(大規模プロジェクト向け)

数万行規模のプロジェクトでは、一度にすべてを移行するのではなく、モジュール単位で段階的に進めることを推奨します。

gantt
    title Bevy 0.19移行タイムライン(大規模プロジェクト)
    dateFormat YYYY-MM-DD
    section Phase 1
    依存関係更新とビルド確認 :2026-05-20, 1d
    コアシステムの移行 :2026-05-21, 2d
    section Phase 2
    プラグインシステムの移行 :2026-05-23, 3d
    レンダリングシステムの移行 :2026-05-26, 2d
    section Phase 3
    ゲームロジックの移行 :2026-05-28, 4d
    統合テストとデバッグ :2026-06-01, 3d
    section Phase 4
    パフォーマンステストと最適化 :2026-06-04, 2d
    本番デプロイ :2026-06-06, 1d

このスケジュールは、10万行規模のBevy 0.18プロジェクトを想定したものです。

モジュール分割の例:

src/
├── core/          # Phase 1: 最優先(ECS、リソース)
├── plugins/       # Phase 2: プラグインシステム
├── rendering/     # Phase 2: レンダリング
├── gameplay/      # Phase 3: ゲームロジック
└── ui/            # Phase 3: UIシステム

各フェーズでcargo checkと単体テストを実行し、次に進む前に問題を解決します。

まとめ

Bevy 0.19のクエリシステム刷新は、破壊的変更を伴うものの、型安全性とパフォーマンスの両面で大きな進化をもたらしました。

重要ポイント:

  • Query<'w, 's, Q, F>Query<Q, F>に簡略化され、ライフタイム管理が不要に
  • データ(Q)とフィルタ(F)の型レベル分離により、コンパイル時最適化が可能に
  • iter()/iter_mut()の代わりに&query/&mut queryを使用
  • 実行時パフォーマンスが平均25%向上(特に複雑なフィルタで顕著)
  • cargo-bevy migrateツールで多くの移行作業を自動化可能
  • 大規模プロジェクトでは、モジュール単位の段階的移行を推奨

Bevy 0.19への移行は、初期の手間を要しますが、長期的にはコードの可読性向上と実行時効率化のメリットが得られます。本記事の移行パターンとツールを活用し、確実にアップグレードを進めてください。

参考リンク

#Rust #Bevy #ECS #ゲーム開発 #破壊的変更
シェア: