Spil Rust på 24 timer: personlig udviklingsoplevelse

Spil Rust på 24 timer: personlig udviklingsoplevelse

I denne artikel vil jeg fortælle om min personlige erfaring med at udvikle et lille spil i Rust. Det tog omkring 24 timer at skabe en fungerende version (jeg arbejdede mest om aftenen eller i weekenden). Spillet er langt fra færdigt, men jeg tror, ​​at oplevelsen vil være givende. Jeg vil dele, hvad jeg lærte, og nogle observationer, jeg gjorde, mens jeg byggede spillet fra bunden.

Skillbox anbefaler: To-årigt praktisk kursus "Jeg er en PRO webudvikler".

Påmindelse: for alle læsere af "Habr" - en rabat på 10 rubler ved tilmelding til ethvert Skillbox-kursus ved hjælp af "Habr"-kampagnekoden.

Hvorfor rust?

Jeg valgte dette sprog, fordi jeg har hørt mange gode ting om det, og jeg ser det bliver mere og mere populært i spiludvikling. Før jeg skrev spillet, havde jeg lidt erfaring med at udvikle simple applikationer i Rust. Dette var lige nok til at give mig en følelse af frihed, mens jeg skrev spillet.

Hvorfor spillet og hvilken slags spil?

At lave spil er sjovt! Jeg ville ønske, der var flere grunde, men til "hjemme"-projekter vælger jeg emner, der ikke er for tæt knyttet til mit almindelige arbejde. Hvilket spil er dette? Jeg ville lave noget som en tennissimulator, der kombinerer Cities Skylines, Zoo Tycoon, Prison Architect og selve tennis. Generelt viste det sig at være et spil om et tennisakademi, hvor folk kommer for at spille.

Teknisk uddannelse

Jeg ville bruge Rust, men jeg vidste ikke præcis, hvor meget grundarbejde det ville tage at starte. Jeg ville ikke skrive pixel shaders og bruge drag-n-drop, så jeg ledte efter de mest fleksible løsninger.

Jeg fandt nyttige ressourcer, som jeg deler med dig:

Jeg udforskede flere Rust-spilmotorer og valgte i sidste ende Piston og ggez. Jeg stødte på dem, mens jeg arbejdede på et tidligere projekt. Til sidst valgte jeg ggez, fordi det virkede mere velegnet til at implementere et lille 2D-spil. Pistons modulære struktur er for kompleks for en nybegynder udvikler (eller en person, der arbejder med Rust for første gang).

Spilstruktur

Jeg brugte lidt tid på at tænke over projektets arkitektur. Det første skridt er at lave "land", mennesker og tennisbaner. Folk må bevæge sig rundt på banerne og vente. Spillere skal have færdigheder, der forbedres over tid. Plus, der skulle være en editor, der giver dig mulighed for at tilføje nye personer og domstole, men dette er ikke længere gratis.

Efter at have tænkt alt igennem, gik jeg i gang.

Spil skabelse

Begyndelse: Cirkler og abstraktioner

Jeg tog et eksempel fra ggez og fik en cirkel på skærmen. Vidunderlig! Nu nogle abstraktioner. Jeg troede, det ville være rart at abstrahere væk fra ideen om et spilobjekt. Hvert objekt skal gengives og opdateres som angivet her:

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

Dette stykke kode gav mig en fin liste over objekter, som jeg kunne opdatere og gengive i en lige så flot loop.

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 er nødvendig, fordi den indeholder alle kodelinjerne. Jeg brugte lidt tid på at adskille filerne og optimere mappestrukturen. Sådan så det ud efter det:
ressourcer -> det er her alle aktiver er (billeder)
src
- enheder
— game_object.rs
— cirkel.rs
— main.rs -> hovedsløjfe

Mennesker, gulve og billeder

Det næste trin er at oprette et Person-spilobjekt og indlæse billeder. Alt skal bygges på basis af 32*32 fliser.

Spil Rust på 24 timer: personlig udviklingsoplevelse

Tennisbaner

Efter at have studeret, hvordan tennisbaner ser ud, besluttede jeg at lave dem af 4*2 fliser. I starten var det muligt at lave et billede af denne størrelse eller at sammensætte 8 separate fliser. Men så indså jeg, at der kun var brug for to unikke fliser, og her er hvorfor.

I alt har vi to sådanne fliser: 1 og 2.

Hver sektion af banen består af flise 1 eller flise 2. De kan lægges ud som normalt eller vendes 180 grader.

Spil Rust på 24 timer: personlig udviklingsoplevelse

Grundlæggende konstruktion (montage) tilstand

Efter at det lykkedes mig at opnå gengivelse af websteder, personer og kort, indså jeg, at en grundlæggende samlingstilstand også var nødvendig. Jeg implementerede det sådan: når der trykkes på knappen, vælges objektet, og klikket placerer det på det ønskede sted. Så knap 1 giver dig mulighed for at vælge en bane, og knap 2 giver dig mulighed for at vælge en spiller.

Men vi skal stadig huske, hvad 1 og 2 betyder, så jeg tilføjede en wireframe for at gøre det klart, hvilket objekt der blev valgt. Sådan ser det ud.

Spil Rust på 24 timer: personlig udviklingsoplevelse

Arkitektur og refactoring spørgsmål

Nu har jeg flere spilobjekter: mennesker, baner og gulve. Men for at wireframes kan fungere, skal hver objektentitet fortælles, om objekterne selv er i demonstrationstilstand, eller om en ramme blot er tegnet. Dette er ikke særlig bekvemt.

Det forekom mig, at arkitekturen skulle gentænkes på en måde, der afslørede nogle begrænsninger:

  • At have en enhed, der gengiver og opdaterer sig selv, er et problem, fordi den entitet ikke vil være i stand til at "vide", hvad den skal gengive - et billede og en wireframe;
  • mangel på et værktøj til at udveksle egenskaber og adfærd mellem individuelle entiteter (for eksempel egenskaben is_build_mode eller adfærdsgengivelse). Det ville være muligt at bruge arv, selvom der ikke er nogen ordentlig måde at implementere det i Rust. Hvad jeg virkelig havde brug for var layoutet;
  • der var behov for et værktøj til interaktion mellem enheder for at udnævne folk til domstolene;
  • selve entiteterne var en blanding af data og logik, der hurtigt kom ud af kontrol.

Jeg foretog noget mere forskning og opdagede arkitekturen ECS - Entity Component System, som er almindeligt brugt i spil. Her er fordelene ved ECS:

  • data er adskilt fra logik;
  • sammensætning i stedet for arv;
  • datacentreret arkitektur.

ECS er karakteriseret ved tre grundlæggende begreber:

  • entiteter - den type objekt, som identifikatoren refererer til (det kunne være en spiller, en bold eller noget andet);
  • komponenter - enheder består af dem. Eksempel - gengivelseskomponent, lokationer og andet. Disse er datavarehuse;
  • systemer - de bruger både objekter og komponenter, plus indeholder adfærd og logik, der er baseret på disse data. Et eksempel er et gengivelsessystem, der itererer gennem alle entiteter med gengivelseskomponenter og udfører gengivelsen.

Efter at have studeret det blev det klart, at ECS løser følgende problemer:

  • bruge layout i stedet for arv til at organisere enheder systemisk;
  • slippe af med kode virvar gennem kontrolsystemer;
  • ved at bruge metoder som is_build_mode til at holde wireframe-logikken på samme sted - i renderingssystemet.

Dette er, hvad der skete efter implementering af ECS.

ressourcer -> det er her alle aktiver er (billeder)
src
- komponenter
—position.rs
— person.rs
— tennis_court.rs
— etage.rs
- wireframe.rs
— mouse_tracked.rs
- ressourcer
—mus.rs
- systemer
— rendering.rs
— konstanter.rs
— utils.rs
— world_factory.rs -> verdensfabriksfunktioner
— main.rs -> hovedsløjfe

Vi udnævner folk til domstolene

ECS har gjort livet lettere. Nu havde jeg en systematisk måde at tilføje data til enheder og tilføje logik baseret på disse data. Og det gjorde det til gengæld muligt at organisere fordelingen af ​​personer mellem domstolene.

Hvad har jeg gjort:

  • tilføjet data om tildelte domstole til Person;
  • tilføjet data om distribuerede personer til TennisCourt;
  • tilføjet CourtChoosingSystem, som giver dig mulighed for at analysere mennesker og baner, opdage tilgængelige baner og distribuere spillere til dem;
  • tilføjet et PersonMovementSystem, som leder efter folk, der er tilknyttet domstolene, og hvis de ikke er der, så sender folk, hvor de skal være.

Spil Rust på 24 timer: personlig udviklingsoplevelse

Opsummering

Jeg nød virkelig at arbejde på dette enkle spil. Desuden er jeg glad for, at jeg brugte Rust til at skrive det, fordi:

  • Rust giver dig, hvad du har brug for;
  • den har fremragende dokumentation, Rust er ret elegant;
  • konsistensen er kølig;
  • du behøver ikke ty til kloning, kopiering eller andre lignende handlinger, som jeg ofte gjorde i C++;
  • Valgmulighederne er meget nemme at bruge og håndterer fejl meget godt;
  • hvis projektet var i stand til at blive kompileret, så fungerer det 99% af tiden, og præcis som det skal. Jeg tror, ​​at compiler-fejlmeddelelserne er de bedste, jeg har set.

Spiludviklingen i Rust er lige begyndt. Men der er allerede et stabilt og ret stort samfund, der arbejder på at åbne Rust for alle. Derfor ser jeg på sprogets fremtid med optimisme og ser frem til resultaterne af vores fælles arbejde.

Skillbox anbefaler:

Kilde: www.habr.com

Tilføj en kommentar