衝突とゲーム状態
ルールと勝利条件を追加します。
「衝突とゲーム状態」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。
AABB衝突の基本
最も単純な衝突判定は、軸に平行な境界ボックス(AABB)の重なりを調べる方法です。2つの長方形がx軸とy軸の両方で重なっていると、衝突しています。
スプライトの場合、この方法は高速で、多くの2Dゲームでは本格的な物理エンジンを導入する前の方法として十分に機能します。
fn overlaps(a_pos: Vec2, a_size: Vec2, b_pos: Vec2, b_size: Vec2) -> bool {
(a_pos.x - b_pos.x).abs() < (a_size.x + b_size.x) * 0.5
&& (a_pos.y - b_pos.y).abs() < (a_size.y + b_size.y) * 0.5
}Colliderコンポーネント
各エンティティに、半分の大きさまたは全体のサイズを保持するColliderコンポーネントを追加します。システムはトランスフォームとコライダーを読み取り、重なりを判定します。
サイズを独立したコンポーネントにしておくと、異なるエンティティで同じ衝突システムを共有できます。
#[derive(Component)]
struct Collider {
size: Vec2,
}システムで衝突を検出する
プレイヤーと多数の障害物を比較するには、プレイヤーを1回クエリし、障害物を反復処理します。2つに分けたクエリを使うことで、同じデータを2回借用することを避けられます。
重なりが検出されたら、Commandsを使うかコンポーネントを変更して、ダメージ、跳ね返り、デスポーンなどの処理を行います。
fn detect(
player: Query<(&Transform, &Collider), With<Player>>,
walls: Query<(&Transform, &Collider), With<Wall>>,
) {
let (pt, pc) = player.single().unwrap();
for (wt, wc) in &walls {
if overlaps(pt.translation.truncate(), pc.size, wt.translation.truncate(), wc.size) {
info!("hit a wall");
}
}
}イベントで反応する
結果をその場で処理する代わりに、カスタムEventを送信します。あるシステムが衝突を検出してイベントを発行し、別のシステムがイベントを読み取って効果を適用します。
これにより検出と反応が分離され、それぞれのシステムを小さく保てます。
#[derive(Event)]
struct CollisionEvent { entity: Entity }
fn emit(mut writer: EventWriter<CollisionEvent>, e: Entity) {
writer.send(CollisionEvent { entity: e });
}イベントを読み取る
読み取り用のシステムはEventReaderでキューに入ったイベントを取り出し、それぞれに対して処理を行います。イベントは2フレームの間存続するため、どの読み取りシステムからも確認できます。
このパブリッシュ・サブスクライブ方式は、密結合にせず、多数の発行側と購読側へ拡張できます。
fn on_hit(mut reader: EventReader<CollisionEvent>, mut commands: Commands) {
for ev in reader.read() {
commands.entity(ev.entity).despawn();
}
}スコアを更新する
プレイヤーがアイテムを取得したら、ResMutを使ってグローバルなScoreリソースを変更します。
リソースは一意なので、どのシステムからでも、手動で受け渡すことなくスコアを読み取ったり更新したりできます。
fn collect(mut score: ResMut<Score>, mut reader: EventReader<PickupEvent>) {
for _ in reader.read() {
score.0 += 10;
}
}状態の全体像
ゲームはメニュー、プレイ中、一時停止、ゲームオーバーなどのモードを切り替えます。Bevyでは、init_stateで登録したStates列挙型によってこれらをモデル化します。
一度にアクティブになる状態は1つだけで、特定の状態のときだけ実行されるようシステムを制限できます。
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
enum GameState {
#[default]
Menu,
Playing,
GameOver,
}状態を登録する
init_stateを使って状態をアプリに追加します。するとBevyが現在の値を追跡し、StateとNextStateのリソースを提供します。
システムで現在のモードを確認する必要がある場合は、Res<State<GameState>>で現在の状態を読み取ります。
App::new()
.add_plugins(DefaultPlugins)
.init_state::<GameState>()
.run();特定の状態でシステムを実行する
run_if条件のin_stateを使って、システムを特定の状態に限定します。移動と衝突判定はPlayingの間だけ実行されます。
これが一時停止の仕組みです。Playing状態を離れてゲームプレイのシステムを停止すると、それらは単に実行されなくなります。
app.add_systems(
Update,
(drive, detect).run_if(in_state(GameState::Playing)),
);状態を遷移させる
状態を変更するには、任意のシステムからNextStateを設定します。Bevyは次のフレーム境界で遷移を適用します。
状態に入ったときにUIを生成したり、状態を離れるときに後片付けをしたりするには、OnEnterとOnExitのスケジュールを使います。
fn lose(mut next: ResMut<NextState<GameState>>, health: Res<Health>) {
if health.0 == 0 {
next.set(GameState::GameOver);
}
}終了時のクリーンアップ
Playingを離れるときは、次のラウンドをきれいな状態で開始できるよう、ゲームプレイ用エンティティをデスポーンします。マーカーを付け、そのマーカーを持つすべてのエンティティをデスポーンします。
OnExit(GameState::Playing)にクリーンアップを登録すると、遷移ごとに正確に1回実行されます。
fn cleanup(mut commands: Commands, q: Query<Entity, With<GameEntity>>) {
for e in &q {
commands.entity(e).despawn();
}
}
// app.add_systems(OnExit(GameState::Playing), cleanup);簡単な確認
ゲームプレイをきれいに一時停止する方法について考えてみましょう。
まとめ: 衝突とゲーム状態
AABB判定とColliderコンポーネントで重なりを検出できます。イベントによって検出と反応を分離し、Scoreリソースで進行状況を追跡します。run_if、NextState、OnEnter/OnExitを使うStatesシステムによって、メニュー、プレイ、ゲームオーバーを構成できます。
これで、生成、操作、衝突、遷移という一連の流れが完成しました。これらの要素から完全なBevyゲームを作ってみましょう。
よくある質問
「衝突とゲーム状態」レッスンは無料ですか?
はい。「衝突とゲーム状態」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。
「衝突とゲーム状態」で何を学びますか?
ルールと勝利条件を追加します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「衝突とゲーム状態」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ECSの考え方
- エンティティを生成して動かす
- 入力と時間を扱う
- 衝突とゲーム状態