Playing Rust in 24 hours: sperienza di sviluppu persunale
In questu articulu, parraraghju di a mo sperienza persunale di sviluppà un picculu ghjocu in Rust. Pigliò circa 24 ore per creà una versione di travagliu (aghju travagliatu soprattuttu in a sera o in u weekend). U ghjocu hè luntanu da esse finitu, ma pensu chì l'esperienza serà gratificante. Aghju sparte ciò chì aghju amparatu è alcune osservazioni chì aghju fattu mentre custruisce u ghjocu da zero.
Ramintemu:per tutti i lettori di "Habr" - un scontu di 10 000 rubles quandu si iscrizzione in ogni cursu Skillbox cù u codice promozionale "Habr".
Perchè Rust?
Aghju sceltu sta lingua perchè aghju intesu assai cose boni è vecu chì diventa sempre più populari in u sviluppu di u ghjocu. Prima di scrive u ghjocu, aghju avutu pocu sperienza in u sviluppu di applicazioni simplici in Rust. Questu era solu abbastanza per dà un sensu di libertà mentre scrive u ghjocu.
Perchè u ghjocu è chì tipu di ghjocu?
Fà ghjochi hè divertente! Vulariu chì ci era più ragiuni, ma per i prughjetti di "casa" sceglite temi chì ùn sò micca troppu in relazione cù u mo travagliu regulare. Chì ghjocu hè questu? Vuliu fà qualcosa cum'è un simulatore di tennis chì combina Cities Skylines, Zoo Tycoon, Prison Architect è u tennis stessu. In generale, hè diventatu un ghjocu nantu à una accademia di tennis induve a ghjente vene à ghjucà.
Formazione tecnica
Vuliu usà Rust, ma ùn sapia micca esattamente quantu travagliu di basi ci vole à principià. Ùn vulia micca scrive pixel shaders è aduprà drag-n-drop, cusì cercava e soluzioni più flessibili.
Aghju trovu risorse utili chì sparte cun voi:
Avemu ancu ghjucà - una lista di elementi Rust necessariu per u sviluppu di u ghjocu;
Aghju esploratu parechji motori di ghjocu Rust, in fine di sceglie Piston è ggez. L'aghju scontru mentre travagliava in un prughjettu precedente. In fine, aghju sceltu ggez perchè pareva più adattatu per implementà un picculu ghjocu 2D. A struttura modulare di Piston hè troppu cumplessa per un sviluppatore principiante (o qualchissia chì travaglia cù Rust per a prima volta).
Struttura di ghjocu
Aghju passatu qualchì tempu à pensà à l'architettura di u prugettu. U primu passu hè di fà "terra", ghjente è campi di tennis. A ghjente hà da spustà intornu à i tribunali è aspittà. I ghjucatori anu da avè cumpetenze chì migliurà cù u tempu. In più, ci deve esse un editore chì permette di aghjunghje novi persone è tribunale, ma questu ùn hè più liberu.
Dopu avè pensatu tuttu, aghju avutu à travaglià.
Creazione di ghjocu
Principiu: Cerchi è Astrazioni
Aghju pigliatu un esempiu da ggez è aghju avutu un cerculu nantu à u screnu. Meravigliosa! Avà qualchi astrazioni. Pensu chì saria bonu per astrattu da l'idea di un ughjettu di ghjocu. Ogni ughjettu deve esse rende è aghjurnatu cum'è dichjaratu quì:
// 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(())
}
}
Stu pezzu di codice m'hà datu una bella lista di l'uggetti chì puderia aghjurnà è rende in un loop ugualmente bellu.
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 hè necessariu perchè cuntene tutte e linee di codice. Aghju passatu un pocu di tempu per separà i schedari è ottimizendu a struttura di u cartulare. Eccu ciò chì pareva dopu: risorse -> quì hè quì tutti l'assi sò (imaghjini)
src
- entità
- game_object.rs
- circulu.rs
- main.rs -> loop principale
Persone, piani è imagine
U prossimu passu hè di creà un oggettu di ghjocu Persona è carica images. Tuttu deve esse custruitu nantu à a basa di 32 * 32 tile.
Campi di tennis
Dopu avè studiatu ciò chì i campi di tennis sò, decisu di fà da 4 * 2 tile. In principiu, era pussibule di fà una maghjina di questa dimensione, o di mette inseme 8 tile separati. Ma poi aghju realizatu chì solu duie tile uniche eranu necessarie, è eccu perchè.
In totale, avemu dui tile: 1 è 2.
Ogni rùbbrica di u tribunale hè custituitu da tile 1 o tile 2. Puderanu esse disposti cum'è normale o flipped 180 gradi.
Modu di custruzzione basica (assemblea).
Dopu avè riesciutu à ottene u rendering di siti, persone è carte, aghju realizatu chì un modu di assemblea di basa era ancu necessariu. L'aghju implementatu cusì: quandu u buttone hè premutu, l'ughjettu hè sceltu, è u clicu u mette in u locu desideratu. Allora, u buttone 1 permette di selezziunà un tribunale, è u buttone 2 permette di sceglie un ghjucatore.
Ma avemu sempre bisognu di ricurdà ciò chì significanu 1 è 2, cusì aghju aghjustatu un wireframe per fà chjaru quale ughjettu hè statu sceltu. Questu hè ciò chì pare.
Quistioni di architettura è refactoring
Avà aghju parechji ogetti di ghjocu: persone, tribunali è pavimenti. Ma per u funziunamentu di i wireframes, ogni entità di l'ughjettu deve esse dichjaratu s'ellu l'uggetti stessi sò in modu di dimostrazione, o se un quadru hè simplicemente disegnatu. Questu ùn hè micca assai convenientu.
Mi paria chì l'architettura avia da esse ripensata in una manera chì palesa certi limitazioni:
Avè una entità chì rende è aghjurnà stessu hè un prublema perchè quella entità ùn serà micca capaci di "sapè" ciò chì deve esse rende - una maghjina è un wireframe;
mancanza di un strumentu per scambià pruprietà è cumportamentu trà entità individuali (per esempiu, a pruprietà is_build_mode o rendering di cumpurtamentu). Puderia esse usà l'eredità, ancu s'ellu ùn ci hè micca manera propria di implementà in Rust. Ciò chì aghju veramente bisognu era u layout;
un strumentu per l'interazzione trà e entità era necessariu per assignà e persone à i tribunali;
l'entità stesse eranu una mistura di dati è di logica chì s'era prestu fora di cuntrollu.
Aghju fattu un pocu di ricerca è scupertu l'architettura ECS - Entity Component System, chì hè comunmente usatu in ghjochi. Eccu i benefici di ECS:
i dati sò separati da a logica;
cumpusizioni invece di l'eredità;
architettura centrata in i dati.
ECS hè carattarizatu da trè cuncetti basi:
entità - u tipu d'ughjettu chì l'identificatore si riferisce à (pò esse un ghjucatore, una bola, o qualcosa altru);
cumpunenti - entità sò custituiti da elli. Esempiu - cumpunenti di rendering, locu è altri. Quessi sò magazzini di dati;
sistemi - usanu tramindui l'uggetti è i cumpunenti, più cuntenenu cumportamentu è logica chì sò basati nantu à sti dati. Un esempiu hè un sistema di rendering chì itera per tutte e entità cù cumpunenti di rendering è face u rendering.
utilizendu u layout invece di l'eredità per urganizà entità sistemicamente;
sbarazzarsi di codice jumble attraversu sistemi di cuntrollu;
utilizendu metudi cum'è is_build_mode per mantene a logica wireframe in u stessu locu - in u sistema di rendering.
Questu hè ciò chì hè accadutu dopu l'implementazione di ECS.
risorse -> quì hè quì tutti l'assi sò (imaghjini)
src
- cumpunenti
—posizione.rs
- persona.rs
— tennis_court.rs
- pianu.rs
- wireframe.rs
- mouse_tracked.rs
- risorse
-mouse.rs
- sistemi
— rendering.rs
- custanti.rs
- utils.rs
- world_factory.rs -> funzioni di fabbrica mundiale
- main.rs -> loop principale
Assignemu persone à i tribunali
ECS hà facilitatu a vita. Avà aghju avutu un modu sistematicu per aghjunghje dati à l'entità è aghjunghje logica basatu annantu à quella dati. È questu, à u turnu, hà permessu di urganizà a distribuzione di e persone trà i tribunali.
Chì aghju fattu:
aghjustatu dati nantu à i tribunali assignati à Persona;
aghjustatu dati nantu à e persone distribuite à TennisCourt;
aghjustatu CourtChoosingSystem, chì permette di analizà e persone è i tribunali, detectà i tribunali dispunibuli è distribuisce i ghjucatori à elli;
aghjunghjia un PersonMovementSystem, chì cerca e persone assignate à i tribunali, è s'ellu ùn sò micca quì, allora manda a ghjente induve deve esse.
Riunione
Mi piace assai di travaglià in stu ghjocu simplice. Inoltre, sò felice chì aghju utilizatu Rust per scrive, perchè:
Rust vi dà ciò chì avete bisognu;
hà una documentazione eccellente, Rust hè abbastanza elegante;
a cunsistenza hè fresca;
ùn avete micca bisognu à a clonazione, copia o altre azzioni simili, chì aghju spessu fattu in C++;
L'opzioni sò assai faciuli d'utilizà è trattà l'errori assai bè;
s'è u prugettu hà pussutu esse cumpilatu, allura 99% di u tempu si travaglia, è esattamente cum'è deve. Pensu chì i missaghji di errore di u compilatore sò i migliori chì aghju vistu.
U sviluppu di u ghjocu in Rust hè appena principiatu. Ma ci hè digià una cumunità stabile è abbastanza grande chì travaglia per apre Rust à tutti. Per quessa, fighjulu l’avvene di a lingua cun ottimisimu, aspittendu i risultati di u nostru travagliu cumunu.