Giocare a Rust in 24 ore: esperienza di sviluppo personale

Giocare a Rust in 24 ore: esperienza di sviluppo personale

In questo articolo parlerò della mia esperienza personale nello sviluppo di un piccolo gioco in Rust. Ci sono volute circa 24 ore per creare una versione funzionante (lavoravo principalmente la sera o nei fine settimana). Il gioco è lungi dall'essere finito, ma penso che l'esperienza sarà gratificante. Condividerò ciò che ho imparato e alcune osservazioni che ho fatto durante la creazione del gioco da zero.

Skillbox consiglia: Corso pratico di due anni "Sono uno sviluppatore web PRO".

Ti ricordiamo: per tutti i lettori di "Habr" - uno sconto di 10 rubli al momento dell'iscrizione a qualsiasi corso Skillbox utilizzando il codice promozionale "Habr".

Perché ruggine?

Ho scelto questo linguaggio perché ne ho sentito parlare molto bene e vedo che sta diventando sempre più popolare nello sviluppo di giochi. Prima di scrivere il gioco, avevo poca esperienza nello sviluppo di semplici applicazioni in Rust. Questo è stato appena sufficiente per darmi un senso di libertà mentre scrivevo il gioco.

Perché il gioco e che tipo di gioco?

Creare giochi è divertente! Vorrei che ci fossero più ragioni, ma per i progetti “casalinghi” scelgo argomenti che non sono troppo strettamente legati al mio lavoro regolare. Che gioco è questo? Volevo creare qualcosa come un simulatore di tennis che combinasse Cities Skylines, Zoo Tycoon, Prison Architect e il tennis stesso. In generale, si è rivelato essere un gioco su un'accademia di tennis in cui le persone vengono a giocare.

Allenamento tecnico

Volevo usare Rust, ma non sapevo esattamente quante basi ci sarebbero volute per iniziare. Non volevo scrivere pixel shader e utilizzare il drag-n-drop, quindi cercavo le soluzioni più flessibili.

Ho trovato risorse utili che condivido con te:

Ho esplorato diversi motori di gioco di Rust, scegliendo infine Piston e ggez. Li ho incontrati mentre lavoravo a un progetto precedente. Alla fine ho scelto ggez perché mi sembrava più adatto per realizzare un piccolo gioco 2D. La struttura modulare di Piston è troppo complessa per uno sviluppatore alle prime armi (o qualcuno che lavora con Rust per la prima volta).

Struttura del gioco

Ho passato un po’ di tempo a pensare all’architettura del progetto. Il primo passo è fare “terra”, persone e campi da tennis. Le persone devono muoversi nei tribunali e aspettare. I giocatori devono avere abilità che migliorano nel tempo. In più, dovrebbe esserci un editor che permetta di aggiungere nuove persone e tribunali, ma questo non è più gratuito.

Dopo aver pensato a tutto, mi sono messo al lavoro.

Creazione di un gioco

Inizio: Cerchi e astrazioni

Ho preso un esempio da ggez e ho ottenuto un cerchio sullo schermo. Meravigliosa! Ora alcune astrazioni. Ho pensato che sarebbe stato carino allontanarsi dall'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 fornito un bell'elenco di oggetti che potevo aggiornare e renderizzare in un ciclo altrettanto carino.

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 è necessario perché contiene tutte le righe di codice. Ho dedicato un po' di tempo a separare i file e a ottimizzare la struttura delle directory. Questo è quello che appariva dopo:
risorse -> qui è dove sono tutte le risorse (immagini)
src
- entità
— game_object.rs
— cerchio.rs
— main.rs -> ciclo principale

Persone, pavimenti e immagini

Il passaggio successivo è creare un oggetto di gioco Persona e caricare le immagini. Tutto dovrebbe essere costruito sulla base di tessere 32*32.

Giocare a Rust in 24 ore: esperienza di sviluppo personale

Campi da tennis

Dopo aver studiato come sono i campi da tennis, ho deciso di realizzarli con piastrelle 4*2. Inizialmente era possibile realizzare un'immagine di queste dimensioni oppure mettere insieme 8 tessere separate. Ma poi ho capito che erano necessarie solo due tessere uniche, ed ecco perché.

In totale abbiamo due di queste tessere: 1 e 2.

Ogni sezione del campo è composta dalla tessera 1 o dalla tessera 2. Possono essere disposte normalmente o capovolte di 180 gradi.

Giocare a Rust in 24 ore: esperienza di sviluppo personale

Modalità di costruzione (assemblaggio) di base

Dopo essere riuscito a ottenere il rendering di siti, persone e mappe, mi sono reso conto che era necessaria anche una modalità di assemblaggio di base. L'ho implementato in questo modo: quando si preme il pulsante, l'oggetto viene selezionato e il clic lo posiziona nella posizione desiderata. Quindi, il pulsante 1 ti consente di selezionare un campo e il pulsante 2 ti consente di selezionare un giocatore.

Ma dobbiamo ancora ricordare cosa significano 1 e 2, quindi ho aggiunto un wireframe per chiarire quale oggetto è stato selezionato. Questo è quello che sembra.

Giocare a Rust in 24 ore: esperienza di sviluppo personale

Domande sull'architettura e sul refactoring

Ora ho diversi oggetti di gioco: persone, campi e piani. Ma affinché i wireframe funzionino, è necessario che a ciascuna entità oggetto venga detto se gli oggetti stessi sono in modalità dimostrativa o se un frame è semplicemente disegnato. Questo non è molto conveniente.

Mi è sembrato che l’architettura dovesse essere ripensata in modo da rivelare alcuni limiti:

  • Avere un'entità che esegue il rendering e si aggiorna da sola è un problema perché quell'entità non sarà in grado di "sapere" cosa dovrebbe renderizzare: un'immagine e un wireframe;
  • mancanza di uno strumento per lo scambio di proprietà e comportamenti tra singole entità (ad esempio, la proprietà is_build_mode o il rendering del comportamento). Sarebbe possibile utilizzare l'ereditarietà, sebbene non esista un modo corretto per implementarla in Rust. Ciò di cui avevo veramente bisogno era il layout;
  • per assegnare le persone ai tribunali era necessario uno strumento di interazione tra enti;
  • le entità stesse erano un miscuglio di dati e logica che andarono rapidamente fuori controllo.

Ho fatto qualche ricerca in più e ho scoperto l'architettura ECS - Sistema di componenti di entità, che è comunemente usato nei giochi. Ecco i vantaggi dell’ECS:

  • i dati sono separati dalla logica;
  • composizione anziché eredità;
  • architettura incentrata sui dati.

ECS è caratterizzato da tre concetti fondamentali:

  • entità - il tipo di oggetto a cui si riferisce l'identificatore (potrebbe essere un giocatore, una palla o qualcos'altro);
  • componenti: le entità sono costituite da essi. Esempio: componente di rendering, posizioni e altro. Questi sono data warehouse;
  • sistemi: utilizzano sia oggetti che componenti e contengono comportamento e logica basati su questi dati. Un esempio è un sistema di rendering che scorre tutte le entità con componenti di rendering ed esegue il rendering.

Dopo averlo studiato, è diventato chiaro che ECS risolve i seguenti problemi:

  • utilizzare il layout anziché l'ereditarietà per organizzare le entità in modo sistematico;
  • eliminare il groviglio di codici attraverso i sistemi di controllo;
  • utilizzando metodi come is_build_mode per mantenere la logica wireframe nello stesso posto: nel sistema di rendering.

Questo è quello che è successo dopo l'implementazione di ECS.

risorse -> qui è dove sono tutte le risorse (immagini)
src
- componenti
—posizione.rs
— persona.rs
— tennis_court.rs
— pavimento.rs
-wireframe.rs
— mouse_tracked.rs
- risorse
—mouse.rs
- sistemi
— rendering.rs
— costanti.rs
— utils.rs
— world_factory.rs -> funzioni di fabbrica mondiale
— main.rs -> ciclo principale

Assegniamo le persone ai tribunali

ECS ha reso la vita più facile. Ora avevo un modo sistematico per aggiungere dati alle entità e aggiungere logica basata su tali dati. E questo, a sua volta, ha permesso di organizzare la distribuzione delle persone tra i tribunali.

Cosa ho fatto:

  • aggiunti dati sui tribunali assegnati alla Persona;
  • aggiunti dati sulle persone distribuite a TennisCourt;
  • aggiunto CourtChoosingSystem, che consente di analizzare persone e campi, individuare i campi disponibili e distribuire loro i giocatori;
  • ha aggiunto un PersonMovementSystem, che cerca le persone assegnate ai tribunali e, se non sono presenti, le invia dove devono essere.

Giocare a Rust in 24 ore: esperienza di sviluppo personale

Riassumendo

Mi è davvero piaciuto lavorare su questo semplice gioco. Inoltre, sono felice di aver usato Rust per scriverlo, perché:

  • Rust ti dà ciò di cui hai bisogno;
  • ha un'ottima documentazione, Rust è piuttosto elegante;
  • la consistenza è fresca;
  • non è necessario ricorrere a clonazioni, copie o altre azioni simili, cosa che facevo spesso in C++;
  • Le opzioni sono molto facili da usare e gestiscono molto bene gli errori;
  • se è stato possibile compilare il progetto, il 99% delle volte funzionerà ed esattamente come dovrebbe. Penso che i messaggi di errore del compilatore siano i migliori che abbia mai visto.

Lo sviluppo del gioco in Rust è appena iniziato. Ma esiste già una comunità stabile e abbastanza ampia che lavora per aprire Rust a tutti. Pertanto, guardo al futuro della lingua con ottimismo, aspettando con ansia i risultati del nostro lavoro comune.

Skillbox consiglia:

Fonte: habr.com

Aggiungi un commento