ECSの考え方
エンティティ、コンポーネント、システムについて学びます。
「ECSの考え方」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。
ECSで解決できること
BevyはEntity Component System(ECS)パターンを基盤に構築されています。深い継承階層の代わりに、小さく独立したデータの要素を組み合わせて振る舞いを構成します。
このデータ指向設計により、メモリ配置がキャッシュフレンドリーになり、エンジンはほとんど追加コストなしでCPUコア間に処理を並列化できます。
エンティティは単なるID
エンティティは軽量な識別子、つまり世代付きインデックスにすぎません。データを所有せず、それ自体には振る舞いもありません。
データベースの行を指すキーのようなものだと考えてください。そのキーにアタッチされたコンポーネントが、「Player」や「Bullet」のようにエンティティに意味を与えます。
// An Entity is a tiny handle
struct Entity {
index: u32,
generation: u32,
}コンポーネントは純粋なデータ
コンポーネントはComponentをderiveする単純なRust構造体です。状態を保持しますが、ロジックは含みません。
コンポーネントは小さく、役割を絞ってください。位置、速度、体力は、1つの巨大な構造体ではなく、3つの別々のコンポーネントにします。
use bevy::prelude::*;
#[derive(Component)]
struct Position { x: f32, y: f32 }
#[derive(Component)]
struct Velocity { x: f32, y: f32 }システムにロジックを記述する
システムはコンポーネントを操作する通常の関数です。ゲームロジックはすべてここに記述します。
Bevyは各システムのパラメーターを調べて、どのデータにアクセスするかを判断します。その後、競合しないシステムを並列実行するようスケジュールします。
fn move_system(mut query: Query<(&mut Position, &Velocity)>) {
for (mut pos, vel) in &mut query {
pos.x += vel.x;
pos.y += vel.y;
}
}Queryでデータを取得する
Queryは、特定のコンポーネント一式を持つエンティティを宣言的に要求します。Bevyは条件に一致するエンティティだけを返します。
&Tで共有アクセスを、&mut Tで排他的アクセスを要求できます。この借用情報が並列スケジューラーの動作を決めます。
// Entities with BOTH Position and Velocity
fn report(query: Query<(&Position, &Velocity)>) {
for (pos, vel) in &query {
info!("at {},{} moving {},{}", pos.x, pos.y, vel.x, vel.y);
}
}Queryをフィルタリングする
Queryの2番目の型パラメーターはフィルターです。WithとWithoutを使うと、そのコンポーネントのデータを借用せずに結果を絞り込めます。
これはタグコンポーネントに最適です。タグコンポーネントとは、エンティティの役割を示す空の構造体です。
#[derive(Component)]
struct Player;
fn player_only(query: Query<&Position, With<Player>>) {
for pos in &query {
info!("player at {},{}", pos.x, pos.y);
}
}リソース:グローバルな状態
ゲーム全体で1つだけ必要なデータもあります。スコア、経過時間、ゲーム設定などです。これらはコンポーネントではなく、リソースとして扱います。
システムでは、読み取りにはRes<T>を、書き込みにはResMut<T>を使ってアクセスします。
#[derive(Resource)]
struct Score(u32);
fn show_score(score: Res<Score>) {
info!("Score: {}", score.0);
}Appビルダー
すべての要素はAppで接続します。プラグインやリソースを追加し、スケジュールに対してシステムを登録します。
DefaultPluginsはウィンドウ管理、レンダリング、入力、時間を提供するため、ゲームプレイに集中できます。
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(Score(0))
.add_systems(Update, show_score)
.run();
}スケジュール:StartupとUpdate
システムはスケジュール内で実行されます。Startupはアプリの起動時に1回だけ実行され、ワールドの生成に最適です。
Updateは毎フレーム実行され、移動、入力、ロジックを記述する場所です。Bevyには、決定論的な物理ステップ用のFixedUpdateもあります。
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, move_system)
.run();コンポジションが優れている理由
空を飛び、毒状態にもなる敵が必要ですか?FlyingとPoisonedコンポーネントをアタッチするだけです。新しいクラスも継承階層も必要ありません。
どのシステムがどのコンポーネントの組み合わせに一致するかによって、振る舞いが生まれます。そのため、機能を追加しやすく、リファクタリングのコストも小さくなります。
// Same systems, new combinations
commands.spawn((Position{x:0.0,y:0.0}, Velocity{x:1.0,y:0.0}, Flying, Poisoned));自然に得られる並列性
各システムがデータへのアクセス方法を宣言するため、Bevyのスケジューラーは競合するシステムを把握できます。競合しないシステムは自動的に別々のスレッドで実行されます。
単一スレッド向けに見えるコードを書くだけで、エンジンが借用の宣言から並列性を引き出します。ロックも手動のスレッド処理も必要ありません。
確認問題
ECSにおける各要素の役割を理解できているか確認しましょう。
まとめ:ECSの考え方
エンティティはID、コンポーネントはデータ、システムはロジック、リソースはグローバルな状態です。Appがスケジュールを通じてこれらを結び付けます。
小さなコンポーネントを組み合わせ、システムにそれらを照合させることで、柔軟で並列化しやすく、キャッシュフレンドリーなゲームを作れます。次はエンティティを生成して動かします。
よくある質問
「ECSの考え方」レッスンは無料ですか?
はい。「ECSの考え方」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。
「ECSの考え方」で何を学びますか?
エンティティ、コンポーネント、システムについて学びます。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「ECSの考え方」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。