Giocare a Rust in 24 ore: esperienza personale di sviluppo

Giocare a Rust in 24 ore: esperienza personale di sviluppo

In questo articolo condividerò la mia esperienza nello sviluppo di un piccolo gioco su Rust. Ci sono volute circa 24 ore per creare una versione funzionante (principalmente ho lavorato la sera o nel fine settimana). Il gioco è ancora lontano dal completamento, ma credo che l'esperienza sarà utile. Condividerò ciò che ho imparato e alcune osservazioni fatte durante la costruzione del gioco da zero.

Skillbox consiglia: Corso pratico di due anni «Io sono uno sviluppatore web PRO».

Ricordiamo: per tutti i lettori di «Habr» — sconto di 10.000 rubli per l'iscrizione a qualsiasi corso Skillbox con il codice promozionale «Habr».

Perché Rust?

Ho scelto questo linguaggio perché ho sentito molte cose positive al riguardo e vedo che diventa sempre più popolare nel settore dello sviluppo di giochi. Prima di scrivere il gioco, avevo una leggera esperienza nello sviluppo di semplici applicazioni su Rust. Questo era sufficiente per sentire una certa libertà durante la scrittura del gioco.

Perché proprio un gioco e che gioco?

Creare giochi è divertente! Vorrei che ci fossero più motivi, ma per i progetti «domestici» scelgo temi che non sono troppo strettamente legati al mio lavoro abituale. Che gioco? Volevo creare qualcosa simile a un simulatore di tennis, dove si combinano Cities Skylines, Zoo Tycoon, Prison Architect e, appunto, il tennis. In sostanza, è venuto fuori un gioco su un'accademia di tennis, dove le persone possono giocare.

Preparazione tecnica

Volevo utilizzare Rust, ma non sapevo esattamente da quanto «zero» sarebbe stato necessario iniziare a lavorare. Non volevo scrivere shader pixel e utilizzare drag-n-drop, quindi cercavo le soluzioni più flessibili.

Ho trovato delle risorse utili, che condividerò con voi:

Ho esaminato diversi motori di gioco Rust, scegliendo infine Piston e ggez. Li avevo già incontrati lavorando al mio progetto precedente. Alla fine ho scelto ggez, poiché sembrava più adatto per realizzare un piccolo gioco 2D. La struttura modulare di Piston è troppo complessa per uno sviluppatore alle prime armi (o per chi lavora su Rust per la prima volta).

Struttura del gioco

Ho dedicato un po' di tempo a riflettere sull'architettura del progetto. Il primo passo è realizzare la «terra», le persone e i campi da tennis. Le persone devono muoversi sui campi e aspettare. I giocatori devono avere abilità che si perfezionano col tempo. Inoltre, deve esserci un editor che consenta di aggiungere nuove persone e campi, ma questo non è gratuito.

Dopo aver pensato a tutto, ho iniziato a lavorare.

Creazione di un gioco

Inizio: cerchi e astrazioni

Ho preso un esempio da ggez e ho ottenuto un cerchio sullo schermo. Straordinario! Ora un po' di astrazioni. Ho pensato che sarebbe utile astrarre l'idea di oggetto di gioco. Ogni oggetto deve essere renderizzato e aggiornato come indicato qui:

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

Questo pezzo di codice mi ha permesso di ottenere un'ottima lista di oggetti che posso aggiornare e rendere in un ciclo altrettanto ottimo.

mpl event::EventHandler per MainState {
    fn update(&mut self, context: &mut Context) -> GameResult {
        // Aggiorna tutti gli oggetti
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult {
        graphics::clear(context);
 
        // Disegna tutti gli oggetti
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

main.rs è necessario perché contiene tutte le righe di codice. Ho dedicato un po' di tempo a separare i file e ottimizzare la struttura delle directory. Ecco come è diventato dopo:
resources -> qui ci sono tutte le risorse (immagini)
src
— entità
— game_object.rs
— circle.rs
— main.rs -> ciclo principale

Persone, campi e immagini

La fase successiva è creare l'oggetto di gioco Person e caricare le immagini. Tutto deve essere costruito su base di mattonelle delle dimensioni 32*32.

Giocare a Rust in 24 ore: esperienza personale di sviluppo

Campi da tennis

Dopo aver studiato com'erano fatti i campi da tennis, ho deciso di farli con mattonelle 4*2. Inizialmente si poteva creare un'immagine di tale dimensione oppure comporre insieme 8 mattonelle separate. Ma poi ho capito che servivano solo due mattonelle uniche, ecco perché.

In totale abbiamo due mattonelle: 1 e 2.

Ogni sezione del campo consiste di un mattonella 1 o mattonella 2. Possono essere disposte come al solito o essere capovolte di 180 gradi.

Giocare a Rust in 24 ore: esperienza personale di sviluppo

Modalità principale di costruzione (assemblaggio)

Dopo aver reso visibili i campi, le persone e le mappe, ho capito che era necessaria anche una modalità di assemblaggio di base. L'ho implementata così: quando il pulsante è premuto, l'oggetto è selezionato e il clic lo posiziona nel luogo desiderato. Così, il pulsante 1 permette di selezionare il campo, mentre il pulsante 2 consente di selezionare un giocatore.

Ma è anche necessario ricordare cosa significano 1 e 2, quindi ho aggiunto un wireframe per far capire quale oggetto è selezionato. Ecco come appare.

Giocare a Rust in 24 ore: esperienza personale di sviluppo

Domande sull'architettura e il refactoring

Ora ho diversi oggetti di gioco: persone, campi e piani. Ma affinché i wireframe funzionino, è necessario comunicare a ciascuna entità dell'oggetto se gli oggetti stessi sono in modalità dimostrativa o se è semplicemente disegnata una cornice. Non è molto comodo.

Ho pensato che fosse necessario ripensare l'architettura in modo che emergessero alcune limitazioni:

  • la presenza di un'entità che si visualizza e si aggiorna da sola è un problema, poiché quest'entità non sarà in grado di 'sapere' cosa deve renderizzare: l'immagine e il wireframe;
  • la mancanza di uno strumento per lo scambio di proprietà e comportamenti tra entità separate (ad esempio, la proprietà is_build_mode o il rendering del comportamento). Si potrebbe utilizzare l'ereditarietà, anche se in Rust non c'è un modo normale per realizzarla. Ciò di cui avevo davvero bisogno era la composizione;
  • uno strumento per interagire tra le entità era necessario per assegnare persone ai campi;
  • le stesse entità rappresentavano una miscela di dati e logica, il che usciva rapidamente fuori controllo.

Ho condotto un'ulteriore ricerca e ho scoperto l'architettura ECS — Entity Component System, che è comunemente usata nei giochi. Ecco i vantaggi dell'ECS:

  • i dati sono separati dalla logica;
  • composizione invece di ereditarietà;
  • architettura orientata ai dati.

Per l'ECS ci sono tre concetti di base:

  • entità — tipo di oggetto a cui si riferisce un identificatore (questo può essere un giocatore, una pallina o altro);
  • componenti — da cui sono composte le entità. Esempio — componente di rendering, posizione e altri. Questi sono contenitori di dati;
  • sistemi — utilizzano sia oggetti che componenti, più contengono comportamento e logica basati su questi dati. Esempio — sistema di rendering che esamina tutte le entità con componenti per il rendering e si occupa del disegno.

Dopo lo studio è diventato chiaro che l'ECS risolve i seguenti problemi:

  • applicazione della composizione invece dell'ereditarietà per l'organizzazione sistemica delle entità;
  • eliminazione del disordine del codice attraverso sistemi di gestione;
  • uso di metodi come is_build_mode per mantenere la logica del wireframe nello stesso luogo — nel sistema di rendering.

Ecco cosa è risultato dopo l'implementazione dell'ECS.

resources -> qui ci sono tutte le risorse (immagini)
src
— components
— position.rs
— person.rs
— tennis_court.rs
— floor.rs
— wireframe.rs
— mouse_tracked.rs
— resources
— mouse.rs
— systems
— rendering.rs
— constants.rs
— utils.rs
— world_factory.rs -> funzioni della fabbrica mondiale
— main.rs -> ciclo principale

Assegniamo persone ai campi

ECS ha semplificato la vita. Adesso avevo un metodo sistematico per aggiungere dati alle entità e integrare logica basata su questi dati. Questo, a sua volta, ha permesso di organizzare la distribuzione delle persone sui campi.

Cosa ho fatto:

  • ho aggiunto dati sui campi assegnati a Persone;
  • ho aggiunto dati sulle persone distribuite a TennisCourt;
  • ho aggiunto CourtChoosingSystem, che consente di analizzare le persone e i campi, scoprire i campi disponibili e distribuire i giocatori su di essi;
  • ho aggiunto il sistema PersonMovementSystem, che cerca le persone assegnate ai campi e, se non sono lì, le invia dove necessario.

Giocare a Rust in 24 ore: esperienza personale di sviluppo

Riepiloghiamo

Mi è piaciuto molto lavorare a questo semplice gioco. Inoltre, sono soddisfatta di aver utilizzato Rust per scriverlo, perché:

  • Rust ti offre ciò di cui hai bisogno;
  • ha una documentazione eccellente, Rust è molto elegante;
  • la costanza è fantastica;
  • non è necessario ricorrere a clonazioni, copie o azioni simili, cosa che facevo spesso in C++;
  • Options è molto comodo da usare, gestisce anche bene gli errori;
  • se il progetto è riuscito a compilarsi, allora nel 99% dei casi funziona esattamente come dovrebbe. I messaggi di errore del compilatore sono, a mio avviso, i migliori che abbia mai visto.

Lo sviluppo di giochi in Rust è solo agli inizi. Ma c'è già una comunità stabile e piuttosto ampia che lavora per rendere Rust accessibile a tutti. Quindi guardo al futuro del linguaggio con ottimismo, ansiosa di vedere i risultati del nostro lavoro comune.

Skillbox consiglia:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster