Ludante Rust en 24 horoj: persona evoluiga sperto

Ludante Rust en 24 horoj: persona evoluiga sperto

En ĉi tiu artikolo mi parolos pri mia persona sperto pri evoluigado de malgranda ludo en Rust. Necesis ĉirkaŭ 24 horoj por krei funkciantan version (mi plejparte laboris vespere aŭ semajnfine). La ludo estas malproksima de finita, sed mi pensas, ke la sperto estos rekompenca. Mi dividos tion, kion mi lernis kaj kelkajn observojn, kiujn mi faris dum la konstruado de la ludo de nulo.

Skillbox rekomendas: Dujara praktika kurso "Mi estas PRO TTT-programisto".

Ni memorigas vin: por ĉiuj legantoj de "Habr" - rabato de 10 000 rubloj kiam oni enskribas en iu ajn Skillbox-kurso per la reklamkodo "Habr".

Kial Rusto?

Mi elektis ĉi tiun lingvon ĉar mi aŭdis multajn bonajn aferojn pri ĝi kaj mi vidas ĝin fariĝi pli kaj pli populara en luddisvolviĝo. Antaŭ ol verki la ludon, mi havis malmulte da sperto pri disvolvi simplajn aplikojn en Rust. Ĉi tio sufiĉis por doni al mi senton de libereco dum verkado de la ludo.

Kial la ludo kaj kia ludo?

Fari ludojn estas amuza! Mi deziras, ke estu pli da kialoj, sed por "hejmaj" projektoj mi elektas temojn, kiuj ne tro proksime rilatas al mia regula laboro. Kiu ludo estas ĉi tio? Mi volis fari ion similan al tenisa simulilo, kiu kombinas Cities Skylines, Zoo Tycoon, Prison Architect kaj tenison mem. Ĝenerale, ĝi rezultis esti ludo pri tenisa akademio, kie homoj venas por ludi.

Teknika trejnado

Mi volis uzi Ruston, sed mi ne sciis precize kiom da bazlaboro necesas por komenci. Mi ne volis skribi pikselojn kaj uzi drag-n-drop, do mi serĉis la plej flekseblajn solvojn.

Mi trovis utilajn rimedojn, kiujn mi dividas kun vi:

Mi esploris plurajn Rust-ludmotorojn, finfine elektante Piston kaj ggez. Mi renkontis ilin laborante pri antaŭa projekto. Fine mi elektis ggez ĉar ĝi ŝajnis pli taŭga por efektivigi malgrandan 2D-ludon. La modula strukturo de Piston estas tro kompleksa por komencanta programisto (aŭ iu, kiu laboras kun Rust por la unua fojo).

Ludstrukturo

Mi pasigis iom da tempo pensante pri la arkitekturo de la projekto. La unua paŝo estas fari "teron", homojn kaj tenisejojn. Homoj devas moviĝi ĉirkaŭ la tribunaloj kaj atendi. Ludantoj devas havi kapablojn kiuj pliboniĝas kun la tempo. Krome, devus ekzisti redaktilo, kiu ebligas al vi aldoni novajn homojn kaj tribunalojn, sed ĉi tio ne plu estas senpaga.

Pripensinte ĉion, mi eklaboris.

Ludokreado

Komenco: Rondoj kaj Abstraktaĵoj

Mi prenis ekzemplon de ggez kaj ricevis rondon sur la ekrano. Mirinda! Nun kelkaj abstraktaĵoj. Mi pensis, ke estus bone abstrakti for de la ideo de ludobjekto. Ĉiu objekto devas esti prezentita kaj ĝisdatigita kiel deklarite ĉi tie:

// 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(())
    }
}

Ĉi tiu kodo donis al mi belan liston de objektoj, kiujn mi povus ĝisdatigi kaj redoni en same bela buklo.

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 estas necesa ĉar ĝi enhavas ĉiujn liniojn de kodo. Mi pasigis iom da tempo apartigante la dosierojn kaj optimumigante la dosierujon. Jen kiel ĝi aspektis post tio:
rimedoj -> ĉi tie estas ĉiuj valoraĵoj (bildoj)
src
- entoj
— ludo_objekto.rs
— rondo.rs
— main.rs -> ĉefa buklo

Homoj, etaĝoj kaj bildoj

La sekva paŝo estas krei Person-ludan objekton kaj ŝargi bildojn. Ĉio devus esti konstruita surbaze de 32 * 32 kaheloj.

Ludante Rust en 24 horoj: persona evoluiga sperto

Tenis-tribunaloj

Studinte kiel aspektas tenisejoj, mi decidis fari ilin el 4*2 kaheloj. Komence, eblis fari bildon de ĉi tiu grandeco, aŭ kunmeti 8 apartajn kahelojn. Sed tiam mi konstatis, ke nur du unikaj kaheloj estas bezonataj, kaj jen kial.

Entute ni havas du tiajn kahelojn: 1 kaj 2.

Ĉiu sekcio de la tribunalo konsistas el kahelo 1 aŭ kahelo 2. Ili povas esti aranĝitaj kiel normale aŭ renversitaj 180 gradoj.

Ludante Rust en 24 horoj: persona evoluiga sperto

Baza konstrua (kunigo) reĝimo

Post kiam mi sukcesis atingi bildigon de retejoj, homoj kaj mapoj, mi konstatis, ke ankaŭ necesas baza kunigmaniero. Mi efektivigis ĝin tiel: kiam la butono estas premata, la objekto estas elektita, kaj la klako metas ĝin en la deziratan lokon. Do, butono 1 permesas elekti tribunalon, kaj butono 2 permesas elekti ludanton.

Sed ni ankoraŭ devas memori, kion signifas 1 kaj 2, do mi aldonis dratkadron por klarigi, kiu objekto estis elektita. Jen kiel ĝi aspektas.

Ludante Rust en 24 horoj: persona evoluiga sperto

Demandoj pri arkitekturo kaj refaktorado

Nun mi havas plurajn ludobjektojn: homojn, tribunalojn kaj etaĝojn. Sed por ke dratkadroj funkciu, al ĉiu objekto-unuo oni devas diri ĉu la objektoj mem estas en pruvreĝimo, aŭ ĉu kadro estas simple desegnita. Ĉi tio ne estas tre oportuna.

Ŝajnis al mi, ke la arkitekturo bezonas esti repensita en maniero, kiu malkaŝis kelkajn limigojn:

  • Havi enton kiu bildigas kaj ĝisdatigas sin estas problemo ĉar tiu ento ne povos "scii" kion ĝi supozas bildigi - bildon kaj dratkadron;
  • manko de ilo por interŝanĝi trajtojn kaj konduton inter unuopaj estaĵoj (ekzemple, la posedaĵo is_build_mode aŭ kondutbildigo). Eblus uzi heredon, kvankam ne ekzistas taŭga maniero efektivigi ĝin en Rust. Kion mi vere bezonis estis la aranĝo;
  • ilo por interago inter estaĵoj estis necesa por asigni homojn al tribunaloj;
  • la estaĵoj mem estis miksaĵo de datumoj kaj logiko, kiuj rapide senkontrolis.

Mi faris pli da esploro kaj malkovris la arkitekturon ECS - Enta Komponanta Sistemo, kiu estas ofte uzata en ludoj. Jen la avantaĝoj de ECS:

  • datumoj estas apartigitaj de logiko;
  • komponado anstataŭ heredo;
  • datencentra arkitekturo.

ECS estas karakterizita per tri bazaj konceptoj:

  • entoj - la speco de objekto al kiu la identigilo rilatas (ĝi povus esti ludanto, pilko, aŭ io alia);
  • komponantoj - estaĵoj konsistas el ili. Ekzemplo - bildiga komponanto, lokoj kaj aliaj. Ĉi tiuj estas datumstokejoj;
  • sistemoj - ili uzas kaj objektojn kaj komponantojn, krome enhavas konduton kaj logikon, kiuj baziĝas sur ĉi tiuj datumoj. Ekzemplo estas bildiga sistemo kiu ripetas tra ĉiuj unuoj kun bildigaj komponentoj kaj faras la bildigon.

Post studi ĝin, evidentiĝis, ke ECS solvas la sekvajn problemojn:

  • uzante aranĝon anstataŭ heredon por organizi entojn sisteme;
  • forigi kodon per kontrolsistemoj;
  • uzante metodojn kiel is_build_mode por konservi la drataran logikon en la sama loko - en la bildiga sistemo.

Jen kio okazis post efektivigo de ECS.

rimedoj -> ĉi tie estas ĉiuj valoraĵoj (bildoj)
src
- komponantoj
—pozicio.rs
— persono.rs
— tennis_court.rs
— etaĝo.rs
- wireframe.rs
— mouse_tracked.rs
- rimedoj
—muso.rs
- sistemoj
— bildigo.rs
— konstantoj.rs
— utils.rs
— world_factory.rs -> mondfabrikaj funkcioj
— main.rs -> ĉefa buklo

Ni asignas homojn al la tribunaloj

ECS faciligis la vivon. Nun mi havis sisteman manieron aldoni datumojn al entoj kaj aldoni logikon bazitan sur tiuj datumoj. Kaj tio, siavice, ebligis organizi la disdonadon de homoj inter la tribunaloj.

Kion mi faris:

  • aldonis datumojn pri asignitaj tribunaloj al Persono;
  • aldonis datumojn pri distribuitaj homoj al TennisCourt;
  • aldonis CourtChoosingSystem, kiu permesas vin analizi homojn kaj tribunalojn, detekti disponeblajn tribunalojn kaj distribui ludantojn al ili;
  • aldonis PersonMovementSystem, kiu serĉas homojn asignitajn al la tribunaloj, kaj se ili ne estas tie, tiam sendas homojn kie ili devas esti.

Ludante Rust en 24 horoj: persona evoluiga sperto

Supre

Mi tre ĝuis labori pri ĉi tiu simpla ludo. Krome, mi ĝojas, ke mi uzis Rust por skribi ĝin, ĉar:

  • Rusto donas al vi tion, kion vi bezonas;
  • ĝi havas bonegan dokumentadon, Rust estas sufiĉe eleganta;
  • konsistenco estas malvarmeta;
  • vi ne devas recurri al klonado, kopiado aŭ aliaj similaj agoj, kion mi ofte faris en C++;
  • Opcioj estas tre facile uzeblaj kaj tre bone pritraktas erarojn;
  • se la projekto povis esti kompilita, tiam 99% de la tempo ĝi funkcias, kaj ĝuste kiel ĝi devus. Mi pensas, ke la erarmesaĝoj de la kompililo estas la plej bonaj, kiujn mi vidis.

Luddisvolviĝo en Rust ĵus komenciĝas. Sed jam ekzistas stabila kaj sufiĉe granda komunumo laboranta por malfermi Rust al ĉiuj. Tial mi optimisme rigardas la estontecon de la lingvo, antaŭĝojante la rezultojn de nia komuna laboro.

Skillbox rekomendas:

fonto: www.habr.com

Aldoni komenton