The Bevy Book - 2.3 ECS
ECS
Bevy 中的所有應用邏輯都使用Entity Component System 範型,該範例通常縮寫為 ECS。ECS 是一種軟體模式,涉及將程式分解為實體、元件和系統。實體是唯一的“事物”,它們被分配了元件組,然後使用系統進行處理。
例如,一個實體可能具有 Position和Velocity元件,而另一個實體可能具有 Position和UI元件。系統在一組特定的元件型別的集上執行邏輯。您可能有一個movement系統,執行所有帶有Position和Velocity元件的實體。
ECS 模式通過強制您將應用資料和邏輯分解為其核心元件來鼓勵乾淨、解耦的設計。它還有助於通過優化記憶體訪問模式和簡化並行性來加快程式碼速度。
Bevy ECS
Bevy ECS 是 Bevy 對 ECS 模式的實現。與其他通常需要複雜生命週期、特徵、構建器模式或巨集的 Rust ECS 實現不同,Bevy ECS 使用通用Rust 資料型別來實現所有這些概念:
-
元件 Components:實現
Component的Rust結構體
#[derive(Component)] struct Position { x: f32, y: f32 }
-
系統 Systems:標準Rust函式
fn print_position_system(query: Query<&Transform>) { for transform in query.iter() { println!("position: {:?}", transform.translation); } }
-
實體 Entities:包含integer的簡單型別
struct Entity(u64);
現在讓我們看看這在實踐中是如何工作的!
您的第一個系統
將以下函式貼上到檔案中:main.rs
fn hello_world() {
println!("hello world!");
}
這將是我們的第一個系統。剩下的唯一步驟是將其新增到我們的應用程式中!
fn main() {
App::new()
.add_system(hello_world)
.run();
}
add_system()
函式將系統新增到應用的Schedule
現在使用 cargo run 再次執行您的app。您應該會在終端中看到hello world!列印。
您的第一個元件
問候整個世界是偉大的,但是如果我們想問候特定的人怎麼辦?在 ECS 中,您通常將人員建模為實體,其中包含一組定義他們的元件。讓我們從一個元件Person
開始。
將此結構體新增到 :main.rs
#[derive(Component)]
struct Person;
但是,如果我們希望我們的人有一個名字呢?在更傳統的設計中,我們可能只是在Person
上釘上name: String
。但其他實體也可能有名稱!例如,狗可能也應該有一個名字。通常,將資料型別分解為多塊以鼓勵程式碼重用是有意義的。因此,讓自己的元件上加上 Name
:
#[derive(Component)]
struct Name(String);
然後,我們可以使用"startup system"新增People
到我們的World
。startup system就像普通系統一樣,但它在所有其他系統之前只執行一次,就在我們的應用程式啟動時。讓我們使用Commands
將一些實體生成到我們的World
:
fn add_people(mut commands: Commands) {
commands.spawn().insert(Person).insert(Name("Elaina Proctor".to_string()));
commands.spawn().insert(Person).insert(Name("Renzo Hume".to_string()));
commands.spawn().insert(Person).insert(Name("Zayna Nieves".to_string()));
}
現在像這樣註冊startup system:
fn main() {
App::new()
.add_startup_system(add_people)
.add_system(hello_world)
.run();
}
我們現在可以執行此應用程式,add_people
系統將首先執行,然後是 hello_world
.但是我們的新員工還沒有任何事情可做!讓我們建立一個系統,正確地迎接我們World
的新公民:
fn greet_people(query: Query<&Name, With<Person>>) {
for name in query.iter() {
println!("hello {}!", name.0);
}
}
我們傳遞給 "system function" 的引數定義了系統執行的資料。在這種情況下,greet_people
將在具有Person
和Name
元件的所有實體上執行。
您可以將上面的查詢解釋為:“迴圈訪問也具有人員元件的實體的每個名稱元件”
現在,我們只需在我們的應用程式中註冊系統:
fn main() {
App::new()
.add_startup_system(add_people)
.add_system(hello_world)
.add_system(greet_people)
.run();
}
執行我們的應用將產生以下輸出:
hello world!
hello Elaina Proctor!
hello Renzo Hume!
hello Zayna Nieves!
奇妙!
快速說明:“hello world!”的顯示順序可能與上面的順序不同。這是因為預設情況下,系統儘可能並行執行。