PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Flexible Speicherverwaltung im RPG-Maker / Stackprinzip



Corti
14.04.2008, 07:57
Flexible Speicherverwaltung im RPG-Maker / Stackprinzip

Frage: Worum gehts in diesem Topic?
Um die Übernahme/Anwendung des Stack-Pinzips in der Speicherverwaltung auf den RPG-Maker.
In einigen Aspekten ( Scriptcodestruktur & Speicheradressierung) hat der Maker starke Ähnlichkeiten mit Assembler,
so wird z.B. im Maker eine Varibale über ihre Nummer im Variablenblock angesprochen, ähnlich der direkten Speicheradressierung
im RAM-Baustein eines Microcontrollers.

Speicherverwaltung? Stackprinzip? Wozu?
Jede in irgend einem Script des Makers verwendete Variable benötigt einen Speicherplatz. Aufgrund der Architektur des Varibalenblocks
besetzen die Variablen einer Hilfsfunktion, die zB den Goldstand im Spiel als Pictures anzeigt, in diesem Fall die Varibalen zur Zerlegung in Einer-, Zehner- Hunderterstellen auch dann Speicherplatz wenn sie nicht benötigt werden.
Die hier genannten Hilfsvariablen werden nur zur Anzeige des Zahlenpictures benötigt, danach braucht man sie nicht mehr und bei der nächsten Verwendung des Call Events werden sie sowieso neu errechnet.

Warum also nicht die Variablen "freigeben" solange sie nicht benötigt werden, oder zumindest den Platz im Variablenblock mehrfach nutzen können.

Warum nicht einfach alle Hilfsvariablen an den selben Ort legen?
Stichworte: rekursiver Aufruf, Verschachtelung

Was machen wir in diesem Topic?
Ich demonstriere das Stackprinzip anhand eines simplen Beispiels, das in diesem Form in der Praxis, Sprich im Maker wunderbar funktioniert.
Das Beispiel ist simpel gehalten um verständlich zu sein. Wer es verstanden hat wird es auch für seine zwecke nutzen können.
Es ist keine "Nachklicken für Kinder" Anleitung, die Inhalte richten sich an Fortgeschrittene.

Das Beispielproject:
Wir haben eine Map & ein Männchen.
Wenn wir das Männchen ansprechen bekommen wir die Aufforderung nacheinander 2 Zahlen und eine Rechenoperation einzugeben.

Die Eingabe soll über ein CommonEvent erfolgen, dass vom Eventscript des Männchen aufgerufen wird.
Die Verarbeitung der 2 Zahlen und der Operation erfolgt in einem 2. CommonEvent.

Hier mal als Blockdiagramm:
http://www.imgimg.de/uploads/Fensterblock53ef1ab3jpg.jpg

Vorbereitung:
Wir reservieren 3 Variablen.

SP - der Stackpointer
SP_H - ein Hilfspointer, nötig weil der Maker keine PointerOffsets verrechnen kann
Tempsave - Zwischenspeicher, entspricht dem Akkumulator einer CPU

Was ist ein Stack/Stackpointer?
Der Stack bezeichnet einen Speicherbereich in dem Speicherplatz vergeben werden kann.
Der Stackpointer ist ein Zeiger auf die unterste Speicheradresse die besetzt ist, man kann also davon ausgehen, dass man den auf dem Platz unterhalb des Stackpointers befindlichen Speicher verwenden kann ohne etwas zu überschreiben.
Der Stackpointer muss zu anfang natürlich erst einmal positioniert werden.

http://www.imgimg.de/uploads/Sp0165f6a11cjpg.jpg
So sieht das dann im Variablenblock des Makers aus.

Das Script:
Dies ist der Scriptcodes des Rechenscripts. Ich habe die Abschnitte & Zeilen nummeriert und werden sie weiter unten kommentieren und mit Speicherabbildern illustrieren.
Wenn ich mich weiter unten auf den Scriptcode beziehe meine ich diese Grafik.
http://www.imgimg.de/uploads/Bild152b85b0e4jpg.jpg


Abschnitt 1:
Dieser Abschnitt besteht aus 3 identischen Einzelschritten.

//Einlesen Zahl1:
Der Stackpointer wird um 1 Feld nach unten verschoben indem man ihn +1 addiert.
Damit zeigt er auf einen leeren Variablenplatz.
Wir lesen die erste Zahl in den Zwischenspeicher ein und verschieben sie an die Stelle
auf die der Stackpointer gerade zeigt.
Diese Stelle ist nun der Speicherort für "Zahl 1" mit der wir nachher rechnen werden.

Dasselbe passiert mit den Eingaben für die Operation (1=Addieren , 2 & Rest = subtrahieren ) und die 2. Zahl.

Nachdem dies passiert ist bekommen wir folgendes Speicherabbild:
Unterhalb des Ursprungsortes des Stackpointers wurde Platz gemacht für unsere 3 Varibalen. Am Ende der Operation zeigt der Stackpointer
auf den letzten belegten Variablenplatz.
http://www.imgimg.de/uploads/Sp0249f4e5dfjpg.jpg

Abschnitt 2:
Nach dem Einlesen der 3 Variablen können diese nun verarbeitet werden, betrachten wir dazu also einmal das Common Event zur Verrechnung.

v_Rechnen(Z1,Z2,OP)

Ich benutze das Common Event hier wie eine Funktion in Assembler.
Die Funtion benötigt 3 Parameter und gibt einen Wert zurück, die Parameterübergabe und der Rückgabewert müssen allerdings per Hand" gemacht werden.
Ein Funktionsaufruf wird gemacht indem man oben auf dem Stack Platz für den Rückgabewert schafft und die Paramater direkt danach anordnet.
Das ist zwar keine "richtige" Parameterübergabe, aber irgendwie doch, denn innerhalb des aufgerufenen Commen Events kann man dann mit Sicherheit davon ausgehen, dass sich die benötigten Parameter auf bestimmten Plätzen relativ zur Position des Stackpointers befinden.
Die Compiler jeder Hochsprache wie C++ verfahren genau so wenn sie den C-Code in Assembler übersetzen.

Reservieren des Rückgabewertes:
http://www.imgimg.de/uploads/Sp03c715d96fjpg.jpg
Der Platz für den Rückgabewert wird reserviert.

Abschnitt 3:
Anschliessend werden die Inhalte der Parameter gelesen und in richtiger Reihenfolge auf dem Stack positioniert.
http://www.imgimg.de/uploads/Sp041c661980jpg.jpg

Warum kopiert man die Variablen nochmal? Die stehen doch direkt davor schon einmal!
Das mag sein, man kann auch die Funktion so umschreiben dass es mit dem schon positionierten Daten klappt, aber nicht immer liegen die Parameter in der selben Reihenfolge vor. Um also sicher zu gehen, dass egal in welchem Kontext das CE aufgerufen wird immer die parameter richtig übernommen, kopiert man sie noch einmal neu auf den Stack.

Abschnitt 4: CallEvent Rechnen!
Das CE wird aufgerufen. Innerhalb des "Rechnen"-CE wird der Stackpointer nochmals um einen Platz nach unten verschoben um Platz für das Rechenergebnis zu schaffen. Anschliessend wird die Rechenoperation durchgeführt, hierzu werden die Parameter über den Stackhilfspointer (SP_H) relativ zum Stackpointer angesprochen.
http://www.imgimg.de/uploads/Sp05e7092bcejpg.jpg
So sieht es dann im Speicher aus.

Abschnitt 5: Wert zurückgeben, Spuren beseitigen
Nachdem die Rechnung nun ausgeführt wurde speichert man den Inhalt des Speichrplatzes auf den Stackpointer gerade zeigt an den Ort für den Rückgabewert, in diesem Fall 4 Plätze oberhalb des Stackpointers, den wir durch Rechnen mit dem Hilfspointer erreichen.
Anschliessend wird der Stackpointer um 4 reduziert.
Damit geben wir den Speicherplatz der 3 Parameter und des Ergebnisses wieder frei
Das Commen Event "Rechnen" endet hier~
http://www.imgimg.de/uploads/Sp06ca110cf8jpg.jpg

Abschnitt 6: Ausgabe & Speicher freiräumen.
Nun da alles getan ist fehlt noch die Ausgabe in einer Textbox.
Dazu wird der Inhalt des Feldes auf das SP zeigt in Tempsave kopiert und dieses dann ausgegeben.
Anschliessend wird SP um 4 verringert, damit zeigt der Stackpointer wieder auf den Ursprungswert
http://www.imgimg.de/uploads/Sp08007bd855jpg.jpg

Hurray~ flexible Speichervergabe =D

Fazit:

Vorteile:
- Rekursive Aufrufe möglich
- weniger gleichzeitiger Speicherbedarf,effizientere Nutzung

Nachteile:
- Parallele Prozesse & Stack verträgt sich nicht da man innerhalb des Makerscript keine Interrupts besitzt, keine Rücksprungpunkte etc.
- Debugging wird schwieriger da man kein bleibendes Speicherabbild mehr hat und die Benennung der Variablen zwecks Übersicht fehlt, je nach Verzweigungstiefe der Aufrufe kanns schwer werden die Daten im F9-Modus überhaupt zu finden.

Das wars soweit

mfg~ der Corti

Kelven
14.04.2008, 08:33
Du hast die Einleitung gleich dreimal gepostet. \o Ich hab den Thread jetzt nur überflogen, aber mir ist nicht ganz klar wieso man nicht für jede neue Situation eine andere Hilfsvariable benutzen sollte. Soweit ich weiß wird der Speicherplatz für die 5000 Variablen sowieso immer reserviert, egal wieviele man davon tatsächlich benutzt (zumindest im XP ist es so). Bevor man sich die Arbeit macht einen Stack zu simulieren nimmt man einfach die nächste freie Variable, es sei denn man kommt wirklich an die 5000er Grenze. Schön, dass du gerade Assembler im Studium o.ä. hast, aber ich denke wenn man schon ein Programmierkonzept auf den Maker überträgt, dann eher das den Code möglichst einfach zu halten.

Rekursion ist btw. beim Maker auch so möglich. Events können sich selber aufrufen, was aber bei zu vielen gleichzeitig laufenden Events zu einer Fehlermeldung führt. Wirklich Sinn macht so was sowieso nur bei richtigen Funktionen.

Cherry
14.04.2008, 11:56
:A
Könnte von mir sein, die Idee. Finde ich toll, dass du dir die Arbeit machst, nur glaube ich, dass es wenige verwenden werden, da es umständlich, langsam und unübersichtlich ist (im Maker! Beim echten programmieren ist das natürlich was anderes.) Aber wie gesagt - wirklich eine tolle Idee!
@Kelven: der 2k3 reserviert nur soviel Speicher bis zur letzten Variable ungleich null.
mfG Cherry

Kelven
14.04.2008, 12:43
der 2k3 reserviert nur soviel Speicher bis zur letzten Variable ungleich null.

Du meinst sicherlich so was wie das nil von Ruby, oder nicht? Sonst würden die Variablen nach einer 0 ja ignoriert werden. Also bist du dir ganz sicher, dass nicht einfach nur ein Array mit 5000 Feldern bei Programmstart angelegt wird? Naja, ich kenne natürlich nicht den Quellcode vom Maker. Beim XP wird es wie gesagt so gehandhabt (unabhängig davon ist der Speicherplatzverbrauch der Variablen ja nicht wirklich ausschlaggebend).

Corti
14.04.2008, 13:58
Danke für den Hinweis Kelven, muss beim Kopieren aus der Backupdatei passiert sein.

Zur Speicherreservierung:
Die Variablenanzahl im 2k / 2k3 kann man beliebig weit runter drehen, ich gehe stark davon aus, dass durch eine Verkleinerung des Variablenblocks auch der nötige Speicher kleiner wird.
Dass es im XP anders ist wusste ich nicht da ich den XP niemals hatte.

Ob das wirklich so ist kann ich nicht sagen.
Ich würde das Prinzip selber nur dann benutzen wenn ich in irgend einem Vorgang mit Rekursiven Aufrufen Vorteile im Arbeitsaufwand dadurch hätte.

Im Grunde ist einzelne variablen Vergeben in Sachen Debugging viel angenehmer und in Sachen Performance ist es egal denn Maker auf aktuellem PC ist Gameboy Tetris auf dem Gamecube. In der Praxis wird sowas sowieso kaum einer nutzen. macht auch nix~ ist sowieso eher für Leute die gern mal mit sowas rumspielen.

Cherry
14.04.2008, 14:05
Ich als Programmierer des PP weiß genau, dass nur soviel Speicher alloziert wird bis zur letzten Variable, die auf einen Wert ungleich null gesetzt wurde. Wenn sie danach wieder auf 0 gesetzt wird, bleibt der Speicher aber, wie er ist. Daher dauert das erste Ändern eine Variable über diesem bereich auch länger. (Dies gilt jeweils für eine "Spielsitzung")

Daher muss man beim PP auch in einem Autostart-Event die letzte Variable auf -1 ändern.

Man kann dies leicht selbst probieren. Dazu schappt man sich ein Cheattool, macht ein leeres Projekt, setzt im TestPlay die Vari 1 auf 123456, sucht mit dem Cheattool danach und rechnet bei der Adresse, die das Cheattool ausspuckt, 1196 dazu (dann sollte man bei Variable 300 sein) und versucht, den dortigen Wert auszulesen. In den meisten Fällen wird er NICHT null sein, obwohl die Vari 300 eigentlich 0 ist. Ganz einfach deswegen, weil der Maker zu diesem Zeitpunkt nur Speicher für eine Variable alloziert hat.

mfG Cherry

PS: @Kelven: Ignoriert wird gar nix, da beim ändern eine Vari, für die noch kein Speicher alloziert wurde, der Variablenspeicher vergrößert und ggf. verschoben wird.

R.D.
14.04.2008, 16:26
Echt gut die Idee^^
Ich habs mir durchgelesen, und bin sicher das auch Anfänger dein, wie du es nennst, Stacksytem verstehen.
Es ist gut und einfach erkärt hab nix zu bemängeln, außer vllt das man das Script sicher auch kürzer schreiben könnte *aber jetzt nicht sicher bin erst testen muss* O____O''
Es so viel aus... irgendwie XD
Naja wie gesagt ich find gut^^

AN ALLE DIE DAS NOCH NCHT KENNEN:
Sowas müsst ihr drauf haben, wie Corti sagt lassen ich damit leichter Scripte bauen, man spart da enorm viel glaubt mir

mfg R.D.

Corti
14.04.2008, 17:07
Kürzer gehts wenn man die Kommentare weglässt.

Ein Großteil sind aber Hilfsoperationen die nötig sind weil keine Referenzoffsets möglich sind und keine Pre- und Post Inkremente / Dekremente.

Inhaltlich passieren da nur wenige Schritte.

R.D.
14.04.2008, 17:50
Ah~ Stimmt, daran kanns liegen^^

mfg R.D.

makenshi
14.04.2008, 21:58
@R.D.

Du hast nicht wirklich ne Ahnung was bspw. mit Pre- und Post Inkremente / Dekremente gemeint ist eh? Falls du es bestreiten willst: man liest es sehr deutlich heraus. Wie dem auch sei.

An sich solltest du auch keine Empfehlungen abgeben nach Marke:

AN ALLE DIE DAS NOCH NCHT KENNEN:
Sowas müsst ihr drauf haben, wie Corti sagt lassen ich damit leichter Scripte bauen, man spart da enorm viel glaubt mir

Damit verzapfst du nämlich ziemlichen Schwachsinn. Das Stackprinzip von Corti würde in einem "Makerscript" einfach nur zu einem Mehraufwand führen den man durch ein paar Hilfsvariablen ausräumen kann. Diese haben den Vorteil das man keinen solchen Stacksystem für die einzelnen Spielsystem anpassen muss sondern simpel sofort die benötigten Werte parat hat. Von der transparenz der Daten beim Debuggingprozess mal ganz abgesehen. :o
Beim (nicht vorhandenen) Debugger kann es ein echter Spaß sein in einem komplexen Skriptablauf nachzuverfolgen wohin die Daten nun wandern und wieder herkommen.

Es braucht also niemand so ein Stackprinzip. Ist es nun deswegen schlecht? Nö. Nur wie Corti schon selbst ganz richtig schreibt, ist eher eine Spielerei die leider auf so etwas wie dem Maker kaum sinnvoll umsetzbar ist. Also rate doch keinen Leuten das es sinnvoll ist ein Stackverfahren einzubauen obwohl du nicht weisst wozu das so gut sein könnte.

@Corti
Gute Ausführung. :) Wir hätten uns wohl doch kein Spielzeug aussuchen sollen hm? Naja, ist ja in umgewandelter Form bei uns in der Zauberverarbeitung drin. Also wirds sicherlich nicht ungenutzt verwelken.

Ist aber sicherlich für technisch interessierte gut geschrieben. Daumen hoch. :)

Dhan
15.04.2008, 12:11
Ich versteh immer noch nech den Vorteil gegenüber einer festen Anzahl an freien Variablen, die von allen möglichen Scriptteilen, die sich nicht überschneiden, benutzt werden dürfen, auch nicht aus Scriptfanatikerbetrachtungsweise (in meinem jetzigen Projekt benutze ich allerdings im CBS nen Stack für Zustände der Partymitglieder, ganz einfach weil ich dutzende Zustände auf verschiedener Intensitivität verwalten will (z.B. 1xGift Stufe 3 Dauer 2, 1xGift Stufe 4 Dauer 1 etc)... muss mal schauen, ob ich pro Zustand eine Vari mache und dann mod benutze oder mehrere mach um Laggs zu vermeiden, sprich wenn mans nicht für freie Variablen sondern zur Sammlung von Information auf einem Gebiet benutzt, passt das... ich hoff mal, der Aufrücken-Algorithmus wird nicht zu laggig bei der lahmen Interpretation des Makers)

R.D.
15.04.2008, 16:05
@R.D.

Du hast nicht wirklich ne Ahnung was bspw. mit Pre- und Post Inkremente / Dekremente gemeint ist eh? Falls du es bestreiten willst: man liest es sehr deutlich heraus. Wie dem auch sei.

schonmal mein Spiel im Maker angesehen o_O
Aber wirst recht haben...

Prä Inkrement = Wert einer variable sofort + 1
Post Inkrement = Wert einer Variablen wird nach erster Verwendung + 1
Inkrement allgemein = Erhöhung einer Variablen ( typsiches Beispiel: Zähler )
Dekrement allgemein = Verminderung einer Variablen
So hab ichs zumindest im Kopf...
Also ich weiß schon von was ich rede o.o ....
Edit: Und ja sachu mal in mein oder Lachsens Spiel, da wirst du sehen, das es sehrwohl einen wichtigen Zweck erfüllt..


mfg R.D.

Cherry
15.04.2008, 16:09
Prä Inkrement = Wert einer variable sofort + 1
Post Inkrement = Wert einer Variablen wird nach erster Verwendung +

Falls du es richtig meinst, hast du es dumm ausgedrückt.

Präinkrement = Variable wird VOR der Verwendung um 1 erhöht
Postinkrement = Variable wird NACH der Verwendeung um 1 erhöht

Bsp.: Um beim KOSMOS-Mikrocontroller-Bausatz auf den Arbeitsspechicher zuzugreifen, gibt es die Befehle "A = [B+]" und "[B+] = A". Ersterer speichert in Register A den Wert aus Adresse B, letzterer tut das Gegenteil. Bei beiden Fällen, wird B NACHHER um 1 erhöht, es handelt sich also um ein Postinkrement.

mfG Cherry

R.D.
15.04.2008, 16:16
Jabs, so hab ichs gemeint^^
Durch dich hab ichs je gelernt XD

mfg R.D.