Das zeigt in welcher Reihenfolge die Methoden aufgerufen werden. Die Schreibweise Klasse#methode bedeutet: die Methode ist eine Instanzmethode der jeweiligen Klasse. Die Schreibweise Klasse.methode bedeutet: Die Methode ist eine Klassenmethode (kann also direkt aufgerufen werden, wie SceneManager.call).
Wenn du eine eigene Scene-Klasse anlegst, die von Scene_Base erbt, kannst du Methoden überschreiben. Das heißt: Schreibst du in deiner Klasse eine update Methode, so wird diese anstelle der update Methode in Scene_Base aufgerufen. Die Methoden in Scene_Base sind aber wichtig, sie sorgen zum Beispiel dafür das Tastatureingaben abgefragt und der Bildschirm neu gezeichnet wird. Aus dem Grund musst du, wann immer du eine Methode von Scene_Base überschreibst, darin super aufrufen. super bedeutet: rufe die Methode der Superklasse (also Scene_Base) auf.
Beispiel: Du schreibst in deiner Memory Klasse eine update Methode mit dem Inhalt:
Dann passiert folgendes:
1. Zuerst wird Memory#mache_dies aufgerufen
2. Danach wird Scene_Base#update aufgerufen.
3. Danach wird Memory#mache_jenes aufgerufen
Wenn du nun in Scene_Base#update schaust, siehst du, dass dort update_basic aufgerufen wird (diese Methode zeichnet den Bildschirm neu und nimmt Tastatureingaben entgegen). Du musst also update_basic nicht selbst aufrufen, wenn du mit super bereits die update Methode der Superklasse aufgerufen hast.
Zitat
Diese Meldung taucht sofort beim Starten des Spiels auf. Hab das extra noch mal in ein neues Projekt gepackt um zu schauen ob es mit anderen Scripten Reibereien gibt.
Aber anscheinend mag er da was gar nicht...
...
Der entscheidende Teil der Fehlermeldung ist for nil:NilClass. Das bedeutet soviel wie: Die Variable, mit der du each_with_index aufrufst, ist leer.
In meinem Beispiel wäre das die Variable @card_sprites. Dieser Variable wurde noch kein Wert zugewiesen, zu dem Zeitpunkt wo each_with_index aufgerufen wird.
In meinem Beispiel wird die Variable @card_sprites in der Methode initialize_graphics angelegt. Offenbar rufst du diese Methode nicht auf. Sie sollte innerhalb von start aufgerufen werden.
Lege also eine Methode Memory#start an, rufe dort mit super die gleichnamige Methode von Scene_Base auf und danach die Methode initialize_graphics.
Zitat
Dwas jedoch verstehe ich nicht, soweit ich mir "each_with_index" angeschaut habe, ist es genau für diesen Fall gedacht. Einen hash aus einem Objekt und seinem Index zu bilden. http://apidock.com/ruby/Enumerable/each_with_index
Habe ich doch bei den "leichten" Schritten was mal wieder falsch verstanden?
...
Mit each_with_index kannst du über eine Collection (z.B. einen Array) iterieren (also eine Schleife bauen), wobei du in jedem Schritt sowohl das jeweilige Element als auch dessen Position/Index bekommst.
Erneut muss ich mit Kleinigkeiten nerven... langsam tut es mir echt leid...
Ich habe nun folgende Methoden angelegt:
start ist gleich die erste Methode und ich habe sie am Ende von "initialize" aufgerufen, da "initialize" ja nun mal immer zuerst startet.
Nun habe ich das grandiose Problem, dass sich folgende Fehlermeldung kommt.
Zitat
Script 'Game_Interpreter' line 1411: NoMethodError occurred.
super: no superclass method `start' for #<Game_Memory:0xe80c074>
...
Habe auch aus Frust versucht das in eine Klasse zu stecken, dass sah dann so aus:
Gab dann aber auch nur eine Fehlermeldung:
Zitat
Script 'Game_Interpreter' line 1411: NameError occurred.
undefined local variable or method `start' for
#<Game_Memory:0xe294c7c>
...
Ich kann mir das gerade nur so erklären, dass ich a) beim Aufruf von start was falsch mache oder die Bedingungen für start nicht stimmen.
Bin auch nebenher dabei mir das über objekt orientierte Programmierung durchzulesen (weiß ich eigentlich alles... aber ich kann auch alles falsch verstanden haben.)
Irgendwo in meinen Gedankengängen scheint echt der Wurm drin zu sein...
Der erste Fehler kommt daher, dass super in diesem Fall versucht, die Methode start von der übergeordneten Klasse aufzurufen, die hier anscheinend Game_Memory ist. Wenn ich alles richtig verstanden hab, soll aber deine Klasse Memory von Scene_Base erben, also ist class Memory < Scene_Base eigentlich richtig. Der zweite Fehler kommt dann her, dass jemand anderes trotzdem noch versucht, start vom Game_Memory aufzurufen.
start ist gleich die erste Methode und ich habe sie am Ende von "initialize" aufgerufen, da "initialize" ja nun mal immer zuerst startet.
...
"start" - und auch alle anderen Methoden, die am Ablauf der Szene beteiligt sind, also "update", "terminate", "perform_transition" etc. - müssen von dir gar nicht selbst aufgerufen werden, darum kümmert sich der SceneManager eigentständig, wenn du ihm mittels SceneManager.call oder SceneManager.goto sagst, dass jetzt ein Szenenwechsel stattfinden soll.
--
"Banjo, you're a BEAR... and I will teach you... THESE MOVES!"
Das ist ja echt ärgerlich. Hab irgendwie noch nicht gerafft wie man Bilder an den eigenen Post anhängt. Hab ein Diagramm hochgeladen, aber das wurde danach prompt von der Forensoftware gelöscht.
Egal, dann eben in Textform:
Was ich mit dem Diagramm eigentlich zeigen wollte, ist welche Methoden in welcher Reihenfolge aufgerufen werden. Das ist in einem fertigen Spiel nicht immer so leicht herauszufinden und erschwert natürlich die Fehlersuche, wenn dir gar nicht klar ist, warum welche Methode wann aufgerufen wird.
Das ganze Spiel beginnt im "Main" Script mit der Zeile
rgss_main ist eine Funktion der RGSS die sagt: Führe den Codeblock einmal aus. Drückt der Spieler dabei die F12 Taste, wird der Codeblock von neuem ausgeführt (ergo, das Spiel neugestartet). Ist also erstmal nicht so wichtig. Wichtig ist: SceneManager.run wird ausgeführt.
Als nächstes guckst du also was SceneManager.run macht.
Okay, SceneManager.run ruft zuerst DataManager.init auf. Die Definition dazu findest du im Script "DataManager". Kurz zusammengefasst: DataManager.init liest die Datenbank des Makers ein (die Datenbank sind alle Einstellungsmöglichkeiten die du im Maker hast, wenn du F9 drückst, z.B. Helden, Monster, Items etc.). Danach werden alle Game-Objekte angelegt. Kleiner Exkurs: Alle Game-Objekte haben einen Klassennamen der mit Game_ anfängt. Sie enthalten den aktuellen Zustand des Spiels. Du kannst dir leicht merken: Alles was in einen Spielstand abgespeichert werden muss (was beim Neuladen also wieder verfügbar sein muss) ist Teil eines Game-Objektes. Denn nur Game-Objekte werden gespeichert. Dem gegenüber gibt es Daten-Objekte. Daten-Objekte fangen alle mit RPG:: an, z.B. RPG::Actor. Sie enthalten die statischen, unveränderlichen Inhalte, die du im Maker definiert hast. Sie müssen nicht im Spielstand abgespeichert werden, weil sie konstant für alle Spielstände sind. Kleines Beispiel: RPG::Actor enthält für jedes Level die zugehörigen Lebenspunkte. Game_Actor enthält die tatsächlichen Lebenspunkte des Helden, mit Inbegriffen also auch Lebenspunkte die man durch Trinken eines Zaubertranks o.ä. erhält. RPG::Actor enthält den Namen des Helden in der Datenbank. Game_Actor enthält den tatsächlichen Namen des Heldens (der sich im Spiel z.B. durch Umbenennung ändern kann). Darum macht es Sinn für dein Minispiel eine Klasse Game_Memory anzulegen, die die veränderlichen Zustände deines Memory-Spiels enthält (welche Karten liegen auf dem Tisch, welche Karten wurden gezogen, wie viele Punkte hat der Spieler usw.).
Zurück zum Spielfluss: Nach dem Aufruf von DataManager.init wird mit
die erste Scene angelegt und in die Variable @scene gespeichert. first_scene_class ist eine Methode, die die Klasse zurückgibt mit deren Scene das Spiel beginnen soll (z.B. Scene_Title). @scene ist eine Variable, welche die aktuelle Scene des Spiels beinhaltet.
Danach kommt
Achtung: Das ist eine Schleife. Die Methode @scene.main wird jetzt solange ausgeführt solange die Variable @scene nicht nil ist. An diesem Punkt befinden wir uns in der Hauptschleife des Spiels. Jetzt wird immer @scene.main aufgerufen, bis das Spiel beendet wird. Das Beenden des Spiels geschieht durch den Aufruf von SceneManager.exit. Wenn du in die Methodendefinition guckst, siehst du, dass diese Methode einfach die Variable @scene auf nil setzt (und damit die Schleifenbedingung verletzt, wodurch die Schleife abbricht).
Jede Scene-Klasse im Spiel erbt von Scene_Base. Wenn du wissen willst was @scene.main macht schaust du also am besten erstmal dort hinein:
Zuerst wird Scene_Base#start aufgerufen. Danach Scene_Base#post_start. Dann kommt wieder eine Schleife: Solange die Hauptscene nicht geändert wird (du also nicht SceneManager.call oder SceneManager.exit aufrufst) wird die Methode Scene_Base#update aufgerufen. Wird die Scene beendet, so werden pre_terminate und terminate_aufgerufen.
Was hat es mit dem start und post_start bzw. pre_terminate und terminate auf sich? Hier geht es darum einen Übergang zwischen zwei Scenen zu erzeugen. Wenn du z.B. von der Map ins Menü wechselst, so geht das nicht abrupt, sondern die Map wird langsam ausgeblendet und das Menü wird langsam eingeblendet. start zeichnet den neuen Bildschirm der neuen Scene. post_start erzeugt den Übergang. Wenn du post_start überschreibst und eigenen Code einfügst, so wird dieser erst ausgeführt, nachdem die Scene eingeblendet wurde. Alles was in start steht wird ausgeführt, bevor die Scene eingeblendet wird. Dasselbe gilt für pre_terminate und terminate. In terminate wird der Bildschirm eingrefroren, so dass die nächste Scene gezeichnet werden kann. Alles was in pre_terminate reinkommt, wird noch sichtbar für den Spieler bevor die Scene ausgeblendet wird.
In der Regel brauchst du post_start und pre_terminate nicht.
Zuletzt noch update: update ruft erstmal nur update_basic auf. update_basic zeichnet den Bildschirm neu und fragt Tastatureingaben ab. update_basic brauchst du nicht überschreiben. In der Regel überschreibst du update und rufst als allererstes super auf um Scene_Base#update aufrufen (die dann update_basic aufruft).
Nochmal zusammengefasst:
1. Spiel startet mit SceneManager.run
2. Dieser ruft in einer Schleife immer wieder @scene.main auf
3. Diese führt start aus um sich zu initialisieren
4. und danach update in einer Schleife (solange die Scene existiert) auf
5. wird die Scene beendet ruft sie vorher noch terminate auf
Schreibst du eine eigene Scene so überschreibst du 3 Methoden: start, update und terminate. Alle drei überschriebenen Methoden sollten als allerersten Aufruf ein super enthalten, um die gleichnamige Methode der Superklasse aufzurufen. Eine Scene sollte den Ablauf eines Scriptes koordinieren. Sie regelt im Grunde genommen drei Elemente: Das Zeichnen aller Grafiken, Abspielen von Soundeffekten/Musik und das Abfragen von Tastatureingaben. Wird der grafische Teil sehr komplex, lagert man ihn in der Regel nochmal in eine eigene Klasse aus.
Die Spieldaten und Spielmechanik schreibt man in eine eigene Klasse. In der Regel nennt man sie Game_XYZ, z.B. Game_Memory. Hier kommt alles rein was einen Zustand deines Scriptes darstellt, der in einem Spielstand abgespeichert werden muss. In einem Memory-Spiel wären das: die Karten auf dem Spielbrett, die Karten auf der Hand des Spielers. Eine Instanz dieser Klasse packt man dann in eine bestehende Game_XYZ Klasse, z.B. Game_System. Danach kann man darauf z.B. mit $game_system.memory zugreifen.
Ich weiß nicht wie dein Code gegenwärtig aussieht. Aber ich vermute mal das dein Problem noch darin liegt zu erkennen, welcher Code in Game_Memory und welcher in Scene_Memory gehört (du nennst letztere Memory, aber es ist besser eine Scene Klasse mit dem Prefix Scene_ anfangen zu lassen).
Ein wenig entwirren konnte ich bis jetzt das Ganze.... ok ein ganz klein wenig.
Ich hatte noch den alten Code wo stehen, so dass ich Klassen doppelt hatte, womit das Script versucht hat die Klasse Memory aus meinem alten Code zu starten.
Beeindruckender Weise hat das irgendwann funktioniert wodurch ich diesen Fehler erst realisiert habe o_o
Mittlerweile habe die Klasse auch unbenannt...
Nun nachdem ich diesen Fehler weg habe, hänge ich wieder.
Und zwar hänge ich am Methodenaufruf.
Beispiel:
Wenn ich nun in der Klasse Game_Memory, in der Methode initialize am Ende folgenden Befehl aufrufe "SceneManager.call(Scene_Memory.start)" wird ja aus der Klasse "Scene_Memory" die Methode "start" aufgerufen. Ja... und hier komme ich zu meinem Knackpunkt. Bei A) gilt die Methode als nicht initialisiert, bei B) kann ich kein super darauf anwenden, da nun start von Scene_Memory erbt. Dafür komme ich in die Methode rein, nützt mir nur nicht viel, da nun die Methode "get_cards" die in "initialize_graphics" aufgerufen wird, nicht initialisiert ist oder leer ist.
Ich habe in der Methode initialize schon mit verschiedensten Aufrufen versucht an die Methode start zu kommen. Unter anderem "SceneManager.call(Scene_Memory.start.new)" was auch geht... nur eben mit den selben Problemen. Auch ein einfacher Aufruf aller "start" oder "Scene_Memory.start" bringt mich in die selbe Lage.
Mein Versuch einfach "SceneManager.call(Scene_Memory)" aufzurufen ... lief darauf hinaus, dass er nicht in die Methode rein ging... habe auch nichts anderes erwartet.
Ich poste hier einfach noch mal Alles rein, großartige Änderungen zum Code von -KD- gibt es noch nicht, ich will erstmal das es so weit läuft. Klar... im Nachhinein andere Sachen einbauen ist nie die klügste Idee... aber wenn es nicht läuft ist es auch doof groß dran herumzuschrauben.
Bei dieser Variante des Codes handelt es sich um jene, wo er keine Daten in get_cards findet.
Die Fehlermeldung lautet um genau zu sein:
Zitat
Script 'Game_Interpreter' line 1144: NoMethodError occurred.
undefined method `get_cards' for nil:NilClass
...
Ergo wurde wohl wieder etwas noch nicht aufgerufen, wodurch ich noch nicht an die Daten komme.
Der Methode zufolge sollte es sich hierbei ja um get_card und den index davon handeln.
Jedoch kann das so wie es da ist, sowieso noch nicht stimmen, immerhin kann ich kein "super" ausführen.
Wie merkt meine Klasse, dass die Methode da ist, ohne das ich self.start schreiben muss?
Das würde dann hoffentlich auch die Folgeprobleme beheben...
Wenn du self.xxx bei einer Methodendefinition schreibst, wird die Methode zur Klassenmethode (du kannst sie also über Klassenname.methodenname aufrufen). Klassenmethoden können ihrerseits keine Instanzmethoden aufrufen, sie beziehen sich nur auf die Klasse. Klassenmethoden braucht man eher selten. In deinem Fall brauchst du sie jedenfalls gar nicht. Also weg mit den self.
Zitat
...
Dieser Code macht was völlig anderes als du wahrscheinlich vermutest. Instanzvariablen, die du in den Klassenblock schreibst, werden zu Klassenvariablen (die du dann innerhalb einer Klassenmethode nutzen kannst). Das ist eigentlich NIE das was du willst. Daher gleich abgewöhnen! Instanzvariablen schreibst du in die Initialize-Methode rein.
Zitat
...
Das hat nichts in der initialize Methode zu suchen. Wenn du deine Memory-Szene starten willst, mache das entweder über ein Event im Spiel (mit dem Call Script Befehl). Oder, wenn du willst das das Spiel direkt mit dem Memory-Spiel beginnt, du schreibst
Das Problem warum dein Code nicht funktioniert ist, dass die Methoden initialize_graphics, update_graphics und dispose_graphics nicht in der Scene_Memory Klasse stehen. Du hast sie einfach lose in den Scripteditor eingefügt.
Entschudige bitte -KD- das ich hier so lange geschwiegen habe ^^" Mein Rechner hat den Geist aufgegeben... teilweise -.-
Danke so weit für deine großartige Hilfe ^^
Ich hab mit hier noch mal das Meiste durchgelesen um mit meinem jetzigen Problem klar zu kommen.
Habe brav die Methoden in die Klasse ingepflegt und "start" ganz nach unten gepackt. Mir Videos zu Programmierung mit Ruby im Verbindung mit dem VX Ace angeschaut (speziell zu Verbung) und aaaaah ... ich glaube ich scheitere am Aufruf.
Ist ja der Code mit dem ich über Event -> Script halt Game_Memory starte.
So und mit: sollte ich rein theoretisch doch Scene_Memory starten können oder?
Also im Prinziep sollte es insgesammt wie folgt aussehen.
In einem Event mit dem Script Befehl folgendes ausführen:
Joar... Game_Memory wird dann auch einmal gestartet... was dann passiert verwundert mich jedoch etwas.
Folgende Fehlermeldung taucht dann auf:
Zitat
Script 'Game_Interpreter' line 1411: NoMethodError occurred.
undefined method `memory='for nil:NilClass
...
Was natürlich auch noch sein kann ist, dass er meint in der Klasse stünde nichts drin.
Bedeutet ich müsste vermutlich am Ende der Klasse "start" aufrufen.
Also wie folgt:
Wobei er sich dann über folgenden Codeteil beschwert.
Da kommt dann folgende Fehlermeldung.
memory scheint aus der Klasse Game_System zu kommen.
muss ich das auch noch irgendwie starten und wenn ja wo?
Das ist seltsam, $game_system sollte es ja eigentlich schon geben. Die Fehlermeldung klingt im ersten Moment nämlich so, als ob $game_system noch nicht angelegt wurde, aber das sollte im laufenden Betrieb nicht der Fall sein. Oder benutzt du noch an anderer Stelle eine Variable oder Methode namens memory?
Ne, bin eben noch mal alle Scripte durchgegangen die ich verwende.
Nirgens findet sich auch nur das Wort "memory" oder "Memory"
Daher sollte es ja an und für sich auch keine Probleme mit anderen Scripten geben O_o (nicht in diesem Fall)
Falls es hilfreich ist, folgende Scripte sind noch im Einsatz. (Was euch ohne Code wenig hilft, aber irgendwie nehme ich auch mal an, dass ihr wenig Lust habt den durchzuschauen) ^^
...hätte ich auch nicht.
Zitat
Yanfly Engine:
- Ace Message System
- Ace Menu Engine
Lone Wolf:
- Gamepad Extender
MOG
- MOG_Animated_Title_A
Khas Arcthunder:
- Pathfinding
...
Und dann halt noch eigene Scripte. Aber da bin ich mir recht sicher, dass da kein "Memory" oder "memory" drin vorhanden ist.
Bei den anderen auch... Suchfunktion und so.
In Game_System legst du noch gar keine Instanz von Game_Memory an, daher kommt wohl auch die Fehlermeldung. attr_accessor ermögtlich dir nur den Zugriff auf memory, sprich du kannst memory lesen und verändern. Angelegt wird memory dadurch noch nicht. Das ist zwar nicht die sauberste Art, aber du könntest ans Ende von initialize aus Game_System Folgendes setzen:
sind in der initialize-Methode von Game_Memory nicht so gut aufgehoben und syntaktisch denke ich auch nicht richtig. Auch
#b = Scene_Memory.new
#b.start
brauchst du nicht, weil der Scene_Manager das alles automatisch macht. Wenn ich den Ace richtig verstehe (ich kann mit der Lite-Version leider keine Scripts testen), dann müsstest du auf deiner ersten Map nur folgenden Aufruf machen:
Scene_Manager.call(Scene_Memory)
Falls ich etwas Falsches erzähle bitte gleich Bescheid sagen.