donderdag 17 september 2009

Metaprogramming spel eenheden

In mijn eerste prototype waarbij ik 'echt' wilde programmeren (dus serverside, geen JS display prototype). Liep ik tegen een aantal problemen aan:

1. Ik heb geen uitgebreide ervaringen met het maken van spellen
2. Je moet items hebben die met elkaar interactief kunnen zijn.

bij punt 2 moet je denken aan soldaatjes die kunnen rondlopen en schieten. Hoe hard lopen die? hoe krachtig en ver schieten die? Dit is meta data die geld voor alle soldaatjes (upgrades daargelaten).

Ga ik die in een database opslaan en er voor querien? Ga ik die in XML of yaml definieren en inlezen bij het opstarten?
Nadelen bij deze approaches zijn dat het niet alleen om getalwaarden ofzo gaat. Als je echt eenheden wil definieren, dan hebben die ook verschillende vaardigheden. Zo kan een helicopter vliegen, raketten afvuren en bommen gooien, een buggy kan wellicht mijnen leggen en mannetjes vervoeren. Dit zijn vaardigheden, die toch een soort uniek zijn per soort eenheid. Als je dat allemaal in XML moet omschrijven is punt 1, maar bij het uitlezen moet je ook een object hebben programmatisch in je systeem die hiermee om kan gaan.

De oplossing: Metaprogramming.

Stel ik zou gewoon een Marine klasse maken? en daarin methodes toevoegen voor zijn vaardigheden? en daarin vertellen wat zijn stats (health, armor, range, speed) zijn?

Dankzij Single Table Inheritance in Rails is dit verdomd makkelijk. Definieer een standaard eenheid. Ik heb deze 'Asset' genoemd. Alles in het spel wat op het speelbord zich bevind is een asset. Dit kan een boom zijn, een gebouw, maar ook meer interactief zoals een soldaat.

Van deze standaard Asset maak ik overervingen. Omdat je in Ruby mooi op klasse en instantie niveau methodes kan definieren, heb ik in de asset basis een klasse methode waarin ik een willekeurig aantal eigenschappen kan definieren van de assets.

class Asset::Base < ActiveRecord::Base
 info \
  :build_time,   # building info
  :build_costs,
  # movement info
  :movement_speed # etc.. meer leuke stats dingetjes
end
In een Marine overgeërfde klasse kan ik vervolgens standaard waarde voor deze attributen opgeven:
class Terran::Unit::Marine < Asset::Base
  # building info
  build_time 10.seconds
  build_costs 75.metal
  # movement
  movement_speed 30.kmph

end
Zoals je ziet zijn in de bassis via kleine opsomming wat attributen verzonnen, en via de klasse van de specifieke unit kan je ze dan "inkleuring" geven. Zo gaat een marine klasse omschrijving eruit zien als een magic the gathering of pokemon (wat je wil) speelkaartje :-) Dit mooie mechanisme heb ik uit "Why's (poignant) guid to Ruby" Echter is deze site nu down, maar met googlen kom je er ook. Dit stuk wordt beschreven in hoofdstuk 6.3 "A Sponsored Dragon-Slaying"
Voordelen van deze approach:

1. Een marine klasse beschrijft een marine in een aantal regels, en is zeer leesbare code
2. Een instantie van deze klasse is ook echt een marine op het speelveld met zijn eigen vaardigheden
3. Door STI (Single Table Inheritance) is elke Asset type direct in de database op te slaan
4. Validatie model van active record is te gebruiken voor minimale eisen en uit te breiden met eigen validatie soorten. Bijv: Om een marine te maken hebben we barakken nodig.

Geen opmerkingen: