Jag har konverterat ett spel, Infinite Bunner, skrivet med spelramverket PyGame Zero i Python till spelramverket Macroquad i programmeringsspråket Rust. Detta som en del av projektet Rust Game Ports från Rust gamedev working group. Projektet innehåller ett flertal spel skrivna i olika spelramverk i Rust som har konverterats från andra språk eller ramverk. Det är tänkt att kunna användas som referens för hur spel kan se ut i olika spelramverk i Rust.
Jag blev intresserad av just Infinite Bunner när jag läste boken Code the Classics vol. 1 från Wireframe Magazine. Boken innehåller historien om fem klassiska arkadspel och fulla källkoden för moderna versioner av spelen. Infinite Bunner är baserat på den gamla klassikern Frogger där spelaren styr en groda över farliga bilvägar och floder för att ta sig hem till sitt bo. I den här moderniserade versionen får den stackars grodan fortsätta i all evighet.
Konverteringen till Rust
En av de större utmaningarna med konverteringen var att undvika globala objekt, delade muterbara referenser och cirkulära referenser. Det fanns också en del dynamisk programmering, till exempel där boolean-variabler hanteras som siffror och multiplicering av en lista med en boolean för att få samma lista eller en tom lista. Detta gör att Rust-koden blir mer omfattande, men det kan också vara enklare att förstå då den är mer beskrivande.
I PyGame Zero är uppdateringsfrekvensen låst till 60 uppdateringar per sekund och alla värden för hur långt alla bilder ska förflyttas baseras på det. I Macroquad är uppdateringsfrekvensen mycket snabbare om grafikdrivaren inte är låst till skärmfrekvensen. Därför brukar tiden sen senaste uppdateringen användas för att bestämma hur långt allt flyttas på skärmen. För att inte behöva anpassa alla dessa värden i hela koden valde jag istället att skriva kod som låste Macroquads uppdateringsfrekvens till 60 FPS. Detta skapade i sin tur ett problem med inläsningen av tangenttryckningar, som måste göras vid varje riktig uppdatering. För att lösa det krävdes det kod som sparade knapptryckningarna mellan varje frame.
Förutom det var det en ganska rak konvertering, när jag väl lyckats följa alla arv i klasserna och se vad dom egentligen gjorde. Klasserna kunde utan några större problem konverteras till structs i Rust. Det finns inte polymorfism genom arv i Rust så jag använde en enum för att matcha mellan dom olika objekten.
Vill du spela?
Du kan ladda ner versioner av spelet till Windows, Mac och Linux från github-sidan för spelet, där du även hittar källkoden.