Rusti mängimine 24 tunniga: isikliku arengu kogemus
Selles artiklis räägin oma isiklikust kogemusest väikese mängu arendamisel Rustis. Tööversiooni loomiseks kulus umbes 24 tundi (töötasin enamasti õhtuti või nädalavahetustel). Mäng pole veel kaugeltki lõppenud, kuid arvan, et kogemus on rahuldust pakkuv. Jagan õpitut ja mõningaid tähelepanekuid, mida tegin mängu nullist ülesehitamisel.
Tuletame meelde:kõigile "Habr" lugejatele - allahindlus 10 000 rubla, kui registreerute mis tahes Skillboxi kursusele, kasutades sooduskoodi "Habr".
Miks Rust?
Valisin selle keele, kuna olen selle kohta palju head kuulnud ja näen, et see muutub mänguarenduses aina populaarsemaks. Enne mängu kirjutamist oli mul vähe kogemusi Rustis lihtsate rakenduste arendamisel. Sellest piisas, et anda mulle mängu kirjutamise ajal vabadustunne.
Miks mäng ja millist mängu?
Mängude tegemine on lõbus! Soovin, et põhjuseid oleks rohkem, aga “koduprojektide” jaoks valin teemad, mis pole minu tavatööga liiga tihedalt seotud. Mis mäng see on? Tahtsin teha midagi tennise simulaatori sarnast, mis ühendaks Cities Skylinesi, Zoo Tycooni, Prison Architecti ja tennise enda. Üldiselt kujunes see mänguks tenniseakadeemiast, kuhu tullakse mängima.
Tehniline koolitus
Tahtsin kasutada Rustit, kuid ma ei teadnud täpselt, kui palju eeltööd selle käivitamine nõuab. Ma ei tahtnud kirjutada pikslivarjureid ja kasutada drag-n-drop-i, seega otsisin kõige paindlikumaid lahendusi.
Uurisin mitmeid Rusti mängumootoreid, valides lõpuks Pistoni ja Ggezi. Sattusin nendega kokku eelmise projekti kallal töötades. Lõpuks valisin ggezi, sest see tundus sobivam väikese 2D mängu realiseerimiseks. Kolvi moodulstruktuur on liiga keeruline algajale arendajale (või inimesele, kes töötab Rustiga esimest korda).
Mängu struktuur
Mõtisklesin mõnda aega projekti arhitektuuri üle. Esimene samm on teha "maa", inimesed ja tenniseväljakud. Inimesed peavad kohtutes ringi liikuma ja ootama. Mängijatel peavad olema oskused, mis aja jooksul paranevad. Lisaks peaks olema redaktor, mis võimaldab teil lisada uusi inimesi ja kohtuid, kuid see pole enam tasuta.
Olles kõik läbi mõelnud, asusin tööle.
Mängu loomine
Algus: ringid ja abstraktsioonid
Võtsin näite ggezist ja sain ekraanile ringi. Imeline! Nüüd mõned abstraktsioonid. Arvasin, et oleks tore mänguobjekti ideest eemalduda. Iga objekt tuleb renderdada ja värskendada, nagu siin on kirjeldatud:
// 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(())
}
}
See kooditükk andis mulle kena loendi objektidest, mida saaksin värskendada ja sama kena tsüklina renderdada.
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 on vajalik, kuna see sisaldab kõiki koodiridu. Kulutasin veidi aega failide eraldamisele ja kataloogistruktuuri optimeerimisele. Pärast seda nägi see välja selline: ressursid -> siin on kõik varad (pildid)
src
- üksused
- game_object.rs
— ring.rs
— main.rs -> põhisilmus
Inimesed, põrandad ja pildid
Järgmise sammuna tuleb luua Person mänguobjekt ja laadida pilte. Kõik tuleks ehitada 32*32 plaatide baasil.
Tenniseväljakud
Olles uurinud, millised tenniseväljakud välja näevad, otsustasin teha need 4*2 plaatidest. Esialgu oli võimalik teha sellises mõõdus pilt ehk siis kokku panna 8 eraldi plaati. Kuid siis sain aru, et vaja on ainult kahte ainulaadset plaati ja siin on põhjus.
Kokku on meil kaks sellist plaati: 1 ja 2.
Iga väljaku sektsioon koosneb plaadist 1 või plaadist 2. Neid saab paigutada tavapäraselt või pöörata 180 kraadi.
Ehitamise (montaaži) põhirežiim
Pärast seda, kui mul õnnestus saavutada saitide, inimeste ja kaartide renderdamine, mõistsin, et vaja on ka elementaarset montaažirežiimi. Tegin selle nii: nupule vajutades valitakse objekt välja ja klõps asetab selle soovitud kohta. Niisiis, nupp 1 võimaldab valida väljaku ja nupp 2 võimaldab valida mängija.
Kuid me peame siiski meeles pidama, mida 1 ja 2 tähendavad, seega lisasin traatraami, et oleks selge, milline objekt on valitud. See näeb välja selline.
Arhitektuuri ja ümberkujundamise küsimused
Nüüd on mul mitu mänguobjekti: inimesed, väljakud ja põrandad. Kuid selleks, et traatraamid töötaksid, tuleb igale objektiolemile öelda, kas objektid ise on demonstratsioonirežiimis või on raam lihtsalt joonistatud. See pole eriti mugav.
Mulle tundus, et arhitektuur tuleb ümber mõelda viisil, mis paljastas mõned piirangud:
Ennast renderdava ja värskendava olemi omamine on probleem, sest see olem ei saa "tea", mida ta peaks renderdama – pilti ja traatraami;
tööriista puudumine omaduste ja käitumise vahetamiseks üksikute üksuste vahel (näiteks atribuut is_build_mode või käitumise renderdamine). Pärimist oleks võimalik kasutada, kuigi Rustis seda õiget juurutada pole. Mida ma tõesti vajasin, oli paigutus;
inimeste kohtusse määramiseks oli vaja üksuste vahelise suhtluse vahendit;
olemid ise olid segu andmetest ja loogikast, mis väljusid kiiresti kontrolli alt.
Uurisin veel veidi ja avastasin arhitektuuri ECS – olemikomponentide süsteem, mida tavaliselt mängudes kasutatakse. Siin on ECS-i eelised:
andmed on loogikast eraldatud;
kompositsioon pärimise asemel;
andmekeskne arhitektuur.
ECS-i iseloomustavad kolm põhikontseptsiooni:
olemid – objekti tüüp, millele identifikaator viitab (see võib olla mängija, pall või midagi muud);
komponendid – olemid koosnevad neist. Näide - renderduskomponent, asukohad ja muud. Need on andmelaod;
süsteemid – need kasutavad nii objekte kui komponente ning sisaldavad nendel andmetel põhinevat käitumist ja loogikat. Näiteks on renderdussüsteem, mis itereerib läbi kõik renderduskomponentidega olemid ja teeb renderduse.
Pärast selle uurimist sai selgeks, et ECS lahendab järgmised probleemid:
olemite süsteemseks korraldamiseks pärandi asemel paigutuse kasutamine;
kasutades selliseid meetodeid nagu is_build_mode, et hoida traatraami loogikat samas kohas – renderdussüsteemis.
See juhtus pärast ECS-i rakendamist.
ressursid -> siin on kõik varad (pildid)
src
- komponendid
—positsioon.rs
— isik.rs
- tennis_court.rs
— korrus.rs
- wireframe.rs
- mouse_tracked.rs
- ressursid
-hiir.rs
- süsteemid
— renderdamine.rs
— konstandid.rs
— utils.rs
— world_factory.rs -> maailma tehase funktsioonid
— main.rs -> põhisilmus
Me määrame inimesed kohtusse
ECS on elu lihtsamaks teinud. Nüüd oli mul süstemaatiline viis üksustele andmete lisamiseks ja nende andmete põhjal loogika lisamiseks. Ja see omakorda võimaldas korraldada inimeste jaotust kohtute vahel.
Mida ma olen teinud:
lisanud Isikule andmed määratud kohtute kohta;
lisas TennisCourtile andmed hajutatud inimeste kohta;
lisatud CourtChoosingSystem, mis võimaldab analüüsida inimesi ja väljakuid, tuvastada saadaolevaid väljakuid ja jagada neile mängijaid;
lisas PersonMovementSystem, mis otsib kohtusse määratud inimesi ja kui neid seal pole, siis saadab inimesed sinna, kus nad olema peavad.
Kokkuvõtteks
Mulle väga meeldis selle lihtsa mängu kallal töötada. Lisaks on mul hea meel, et kasutasin selle kirjutamiseks Rustit, sest:
Rooste annab teile selle, mida vajate;
sellel on suurepärane dokumentatsioon, Rust on üsna elegantne;
konsistents on lahe;
te ei pea kasutama kloonimist, kopeerimist ega muid sarnaseid toiminguid, mida ma C++-s sageli tegin;
Valikud on väga lihtsalt kasutatavad ja vead väga hästi toime;
Kui projekt suudeti koostada, siis 99% ajast see toimib ja täpselt nii nagu peab. Arvan, et kompilaatori veateated on parimad, mida ma näinud olen.
Mänguarendus Rustis alles algab. Kuid juba töötab stabiilne ja üsna suur kogukond, et avada Rust kõigile. Seetõttu vaatan keele tulevikku optimistlikult, oodates meie ühise töö tulemusi.