
I den hÀr artikeln kommer jag att berÀtta om min personliga erfarenhet av att utveckla ett litet spel i Rust. Det tog ungefÀr 24 timmar att skapa en fungerande version (för det mesta arbetade jag pÄ kvÀllar eller helger). Spelet Àr fortfarande lÄngt ifrÄn klart, men jag tror att erfarenheten kommer att vara anvÀndbar. Jag kommer att dela med mig av vad jag har lÀrt mig och nÄgra observationer som jag har gjort under byggandet av spelet frÄn grunden.
Skillbox rekommenderar: TvÄÄrig praktisk kurs .
PĂ„minnelse: för alla lĂ€sare av "Habr" â 10 000 rubel rabatt vid registrering för vilken kurs som helst pĂ„ Skillbox med rabattkoden "Habr".
Varför Rust?
Jag valde detta sprÄk eftersom jag har hört mycket bra om det och ser att det blir alltmer populÀrt inom spelutveckling. Innan jag skrev spelet hade jag lite erfarenhet av att utveckla enkla program i Rust. Det var precis tillrÀckligt för att kÀnna en viss frihet nÀr jag skrev spelet.
Varför just spel och vilket spel?
Att skapa spel Ă€r roligt! Jag skulle vilja ha fler skĂ€l, men för âhemmaâ projekt vĂ€ljer jag Ă€mnen som inte Ă€r för nĂ€ra kopplade till mitt vanliga arbete. Vilket spel? Jag ville göra nĂ„got som en tennissimulator dĂ€r Cities Skylines, Zoo Tycoon, Prison Architect och egen tennis blandas. I allmĂ€nhet blev det ett spel om en tennisklubb dĂ€r mĂ€nniskor kommer för att spela.
Teknisk förberedelse
Jag ville anvĂ€nda Rust, men jag visste inte exakt hur mycket jag behövde börja frĂ„n âgrundenâ. Jag ville inte skriva pixel-shaders eller anvĂ€nda drag-n-drop, sĂ„ jag letade efter de mest flexibla lösningarna.
Jag har hittat anvÀndbara resurser som jag delar med er:
- â en lista över nödvĂ€ndiga Rust-element för spelutveckling;
Jag studerade flera Rust-spelmotorer och valde till slut Piston och ggez. Jag hade stött pÄ dem nÀr jag arbetade med mitt tidigare projekt. I slutÀndan valde jag ggez eftersom det verkade mer lÀmpligt för att skapa ett litet 2D-spel. Den modulÀra strukturen i Piston Àr för komplicerad för en nybörjare (eller nÄgon som arbetar med Rust för första gÄngen).
Spelstruktur
Jag spenderade lite tid pÄ att tÀnka pÄ projektets arkitektur. Det första steget Àr att göra "jorden", mÀnniskor och tennisbanor. MÀnniskor ska kunna röra sig pÄ banorna och vÀnta. Spelarna ska ha fÀrdigheter som förbÀttras över tid. Dessutom mÄste det finnas en redaktör som gör det möjligt att lÀgga till nya mÀnniskor och banor, men det Àr inte gratis.
Efter att ha övervÀgt allt började jag arbeta.
Skapande av spel
Inledning: cirklar och abstraktioner
Jag tog ett exempel frÄn ggez och fick en cirkel pÄ skÀrmen. Fantastiskt! Nu lite abstraktioner. Jag kÀnde att det var bra att abstrahera frÄn idén om ett spelobjekt. Varje objekt mÄste renderas och uppdateras som anges hÀr:
// 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(())
}
}Denna kodsnutt gjorde att jag fick en fantastisk lista över objekt som jag kan uppdatera och rendera i en lika fantastisk loop.
mpl event::EventHandler för MainState {
fn update(&mut self, context: &mut Context) -> GameResult {
// Uppdatera alla objekt
for object in self.objects.iter_mut() {
object.update(context)?;
}
Ok(())
}
fn draw(&mut self, context: &mut Context) -> GameResult {
graphics::clear(context);
// Rita alla objekt
for object in self.objects.iter_mut() {
object.draw(context)?;
}
graphics::present(context);
Ok(())
}
} main.rs Àr nödvÀndig eftersom den innehÄller alla kodrader. Jag la ner lite tid pÄ att dela upp filerna och optimera mappstrukturen. SÄ hÀr ser allt ut efter detta:
resources -> hÀr finns alla tillgÄngar (bilder)
src
â entities
â game_object.rs
â circle.rs
â main.rs -> huvudloop
MĂ€nniskor, banor och bilder
NÀsta steg Àr att skapa spelobjektet Person och ladda bilder. Allt ska byggas pÄ grundval av brickor som Àr 32*32.

Tennisbanor
Efter att ha studerat hur tennisbanor ser ut bestÀmde jag mig för att göra dem av 4*2-brickor. Inledningsvis kunde jag antingen skapa en bild av denna storlek eller sammanfoga 8 separata brickor. Men senare insÄg jag att det bara behövdes tvÄ unika brickor, och hÀr Àr varför.
Vi har totalt tvÄ sÄdana brickor: 1 och 2.
Varje sektion av banan bestÄr av antingen bricka 1 eller bricka 2. De kan placeras som vanligt eller vÀndas 180 grader.

HuvudbyggnadslÀget (sammanstÀllning)
NÀr jag lyckades fÄ till rendering av banor, mÀnniskor och kartor insÄg jag att jag ocksÄ behövde ett grundlÀggande bygglÀgen. Jag implementerade det sÄ hÀr: nÀr en knapp trycks ned vÀljs ett objekt, och ett klick placerar det pÄ önskad plats. SÄ, knapp 1 lÄter mig vÀlja en bana, medan knapp 2 lÄter mig vÀlja en spelare.
Men vi mÄste ocksÄ komma ihÄg vad 1 och 2 betyder, sÄ jag lade till en wireframe för att göra det tydligt vilket objekt som var valt. SÄ hÀr ser det ut.

FrÄgor om arkitektur och refaktorering
Nu har jag nÄgra spelobjekt: mÀnniskor, tennisplaner och vÄningar. Men för att wireframes ska fungera mÄste varje objekts enhet informeras om huruvida sjÀlva objekten Àr i demonstrationslÀge eller bara har en ritad ram. Det Àr inte sÀrskilt praktiskt.
Jag tyckte att det var nödvÀndigt att ompröva arkitekturen sÄ att vissa begrÀnsningar framtrÀdde:
- NĂ€rvaron av en enhet som visar och uppdaterar sig sjĂ€lv Ă€r ett problem, eftersom den enheten inte kan "veta" vad den ska rendera â bild och wireframe;
- Det finns en brist pÄ ett verktyg för att utbyta egenskaper och beteenden mellan separata enheter (exempel - egenskapen is_build_mode eller renderingbeteenden). Man skulle kunna anvÀnda arv, men i Rust finns det inget bra sÀtt att implementera detta. Vad jag verkligen behövde var komposition;
- Ett verktyg för att interagera mellan enheterna var nödvÀndigt för att tilldela mÀnniskor till tennisplaner;
- SjÀlva enheterna var en blandning av data och logik, vilket snabbt blev oöverskÄdligt.
Jag genomförde en ytterligare studie och upptÀckte arkitekturen , som vanligtvis anvÀnds i spel. HÀr Àr fördelarna med ECS:
- Data Àr separerade frÄn logik;
- Komposition istÀllet för arv;
- Dataorienterad arkitektur.
ECS kÀnnetecknas av tre grundlÀggande koncept:
- Enheter â en typ av objekt som refereras av en identifierare (det kan vara en spelare, en boll eller nĂ„got annat);
- Komponenter â dessa utgör enheterna. Exempel - renderingkomponent, positionskomponent och andra. Dessa Ă€r datalagringsutrymmen;
- System â de anvĂ€nder bĂ„de objekt och komponenter, samt innehĂ„ller beteende och logik baserat pĂ„ dessa data. Exempel - rendering system som gĂ„r igenom alla enheter med komponenter för rendering och hanterar ritningen.
Efter studien blev det klart att ECS löser sÄdana problem:
- AnvÀndning av komposition istÀllet för arv för systematisk organisering av enheter;
- Bli av med kodpackningen genom system för hantering;
- AnvÀndning av metoder som is_build_mode för att lagra wireframe-logiken pÄ samma stÀlle - i rendering systemet.
HÀr Àr vad som kom ut efter implementeringen av ECS.
resources -> hÀr finns alla tillgÄngar (bilder)
src
â komponenter
â position.rs
â person.rs
â tennis_court.rs
â floor.rs
â wireframe.rs
â mouse_tracked.rs
â resurser
â mouse.rs
â system
â rendering.rs
â constants.rs
â utils.rs
â world_factory.rs -> vĂ€rldsfabriksfunktioner
â main.rs -> huvudloop
Vi tilldelar personer till tennisbanor
ECS har gjort livet enklare. Nu har jag ett systematiskt sÀtt att lÀgga till data till entiteter och lÀgga till logik baserad pÄ dessa data. Detta har i sin tur möjliggjort att organisera fördelningen av mÀnniskor pÄ tennisbanorna.
Vad jag gjorde:
- lade till data om tilldelade banor i Person;
- lade till data om de fördelade personerna i TennisCourt;
- lade till CourtChoosingSystem, som gör det möjligt att analysera mÀnniskor och anlÀggningar, upptÀcka tillgÀngliga banor och fördela spelare pÄ dem;
- lade till PersonMovementSystem, som letar efter personer som Àr tilldelade banorna, och om de inte Àr dÀr, skickar folk dit de behövs.

Sammanfattning
Jag gillade verkligen att arbeta med detta enkla spel. Dessutom Àr jag nöjd med att jag anvÀnde Rust för att skriva det, eftersom:
- Rust ger dig det du behöver;
- det har utmÀrkt dokumentation, Rust Àr mycket elegant;
- konstans Àr coolt;
- jag behöver inte förlita mig pÄ kloning, kopiering eller liknande ÄtgÀrder, vilket jag ofta gjorde i C++;
- Options Àr vÀldigt anvÀndarvÀnliga, de hanterar ocksÄ fel bra;
- om projektet lyckas kompilera, fungerar det i 99% av fallen precis som det ska. Jag tycker att kompilatorns felmeddelanden Àr de bÀsta jag sett.
Spelutveckling med Rust Àr i sitt begynnelsefas. Men det finns redan ett stabilt och ganska stort community som arbetar för att göra Rust tillgÀngligt för alla. DÀrför ser jag med optimism pÄ sprÄkets framtid, ivrigt vÀntande pÄ resultaten av vÄrt gemensamma arbete.
Skillbox rekommenderar:
- Onlinekurs .
- Praktisk kurs .
- Praktisk Ärskurs .
KĂ€lla: habr.com
