PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ressourcenfreigabe



Kelven
26.06.2007, 13:50
Mal angenommen ich benutze für ein Menü eine globale Variables, nennen wir sie $menü. Je nachdem welches Menü aktiv ist, wird die eine andere Klasse zugewiesen und das Objekt benutzt ein Sprite zur Anzeige von allen möglichen Werten. Was wäre nun am praktischten, wenn das Menü verlassen wird? $menü=nil setzen, nur das Sprite disposen oder gar nichts machen außer die Anzeige löschen?

MagicMagor
26.06.2007, 15:35
Das hängt davon ab wie der Menüwechsel von statten geht.

Wann immer ein Objekt durch keine Variable mehr angesprochen werden kann, wird das Objekt vom Garbage Collector "markiert", nach einiger Zeit wird es dann vom GC freigegeben. Generell sollte man das Freigeben also immer dem GC überlassen, man hat auch sonst keine explizite Möglichkeit dazu.
Eine Ausnahme besteht aber bei Sprite- und Bitmap-Objekten. Solange diese Objekte noch im Speicher sind, sind sie sichtbar. Da das objekt bei der sogenannten Dereferenzierung (also alle referenzen von variablen auf das objekt verschwinden) vom GC nicht direkt gelöscht sondern nur "markiert" wird, bleiben diese Objekte noch kurze Zeit sichtbar, bis der GC sie wegräumt. Um diese objekte direkt mit einem Aufruf freizugeben gibt es die Methode "dispose".
Alle Sprite-objekte und alle selbst erstellten Bitmap-objekte (über Bitmap.new) sollte man also vor dem dereferenzieren disposen. (Anmerkung: Bitmaps die über RPG::Cache eingeladen werden sollten NICHT disposet werden, da sie sonst aus dem Cache verschwinden)

So, nach dieser grundsätzlichen Erklärung mal zu deinem Problem. Wenn du deine globale variable $menu (umlaute sollte man nicht verwenden, falls sie überhaupt funktionieren) auf nil setzt, entfernst du die Referenz auf dein Menü-objekt und damit auch alle Referenzen die dieses objekt auf andere objekte hatte (zB die Sprite-objekte die zum anzeigen benutzt wurden).
Die Scene-objekte funktionieren nach dem Prinzip, das die Objekte in ihrer main-schleife überprüfen ob sie selbst die aktuelle scene sind (if $scene == self) und wenn nicht, brechen sie die Schleife ab und disposen alle Sprites die sie benutzen.
Wenn dein Menüobjekt nicht in einer schleife selbst abläuft, wie es bei den scenes der fall ist, solltest du eine eigene Methode zum Aufräumen schreiben, in der du alle sprites die du benutzt disposet.


class MenuTest
def dispose
@sprite.dispose
# Weitere sprites disposen..
end
end

Dann musst du nur bevor du die Referenz auf das objekt entfernst, $menu.dispose zum Aufräumen aufrufen.

Das Menü zu verlassen sähe dann so aus:


$menu.dispose
$menu = nil


Eine kleine Anmerkung noch. Du meintest "dem werden dann je nachdem unterschiedliche Klassen zugeordnet", ich vermute du meinst damit einfach so etwas wie es bei der Scene abläuft.


# Item-Menü
$menu = MenuItem.new
# Skill-Menü
$menu = MenuSkill.new
# etc...

Hierbei erstellst du jeweils ein Objekt (also eine Instanz einer Klasse) und speicherst diese in der globalen Variable ab. Wichtig hierbei ist, das dabei die Referenz auf das alte Objekt, was vorher in $menu gespeichert war, verloren geht. Das objekt selber existiert aber noch im Speicher, samt der von ihm erstellten Sprites. Korrekterweise müsste du dann auch vor jedem Wechsel das objekt sich selbst "aufräumen" lassen.


self.dispose
# Jetzt wechseln
$menu = MenuItem.new


Zusätzlich sollte man bei einem Menüwechsel aber noch beachten, daß die Erstellung neuer Sprites je nachdem einige Zeit beanspruchen kann. Damit es durch solche unkalkulierbaren Verzögerung keinen hakeligen Aufbau des neuen Menüs gibt, kann man den Screen "freezen", was verhindert, daß irgendwelche Zeichenoperationen auf dem Monitor ausgegeben werden, bis der Bildschirm wieder freigegeben wird.
Mittels "Graphics.freeze" kann man den Bildschirm sperren, mittels "Graphics.transition(time)" kriegt man einen weichen Übergang vom gesperrtem Bild auf das aktuelle Bild, wobei der Bildschirm dadurch auch entsperrt wird. time gibt die Zeit des Fadens in Frames an, standardwert hier ist 20.

Nach all der Info über die Möglichkeiten, hier aber mal meine Empfehlung wie man Menü-wechsel mMn sehr elegant und mit wenig Aufwand löst. Wir schreiben uns dafür eine methode namens "menuChange", dabei packen wir diese explizit ins Modul "Menu" um Namenskonflikte mit anderen Skripten zu vermeiden.
Zu welchem Menü wir wechseln wollen geben wir dabei durch eine Zahl an, zwecks besserer Übersicht verstecken wir diese aber hinter Konstanten, die wir ebenso ins Modul "Menu" packen.



module Menu

# Hier definieren wir die Konstanten, sie müssen mit einem großbuchstaben anfangen. Aus Übersichtsgründen und um sie von Klassen- und Modulnamen zu unterscheiden schreibe ich sie immer komplett groß.
MENU_ITEM = 1
MENU_SKILL = 2
MENU_EQUIP = 3
MENU_NONE = 4 # Zum Verlassen des Menüs

# Jetzt müssen sie nur noch auch von aussen lesbar sein
attr_reader :MENU_ITEM, :MENU_SKILL, :MENU_EQUIP, :MENU_NONE

def Menu.menuChange(menuID)
# Wir bereiten den Bildschirm auf den Wechsel vor und räumen das alte Menü-objekt auf.
Graphics.freeze
$menu.dispose
# Je nachdem welchen Wert menuID hat, wechseln wir auf das Menü indem wir ein neues Menüobjekt erstellen und in unserer globalen Variable abgeben.
case menuID
when MENU_ITEM
$menu = MenuItem.new
when MENU_SKILL
$menu = MenuSkill.new
when MENU_EQUIP
$menu = MenuEquip.new
# Hier kann man nach dem schema jetzt beliebig viele weitere Menüs einfügen. Die Reinfolge in der diese when-klauseln hier stehen spielt dabei keine Rolle
# Der default-Wert wird ausgeführt, wenn die geprüfte Variable (menuID) einen Wert aufweist, für den es keine when-Bedingung gibt. Daher muss diese Klausel ans ende der case-Anweisung.
else
# Wir springen in diesem Fall zurück zur Map
$menu = nil
$scene = Scene_Map.new
end # Ende der case-Anweisung
# Das neue Menü-objekt hat sich jetzt selbst initialisiert und dabei alle Sprite und Bitmap-objekte erstellt, die es beim Betreten des Menüs anzeigen will (dazu muss das alles nur in die initialize-funktion der Menü-Klasse), daher wechseln wir jetzt mit einem weichen Fade ins neue Bild
Graphics.transition(20)
end
end


Der Wechsel auf das Item-Menü würde jetzt zB einfach so aussehen:


Menu.menuChange(Menu::MENU_ITEM)

Das schöne ist, sollte man neue Untermenüs einfügen braucht man nur eine neue Konstante erstellen und eine weitere when-Bedingung in obiger Funktion einbauen. Ebenso kann man den Übergang für alle Menü-wechsel an einer stelle ändern.
Ich hoffe es war nicht zuviel Text und ein wenig verständlich, ansonsten kannst du gerne noch einmal nachfragen =).

Kelven
26.06.2007, 16:10
Danke für die ausführliche Erklärung. Bisher hab ich das bei mir auch so gemacht; also eine eigene dispose-Methode geschrieben, die das Sprite entfernt. Die globale Variable habe ich andererseits danach nicht auf nil gesetzt.

Dein Menüwechsel-Modul ist interessant, aber ich hab das Menü bei mir etwas anderes realisiert. Als eine Art Hybridlösung. Alles Interaktive läuft per Eventkommandos und die Anzeige per Ruby (da hat die Faulheit bei mir wieder gesiegt). Ich benutze btw. nur ein Sprite für die Anzeige von Text, da gab es bis jetzt noch keine Probleme mit hackeligem Bildaufbau. Aber gut zu wissen, wofür Freeze und Transition gedacht sind, ich hab mich schon immer bei den Eventkommandos gewundert.