在這篇文章中我將談談我用 Rust 開發小遊戲的個人經驗。 創建一個工作版本大約需要 24 小時(我主要在晚上或週末工作)。 遊戲還遠遠沒有完成,但我認為這次經歷將會是有益的。 我將分享我在從頭開始建立遊戲時學到的知識和一些觀察。
技能箱推薦: 兩年實踐課程
“我是專業網頁開發人員” .提醒: 對於“Habr”的所有讀者 - 使用“Habr”促銷代碼註冊任何 Skillbox 課程可享受 10 盧布的折扣。
為什麼生鏽?
我選擇這種語言是因為我聽說過很多關於它的好消息,我看到它在遊戲開發中變得越來越流行。 在編寫遊戲之前,我幾乎沒有用 Rust 開發簡單應用程式的經驗。 這足以讓我在編寫遊戲時有一種自由感。
為什麼要玩這個遊戲以及什麼樣的遊戲?
製作遊戲很有趣! 我希望有更多的理由,但對於「家庭」項目,我選擇與我的日常工作不太密切相關的主題。 這是什麼遊戲? 我想製作一款類似網球模擬器的東西,將《城市天際線》、《動物園大亨》、《監獄建築師》和網球本身結合在一起。 總的來說,這是一個關於人們來玩的網球學院的遊戲。
技術培訓
我想使用 Rust,但我不知道需要做多少基礎工作才能開始。 我不想編寫像素著色器並使用拖放功能,因此我正在尋找最靈活的解決方案。
我發現了一些有用的資源,與您分享:
我們還在玩嗎 — 遊戲開發所需的 Rust 元素清單;Rust 遊戲開發子版塊; 免費像素藝術。
我探索了幾個 Rust 遊戲引擎,最後選擇了 Piston 和 ggez。 我在處理之前的項目時遇到了它們。 最後我選擇了ggez,因為它看起來更適合實現一個小型2D遊戲。 Piston 的模組化結構對於新手開發人員(或第一次使用 Rust 的人)來說太複雜了。
遊戲結構
我花了一些時間思考該專案的架構。 第一步是製作「土地」、人和網球場。 人們必須在法庭周圍走動並等待。 玩家的技能必須隨著時間的推移而提高。 另外,應該有一個編輯器可以讓你添加新的人和法院,但這不再是免費的。
想通了一切之後,我就開始工作了。
遊戲創作
開始:圓圈和抽象
我從 ggez 中獲取了一個範例,並在螢幕上看到了一個圓圈。 奇妙! 現在一些抽象。 我認為從遊戲物件的想法中抽象化會很好。 每個物件都必須按照此處所述進行渲染和更新:
// the game object trait
trait GameObject {
fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;
fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;
}
// a specific game object - Circle
struct Circle {
position: Point2,
}
impl Circle {
fn new(position: Point2) -> Circle {
Circle { position }
}
}
impl GameObject for Circle {
fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
let circle =
graphics::Mesh::new_circle(ctx, graphics::DrawMode::Fill, self.position, 100.0, 2.0)?;
graphics::draw(ctx, &circle, na::Point2::new(0.0, 0.0), 0.0)?;
Ok(())
}
}
這段程式碼給了我一個很好的物件列表,我可以在同樣好的循環中更新和渲染這些物件。
mpl event::EventHandler for MainState {
fn update(&mut self, context: &mut Context) -> GameResult<()> {
// Update all objects
for object in self.objects.iter_mut() {
object.update(context)?;
}
Ok(())
}
fn draw(&mut self, context: &mut Context) -> GameResult<()> {
graphics::clear(context);
// Draw all objects
for object in self.objects.iter_mut() {
object.draw(context)?;
}
graphics::present(context);
Ok(())
}
}
main.rs 是必要的,因為它包含所有程式碼行。 我花了一點時間分離文件並優化目錄結構。 這是之後的樣子:
資源 -> 這是所有資產所在的位置(圖像)
SRC
- 實體
— 遊戲物件.rs
— 圓.rs
— main.rs -> 主循環
人物、樓層和圖像
下一步是建立 Person 遊戲物件並載入圖片。 一切都應該建立在32*32瓷磚的基礎上。
網球場
在研究了網球場的外觀後,我決定用 4*2 的瓷磚來製作它們。 最初,可以製作這種尺寸的圖像,或將 8 個獨立的圖塊放在一起。 但後來我意識到只需要兩個獨特的圖塊,原因如下。
我們總共有兩個這樣的圖塊:1 和 2。
球場的每個部分都由 1 塊或 2 塊組成。它們可以正常佈置,也可以翻轉 180 度。
基本施工(組裝)模式
當我成功實現了場地、人物和地圖的渲染後,我意識到還需要一個基本的組裝模式。 我是這樣實現的:按下按鈕時,將選擇對象,然後單擊將其放置在所需的位置。 因此,按鈕 1 允許您選擇球場,按鈕 2 允許您選擇球員。
但我們仍然需要記住 1 和 2 的含義,因此我添加了一個線框以明確選擇了哪個物件。 這就是它的樣子。
架構和重構問題
現在我有幾個遊戲對象:人、球場和地板。 但為了讓線框發揮作用,需要告知每個物件實體物件本身是否處於演示模式,或者是否只是簡單地繪製了一個框架。 這不太方便。
在我看來,該架構需要重新思考,以揭示一些限制:
- 擁有一個能夠渲染和更新自身的實體是一個問題,因為該實體將無法「知道」它應該渲染什麼 - 圖像和線框;
- 缺乏用於在各個實體之間交換屬性和行為的工具(例如,is_build_mode 屬性或行為渲染)。 可以使用繼承,儘管在 Rust 中沒有正確的方法來實現它。 我真正需要的是佈局;
- 需要一種實體之間互動的工具來將人員分配給法院;
- 這些實體本身是數據和邏輯的混合體,很快就失去了控制。
我做了更多研究並發現了架構
ECS - 實體組件系統 ,常用於遊戲中。 ECS 的優點如下:
- 數據與邏輯分離;
- 組合而不是繼承;
- 以資料為中心的架構。
ECS 有三個基本概念:
- 實體 - 識別碼所指的物件類型(可以是球員、球或其他東西);
- 組件-實體由它們組成。 範例 - 渲染元件、位置等。 這些是資料倉儲;
- 系統 - 它們使用物件和元件,並包含基於此資料的行為和邏輯。 一個範例是渲染系統,它使用渲染元件迭代所有實體並進行渲染。
經過研究,發現ECS解決了以下問題:
- 使用佈局而不是繼承來有系統地組織實體;
- 透過控制系統消除程式碼混亂;
- 使用 is_build_mode 等方法將線框邏輯保持在相同位置 - 在渲染系統中。
這就是實施 ECS 後發生的情況。
資源 -> 這是所有資產所在的位置(圖像)
SRC
- 成分
—position.rs
— 人.rs
—tennis_court.rs
— 地板.rs
- 線框.rs
— mouse_tracked.rs
- 資源
—mouse.rs
- 系統
— 渲染.rs
— 常數.rs
— utils.rs
— world_factory.rs -> 世界工廠函數
— main.rs -> 主循環
我們指派人員到法院
ECS 讓生活變得更輕鬆。 現在我有了一種系統的方法來向實體添加資料並基於該資料添加邏輯。 這反過來又使得組織法院之間的人員分配成為可能。
我做了什麼:
- 向 Person 添加了有關指定法院的數據;
- 向 TennisCourt 添加了有關分散式人員的數據;
- 新增了 CourtChoosingSystem,它允許您分析人員和球場,偵測可用的球場並將球員分配給他們;
- 新增了人員移動系統,該系統會尋找分配到法院的人員,如果他們不在那裡,則將人員派往他們需要的地方。
總結
我真的很喜歡開發這個簡單的遊戲。 而且,我很高興我使用 Rust 來編寫它,因為:
- Rust 可以滿足您的需求;
- 它有優秀的文檔,Rust 相當優雅;
- 一致性很酷;
- 你不必訴諸克隆、複製或其他類似的操作,而我在 C++ 中經常這樣做;
- 選項非常易於使用並且可以很好地處理錯誤;
- 如果該專案能夠編譯,那麼 99% 的情況下它都能正常運作,並且完全符合預期。 我認為編譯器錯誤訊息是我見過的最好的。
Rust 的遊戲開發才剛開始。 但已經有一個穩定且相當大的社區致力於向所有人開放 Rust。 因此,我樂觀地看待語言的未來,並期待我們共同努力的成果。
技能箱推薦:
- 在線課程
《職業前端開發人員》 .- 實踐課程
“移動開發者專業版” .- 實踐年課程
《PHP 開發者從 0 到 PRO》 .
來源: www.habr.com