In Spielen kommen ständig Objekte und gehen wieder, Schüsse, Partikel, Bruchstücke, manchmal hundert Stück gleichzeitig. In modernen Sprachen ist das new und free oder eine dynamische Liste. In MMBasic gibt es weder das eine noch das andere als praktikable Spiel-Lösung. Selbst wenn es das gäbe, wäre eine Allokation pro Frame zu teuer. Wie ich für mein Spiel ein Pool-Pattern aufgebaut habe, das in keinem Tutorial auftaucht, mir aber den Game-Loop rettet.

Object Pool

Mein Ansatz ist ein fester Pool: ich reserviere am Anfang die maximale Zahl Objekte, die je gleichzeitig leben sollen, einmal komplett. Dazu gibt es ein aactive-Flag pro Slot. Aktiv wird über das Flag gesetzt, nicht über das Erzeugen oder Zerstören.

CONST MAX_PARTICLES = 200

DIM FLOAT px(MAX_PARTICLES-1), py(MAX_PARTICLES-1)
DIM FLOAT pvx(MAX_PARTICLES-1), pvy(MAX_PARTICLES-1)
DIM INTEGER plife(MAX_PARTICLES-1), pactive(MAX_PARTICLES-1)

Beim Reset: MATH SET 0, pactive(). Damit sind alle Slots auf einen Schlag frei, ohne FOR-Schleife.

Einen freien Slot finden

Wenn ein neuer Schuss oder Partikel rein soll, suche ich den ersten freien Slot:

FUNCTION FindFreeSlot() AS INTEGER
  LOCAL INTEGER i
  FOR i = 0 TO MAX_PARTICLES - 1
    IF NOT pactive(i) THEN
      FindFreeSlot = i
      EXIT FUNCTION
    END IF
  NEXT i
  FindFreeSlot = -1
END FUNCTION

Liefert -1, wenn der Pool voll ist. Dann wird der neue Partikel halt verworfen, das fällt im Spielbild nicht auf.

Updaten heißt aktive Slots durchgehen

Im Game-Loop iteriere ich einfach über alle Slots und prüfe das Flag:

FOR i = 0 TO MAX_PARTICLES - 1
  IF pactive(i) THEN
    INC px(i), pvx(i)
    INC py(i), pvy(i)
    INC plife(i), -1
    IF plife(i) <= 0 THEN
      pactive(i) = 0
    ELSE
      PIXEL px(i), py(i), COL_WHITE
    END IF
  END IF
NEXT i

Beim Auslaufen wird pactive einfach wieder 0, der Slot ist frei und kann beim nächsten Aufruf von FindFreeSlot wiederverwendet werden.

Der Speicherverbrauch ist konstant, weil der Pool von Anfang an seine volle Größe hat — keine Überraschungen mitten im Spiel. Pro Frame fällt keine Allokation an, das Setzen eines Flags plus ein paar Werte ist billig genug für eine 60-fps-Schleife. Und das aactive-Schema überträgt sich auf jede Pool-Art: bei mir laufen Asteroiden, Schüsse, UFO-Schüsse und Partikel jeweils auf eigenen Pools mit derselben Logik. Für kurzlebige Spielobjekte in MMBasic ist das Pool-Pattern für mich nicht eine von vielen Optionen, sondern faktisch die einzig sinnvolle.