Jucând Rust în 24 de ore: experiență de dezvoltare personală

Jucând Rust în 24 de ore: experiență de dezvoltare personală

În acest articol voi vorbi despre experiența mea personală de a dezvolta un joc mic în Rust. A durat aproximativ 24 de ore pentru a crea o versiune de lucru (am lucrat mai ales seara sau în weekend). Jocul este departe de a fi încheiat, dar cred că experiența va fi plină de satisfacții. Voi împărtăși ceea ce am învățat și câteva observații pe care le-am făcut în timp ce construiam jocul de la zero.

Skillbox recomandă: Curs practic de doi ani „Sunt un dezvoltator web PRO”.

Amintim: pentru toți cititorii „Habr” - o reducere de 10 de ruble la înscrierea la orice curs Skillbox folosind codul promoțional „Habr”.

De ce Rust?

Am ales acest limbaj pentru că am auzit multe lucruri bune despre el și văd că devine din ce în ce mai popular în dezvoltarea jocurilor. Înainte de a scrie jocul, aveam puțină experiență în dezvoltarea de aplicații simple în Rust. Acest lucru a fost suficient pentru a-mi oferi un sentiment de libertate în timp ce scriam jocul.

De ce jocul și ce fel de joc?

A face jocuri este distractiv! Mi-aș dori să existe mai multe motive, dar pentru proiectele „acasă” aleg subiecte care nu sunt prea strâns legate de munca mea obișnuită. Ce joc este acesta? Am vrut să fac ceva ca un simulator de tenis care să combină Cities Skylines, Zoo Tycoon, Prison Architect și tenisul în sine. În general, sa dovedit a fi un joc despre o academie de tenis unde oamenii vin să joace.

Antrenament tehnic

Am vrut să folosesc Rust, dar nu știam exact cât de mult lucru ar fi nevoie pentru a începe. Nu am vrut să scriu pixel shadere și să folosesc drag-n-drop, așa că am căutat cele mai flexibile soluții.

Am găsit resurse utile pe care le împărtășesc cu voi:

Am explorat mai multe motoare de joc Rust, alegând în cele din urmă Piston și ggez. Le-am întâlnit în timp ce lucram la un proiect anterior. Până la urmă, am ales ggez pentru că mi s-a părut mai potrivit pentru implementarea unui mic joc 2D. Structura modulară a lui Piston este prea complexă pentru un dezvoltator începător (sau cineva care lucrează cu Rust pentru prima dată).

Structura jocului

Am petrecut ceva timp gândindu-mă la arhitectura proiectului. Primul pas este să faci „pământ”, oameni și terenuri de tenis. Oamenii trebuie să se miște prin tribunale și să aștepte. Jucătorii trebuie să aibă abilități care se îmbunătățesc în timp. În plus, ar trebui să existe un editor care să îți permită să adaugi oameni și instanțe noi, dar acesta nu mai este gratuit.

După ce am gândit totul, m-am apucat de treabă.

Crearea jocului

Început: cercuri și abstracții

Am luat un exemplu de la ggez și am primit un cerc pe ecran. Minunat! Acum câteva abstracții. M-am gândit că ar fi frumos să fac abstracție de la ideea unui obiect de joc. Fiecare obiect trebuie redat și actualizat așa cum se menționează aici:

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

Această bucată de cod mi-a oferit o listă frumoasă de obiecte pe care le-aș putea actualiza și reda într-o buclă la fel de frumoasă.

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 este necesar deoarece conține toate liniile de cod. Am petrecut puțin timp separând fișierele și optimizând structura directoarelor. Iată cum arăta după aceea:
resurse -> aici sunt toate activele (imagini)
src
- entitati
— game_object.rs
— cerc.rs
— main.rs -> buclă principală

Oameni, etaje și imagini

Următorul pas este să creați un obiect de joc Persoană și să încărcați imagini. Totul ar trebui să fie construit pe baza de plăci de 32*32.

Jucând Rust în 24 de ore: experiență de dezvoltare personală

Terenuri de tenis

După ce am studiat cum arată terenurile de tenis, am decis să le fac din plăci de 4*2. Inițial, a fost posibil să se realizeze o imagine de această dimensiune sau să se pună împreună 8 plăci separate. Dar apoi mi-am dat seama că sunt necesare doar două plăci unice și iată de ce.

În total avem două astfel de plăci: 1 și 2.

Fiecare secțiune a terenului este formată din țiglă 1 sau țiglă 2. Pot fi așezate normal sau răsturnate la 180 de grade.

Jucând Rust în 24 de ore: experiență de dezvoltare personală

Mod de construcție de bază (asamblare).

După ce am reușit să realizez randarea site-urilor, a oamenilor și a hărților, mi-am dat seama că era nevoie și de un mod de asamblare de bază. Am implementat-o ​​astfel: atunci când butonul este apăsat, obiectul este selectat, iar clicul îl plasează în locul dorit. Deci, butonul 1 vă permite să selectați un teren, iar butonul 2 vă permite să selectați un jucător.

Dar încă trebuie să ne amintim ce înseamnă 1 și 2, așa că am adăugat un cadru sârmă pentru a clarifica ce obiect a fost selectat. Așa arată.

Jucând Rust în 24 de ore: experiență de dezvoltare personală

Întrebări de arhitectură și refactorizare

Acum am mai multe obiecte de joc: oameni, terenuri și etaje. Dar pentru ca wireframes-urile să funcționeze, fiecărei entitati obiect trebuie să i se spună dacă obiectele în sine sunt în modul demonstrativ sau dacă un cadru este pur și simplu desenat. Acest lucru nu este foarte convenabil.

Mi s-a părut că arhitectura trebuie regândită într-un mod care să dezvăluie unele limitări:

  • A avea o entitate care se redă și se actualizează este o problemă, deoarece acea entitate nu va putea „ști” ce ar trebui să redea - o imagine și un cadru fir;
  • lipsa unui instrument pentru schimbul de proprietăți și comportament între entități individuale (de exemplu, proprietatea is_build_mode sau randarea comportamentului). Ar fi posibil să se folosească moștenirea, deși nu există o modalitate adecvată de a o implementa în Rust. Ceea ce aveam cu adevărat nevoie era aspectul;
  • a fost nevoie de un instrument de interacțiune între entități pentru repartizarea persoanelor în instanțe;
  • entitățile în sine erau un amestec de date și logică care scăpa rapid de sub control.

Am făcut mai multe cercetări și am descoperit arhitectura ECS - Entity Component System, care este folosit în mod obișnuit în jocuri. Iată beneficiile ECS:

  • datele sunt separate de logică;
  • compoziția în loc de moștenire;
  • arhitectură centrată pe date.

ECS se caracterizează prin trei concepte de bază:

  • entități - tipul de obiect la care se referă identificatorul (ar putea fi un jucător, o minge sau altceva);
  • componente - entitățile sunt formate din ele. Exemplu - componenta de randare, locații și altele. Acestea sunt depozite de date;
  • sisteme - folosesc atât obiecte, cât și componente, plus conțin comportament și logică care se bazează pe aceste date. Un exemplu este un sistem de randare care iterează prin toate entitățile cu componente de randare și face randarea.

După ce l-am studiat, a devenit clar că ECS rezolvă următoarele probleme:

  • utilizarea aspectului în loc de moștenire pentru a organiza entitățile în mod sistemic;
  • scăparea de amestecul de coduri prin sisteme de control;
  • folosind metode precum is_build_mode pentru a menține logica wireframe în același loc - în sistemul de randare.

Acesta este ceea ce s-a întâmplat după implementarea ECS.

resurse -> aici sunt toate activele (imagini)
src
- componente
—poziție.rs
— persoană.rs
— teren_tenis.rs
— etaj.rs
- wireframe.rs
— mouse_tracked.rs
- resurse
—mouse.rs
- sisteme
— redare.rs
— constante.rs
— utils.rs
— world_factory.rs -> funcții world factory
— main.rs -> buclă principală

Atribuim oameni la tribunale

ECS a făcut viața mai ușoară. Acum aveam o modalitate sistematică de a adăuga date la entități și de a adăuga logică bazată pe acele date. Și aceasta, la rândul său, a făcut posibilă organizarea repartizării oamenilor între instanțe.

Ce am facut:

  • au adăugat date despre instanțele atribuite Persoanei;
  • au adăugat date despre persoanele distribuite la TennisCourt;
  • a adăugat CourtChoosingSystem, care vă permite să analizați oamenii și terenurile, să detectați terenurile disponibile și să le distribuiți jucătorii;
  • a adăugat un PersonMovementSystem, care caută persoane desemnate în instanțe și, dacă nu sunt acolo, trimite oamenii acolo unde trebuie.

Jucând Rust în 24 de ore: experiență de dezvoltare personală

Rezumând

Mi-a plăcut foarte mult să lucrez la acest joc simplu. Mai mult, mă bucur că am folosit Rust pentru a-l scrie, pentru că:

  • Rugina vă oferă ceea ce aveți nevoie;
  • are documentatie excelenta, Rust este destul de elegant;
  • consistența este rece;
  • nu trebuie să recurgeți la clonare, copiere sau alte acțiuni similare, ceea ce am făcut adesea în C++;
  • Opțiunile sunt foarte ușor de utilizat și gestionează foarte bine erorile;
  • dacă proiectul a putut fi compilat, atunci 99% din timp funcționează și exact așa cum ar trebui. Cred că mesajele de eroare ale compilatorului sunt cele mai bune pe care le-am văzut.

Dezvoltarea jocului în Rust abia începe. Dar există deja o comunitate stabilă și destul de mare care lucrează pentru a deschide Rust pentru toată lumea. Prin urmare, privesc viitorul limbii cu optimism, așteptând cu nerăbdare rezultatele muncii noastre comune.

Skillbox recomandă:

Sursa: www.habr.com

Adauga un comentariu