RMXP > Ruby-Script: Bräuchte Hilfe bei grafischem Menu-Window!
Hallo,
ich würde gerne in einem Spiel im Item-Menü ein zusätzliches Fenster anzeigen, welches größere Bilder der ausgewählten Items anzeigt.
Mein Ruby reicht soweit, dass ich Fenstergrößen und Positionen ändern kann, aber nicht weit genug um dem Maker konkret zu sagen welche Dateien aus welchen Ordnern er wo anzuzeigen hat. Ich bräuchte in diesem Punkt etwas Hilfe.
Es wäre übrigens super wenn ich die entsprechenden Bilddateien direkt entweder über den Namen des Items oder den Dateinamen des Icons ermitteln lassen könnte, also zum Beispiel einfach eine gleichnamige Datei aus den Picture-Ordner ansteuern zu lassen.
Wäre lieb wenn mir da jemand helfen könnte. Neue Sachen zu lernen macht zwar Spaß, aber das ganze Programmiergedöhns überfordert leider leicht meine Konzentrationsfähigkeiten
Das sollte relativ einfach gemacht sein. Hättest du gerne ein zusätzliches statisches Fenster im Menü, oder möchtest du lieber, dass es erst mit einem Tastendruck erscheint?
Beides sollte selbst für einen Anfänger einigermaßen "schnell" erledigt sein.
Du kannst auch die Bilddatei direkt über den Namen des Items oder des verwendeten Icons auswählen lassen.
Ein statisches Fenster, bitte! Wenn Du, oder sonst jemand mir das machen könnte wäre das super, dann könnt ich mich wieder auf die anderen Aspekte konzentrieren, welche mir mehr liegen.
Auch wenns vielleicht Anfängerkram ist, ich komm bei all den kryptischen Begriffen und Zahlen immer leicht durcheinander und bin dann frustriert. An Sprites rumpixeln und Dialoge schreiben liegt mir irgendwie mehr ^-^
Ein Problem beim Item-Menü könnte aber sein, dass das Fenster dem Item-Fenster ja einigen Platz wegnimmt. Zumindest wenn du den Standardaufbau benutzt.
Ein Problem beim Item-Menü könnte aber sein, dass das Fenster dem Item-Fenster ja einigen Platz wegnimmt. Zumindest wenn du den Standardaufbau benutzt.
...
Hehe, das ist kein Problem. Ehrlich gesagt, das Fenster ist nötig geworden, weil ich einige andere Elemente aus dem Menü wegen spielbezogener Nutzlosigkeit entfernt habe.
Zitat von Cornix
Ich werde das Script nicht für dich schreiben, aber wenn du es willst werde ich dir gerne im Detail erklären wie du es selbst tun kannst.
...
Schon klar, Du brauchst kein komplettes Script für mich zu schreiben. Mir würden schon die Codezeilen reichen, mit denen ich die Bilddatei finden und anzeigen lassen kann. Den allgemeinen Fensteraufbau werd ich mir dann zusammenkopieren und ein bisschen rumprobieren, bis alles hinhaut
Auch wenn ich nicht Cornix bin: So ungefähr zeigt man allgemein ein Bild im XP an (zumindest machen es die Leute von Enterbrain so). Da du dich mit der Programmierung nicht so auskennst, werde ich dich nicht mit Fachwörtern erschlagen.
Testen kannst du, indem du per Event-Befehl Script Folgendes eingibst:
Das Bild jetzt in einem Menü anzuzeigen, ist natürlich noch etwas komplizierter.
Ach so, eine Sache hab ich noch vergessen. Grundsätzlich ist es immer gut, die angelegten Bilder zu entfernen, wenn sie nicht mehr benötigt werden. Im Menü wäre das der Fall, wenn der Spieler das Menü verlässt. Dazu ruft man die Methode dispose auf. Beim Beispiel oben müsste dafür noch Folgendes vor das letzte end.
Löschen würde man dann mit
Falls ich hier Unsinn schreibe bitte sofort verbessern.
Okay, hier einmal im Groben:
1) Musst du eine neue Klasse anlegen für dein zusätzliches Window. Zum Beispiel mit dem Namen Window_Preview. In dieser Klasse werden wir später programmieren wie ein Bild dargestellt wird.
2) Als nächstes müssen wir die Scene_Item (eine bereits vorhandene Klasse) abändern um unser neues Window_Preview einbinden zu können. Genaueres dazu später.
3) Zum Schluss kümmern wir uns darum, dass alle Informationen korrekt übertragen werden damit wir auch das richtige Bild anzeigen können.
So, jetzt im Detail. Für dein eigenes Window kannst du dich einmal an den bereits vorhandenen orientieren. Die Klasse Window_Help könnte hierfür ein guter Ausgangspunkt sein. Diese Klasse wird verwendet um eine kurze Beschreibung zu Items oder Fähigkeiten zu zeigen, unter anderem auch im Item-Menü.
Am Anfang könnte unsere Klasse wie folgt aussehen:
Beachte, die Positionierung unseres Fensters, ich habe hier y = 64 gewählt, weil das Window_Help (mit der Item-Beschreibung) eine Y-Position von 0 und eine Höhe von 64 hat. Dementsprechend wird unser Preview-Window direkt unter dem Help-Window sein.
Das bedeutet auch, dass wir die Position und Höhe des Item-Windows anpassen müssen.
Also gehen wir zur Klasse Window_Item, hier verändern wir wie die Position und Dimension im Konstruktor gesetzt wird.
Das hier wäre eine gute Variante die Klasse zu verändern:
Dadurch wird das Item-Window genau unterhalb unseres Preview-Windows platziert und den restlichen Platz bis zum Bildschirmrand einnehmen.
Als nächstes packen wir unser Fenster in die Scene_Item. Wir müssen uns um folgende Dinge kümmern:
1) Fenster erstellen
2) Fenster vernichten
3) Fenster updaten und Inhalt setzen
Dabei kannst du dich daran orientieren wie die anderen Fenster bedient werden, nämlich das @item_window und das @help_window.
Dort wo diese Fenster erstellt werden erstellen wir am besten auch unser Preview-Window, zum Beispiel:
Ebenfalls in der Main-Methode werden die Fenster wieder zerstört, das passiert über die "dispose" methode.
Also:
dort wo auch die beiden anderen Fenster entfernt werden.
Jetzt noch das update, dafür fügst du in der Methode "update" der Scene_Item die Zeile:
dort ein wo auch die übrigen Fenster ge-updated werden.
Jetzt ist das Fenster bereits in unserem Menü, nur wird noch nichts angezeigt. Worum wir uns zuerst kümmern sollten ist, dass wir die Information über das ausgewählte Item zu unserer Window_Preview-Klasse bekommen.
Auch hier schauen wir einmal wie es mit dem Window_Help getan wird, immerhin bekommt dieses Fenster auch seine Informationen über das ausgewählte Item.
Mit ein wenig recherche werden wir erkennen, dass das Update für unser Fenster in der Klasse Window_Item geschieht. Hier gibt es die Methode "update_help" wo das Beschreibungs-Fenster über das ausgewählte Item informiert wird.
An genau dieser Stelle werden wir jetzt auch dafür sorgen, dass unser Preview-Window auch über das Item informiert wird.
Damit das Item-Window unser Fenster informieren kann muss es eine Variable besitzen, welche auf das Fenster verweist. Ich nenne diese Variable einmal @preview_window.
Die update_help Methode von Window_Item könnten wir nun wie folgt schreiben:
Bitte beachte, wir prüfen ersteinmal ob ein solches Fenster überhaupt existiert! Das ist wichtig, weil das Window-Item ebenfalls im Kampf eingesetzt wird und wir dort kein Preview-Window haben!
Auch wichtig: Die Zeile "attr_accessor review_window". Hier definieren wir unsere Variable!
Nun müssen wir uns auch noch darum kümmern, dass das Item-Window unser Preview-Window kennt. Das tun wir wieder in der Scene_Item, denn dort wird auch das Help-Window erstellt und bekannt gemacht.
In der "main" Methode gibt es dort die Zeile:
auf die gleiche Art und Weise gehen wir jetzt mit unserem Preview-Window vor:
Das letzte was fehlt ist nun nurnoch, dass die Bilder korrekt angezeigt werden. Dafür gehen wir zurück in die Klasse Window_Preview. Nun ist es Zeit die Methode "set_item(item)" zu implementieren.
Hier einmal eine sehr einfache Implementierung, welche einfach das Icon des Items wiederholt:
Bitte beachten, wir prüfen ersteinmal ob das neue Item ein anderes ist als das Item, welches wir gerade anzeigen!
Das ist wichtig um die Performance zu verbessern, aber nicht zwangsweise notwendig; ich würde es trotzdem dringend empfehlen.
Sofern das Item ein neues Item ist setzen wir die "contents" von dem Window auf "RPG:ache.icon(item.icon_name)". Die "contents" sind tatsächlich ein Bitmap, ähnlich wie das was Kelven vorher bereits angesprochen hat. Hier könntest du also ein neues Bitmap erstellen falls du es willst, für diesen einfachen Fall nehmen wir aber einfach nur das vorhandene Icon aus dem Cache.
Der Cache speichert alle Bilder, welche schoneinmal geladen wurden zwischen. Dadurch wird das Script schneller weil eine Datei nicht immer wieder neu von der Festplatte geladen werden muss.
Hier laden wir ein Bild von dem Ordner "icon". Du kannst auch aus dem Ordner "picture" oder jedem anderen laden.
Zum Beispiel mit:
Noch eine wichtige Sache:
Wenn du Bilder aus dem Cache lädst anstatt selbst welche zu erstellen darfst du sie NICHT mit dispose zerstören! Das hätte fatale Folgen.
dispose solltest du nur dann einsetzen wenn du das Bild mit "Bitmap.new" selbst erstellt hast.
Ich hoffe das ist ausreichend, um dir fürs erste den Weg zu zeigen deine eigene Version zu schreiben.
Falls du noch Fragen hast dann stell sie einfach.
Ah, schön, Ihr habt euch ja sogar richtig Mühe gemacht, danke
Also das Fenster hab ich jetzt und es funktioniert auch. Allerdings hab ich zwei kleine Problemchen.
Punkt 1 ist: Scheinbar gibt er beim Anzeigen des Bildes im Fenster noch etwas Spielraum an den ändern, soll heißen: Ein Fenster mit den Maßen 320x240 Pixel zeigt ein Bild mit den gleichen Maßen nicht deckungsgleich über dem Fenster an. Kann mir jemand bitte sagen, warum und vor allem, wie groß der Abstand genau ist, damit ich das kompensieren kann?
Punkt 2: Wie kann ich bei "set_item" noch eine Abfrage einbauen ob eine Datei mit dem Namen der Icon-Grafik tatsächlich im Picture-Ordner liegt? Möchte gerne eine Platzhalter-Grafik anzeigen lassen, wenn das nicht der Fall ist, statt einer Fehlermeldung mit Programmabbruch. Ist hauptsächlich für Testspiele während der Entwicklung, wenn ich noch nicht für jedes neue Item ein passendes Bild habe.
Punkt 2: Wie kann ich bei "set_item" noch eine Abfrage einbauen ob eine Datei mit dem Namen der Icon-Grafik tatsächlich im Picture-Ordner liegt? Möchte gerne eine Platzhalter-Grafik anzeigen lassen, wenn das nicht der Fall ist, statt einer Fehlermeldung mit Programmabbruch. Ist hauptsächlich für Testspiele während der Entwicklung, wenn ich noch nicht für jedes neue Item ein passendes Bild habe.
...
Alternativ geht auch File.exist?, allerdings weiß icht nicht, ob sich das mit verschlüsselten Dateien verträgt.
Punkt 1 ist: Scheinbar gibt er beim Anzeigen des Bildes im Fenster noch etwas Spielraum an den ändern, soll heißen: Ein Fenster mit den Maßen 320x240 Pixel zeigt ein Bild mit den gleichen Maßen nicht deckungsgleich über dem Fenster an. Kann mir jemand bitte sagen, warum und vor allem, wie groß der Abstand genau ist, damit ich das kompensieren kann?
...
Die Window-Klasse besitzt zwei Attribute, "ox" und "oy", diese beiden Attribute geben eine Verschiebung des contents in pixeln an. Mit ox kannst du die contents nach links schieben; mit oy nach oben. Um jeweils nach Rechts / Unten zu schieben musst du entsprechend negative Werte verwenden.
Im Moment hast du die linke obere Ecke deines Bildes an dem Punkt (0, 0). Du willst aber, dass das Bild in der Mitte des Fensters zentriert wird. Dafür brauchst du eine kleine Rechnung; denk kurz darüber nach, vielleicht kommst du ja von selbst drauf.
Hier ein kleiner Tipp, du kannst die Attribute "width" und "height" sowohl von der Window-Klasse als auch von den contents verwenden.
Sprich:
gibt die Breite des Fensters in Pixeln an, und
gibt die Breite des Bildes für das Item in Pixeln an. Natürlich muss das Bild dafür davor gesetzt worden sein.
Zitat von Lil_Lucy
Punkt 2: Wie kann ich bei "set_item" noch eine Abfrage einbauen ob eine Datei mit dem Namen der Icon-Grafik tatsächlich im Picture-Ordner liegt? Möchte gerne eine Platzhalter-Grafik anzeigen lassen, wenn das nicht der Fall ist, statt einer Fehlermeldung mit Programmabbruch. Ist hauptsächlich für Testspiele während der Entwicklung, wenn ich noch nicht für jedes neue Item ein passendes Bild habe.
...
Leider ein kleines bisschen kompliziert.
Damit du dies in Ruby überprüfen kannst musst du das Modul "FileTest" verwenden. Das Modul besitzt eine Methode namens "exists?" welche überprüft, ob eine Datei existiert oder nicht.
Diese Methode benötigt jedoch einen Dateipfad als Parameter.
Hier ein kleines Code-Schnipsel um nach einem Picture zu prüfen:
Vielleicht hilft dir das ja weiter.
O.K. Also die Methode von Cepanks, mit dem "rescue", hat super funktioniert. Das haut schon mal hin, danke
Allerdings bin ich bei Punkt 1 noch dezent überfordert
Ich weiß leider nicht so recht was ich mit "ox" und "oy" anfangen soll, weil, ich seh nicht, wo ich die ändern kann. Muss an der Stelle auch zugeben, dass ich nie so recht dahinter gestiegen bin, was Methoden wie "self" oder "super" eigentlich genau machen.
Mir würde es ja reichen zu wissen wie viele Pixel der Toleranzrahmen in einem Fenster beträgt, bzw. wie sich das errechnet, dann könnt ich zur Not die Bilddatei von vornherein anpassen.
Tut mir Leid, wenn ich mich da grade etwas blöd anstelle, aber wie gesagt, mein gesamtes Verständnis von Programmierung basiert mehr oder weniger auf "Pfusch and Error" ^^"
"ox" und "oy" sind Attribute die jedes Window besitzt. Alle Attribute werden auf die gleiche Art und Weise verwendet. Du kannst den Wert eines Attributes setzen mit "self.attribute = value" wobei "attribute" der Name des Attributs ist, und "value" ein Wert, welcher dem Attribut zugewiesen werden soll.
Zum Beispiel:
Dabei ist zu beachten, dass das "self" auch weggelassen werden kann, allerdings ist es sicherer es anzugeben um Namenskonflikte zu vermeiden.
Das Schlüsselwort "self" sagt einfach aus, dass dein Code sich auf "dieses" Objekt bezieht. In dem Fall des Fensters halt auf das Fenster in welchem der Code gerade ausgeführt wird.
Wörtlich: Das Fenster verändert seine eigenen Attribute und nicht die Attribute von einem anderen Fenster wenn du "self" verwendest.
Das "super" bedeutet, dass du Code an die Oberklasse delegierst. Hier kommt ein Konzept zum Einsatz, welches in der Programmierung als Polymorphie bezeichnet wird und relativ kompliziert ist.
Im Grunde beschreibt es ein Konzept um eine Klasse um Zusatzfunktionen zu erweitern. Zum Beispiel ist unser Window_Preview eine Unterklasse von Window_Selectable. Was das bedeutet ist, dass unser Window_Preview auch ein Window_Selectable ist, aber ein spezielleres.
Da das Window_Preview ein Window_Selectable ist erbt es alle Attribute und Funktionen eines Windows. Unter anderem erbt es "ox" und "oy", aber auch vieles mehr.
Wenn du an einer Stelle "super" verwendest dann sagst du explizit, dass du die Funktionalität der Oberklasse haben möchtest und eben nicht von der Klasse selbst, welche du gerade schreibst. Man benutzt es in der Regel nur, wenn man eine Funktion der Oberklasse erweitern will und die alte Funktion vollständig beibehalten möchte.
Zum Beispiel unser "super(x, y, width, height)" in der Methode "initialize" sagt aus, dass wir an dieser Stelle die Methode "initialize" von Window_Selectable aufrufen möchten.
Aber ich glaube das wird ein bisschen zu viel Theorie für einen Anfänger.
Das Fenster verändert seine eigenen Attribute und nicht die Attribute von einem anderen Fenster wenn du "self" verwendest.
...
Aber sollte das nicht sowieso immer implizit so sein? Ich frag nach, weil ich das self von Ruby auch nicht so wirklich durchschaue. In der Methode wird ja kein anderes contents benutzt.
"ox" und "oy" sind Attribute die jedes Window besitzt. Alle Attribute werden auf die gleiche Art und Weise verwendet. Du kannst den Wert eines Attributes setzen mit "self.attribute = value" wobei "attribute" der Name des Attributs ist, und "value" ein Wert, welcher dem Attribut zugewiesen werden soll.
Zum Beispiel:
Dabei ist zu beachten, dass das "self" auch weggelassen werden kann, allerdings ist es sicherer es anzugeben um Namenskonflikte zu vermeiden.
Das Schlüsselwort "self" sagt einfach aus, dass dein Code sich auf "dieses" Objekt bezieht. In dem Fall des Fensters halt auf das Fenster in welchem der Code gerade ausgeführt wird.
Wörtlich: Das Fenster verändert seine eigenen Attribute und nicht die Attribute von einem anderen Fenster wenn du "self" verwendest.
Das "super" bedeutet, dass du Code an die Oberklasse delegierst. Hier kommt ein Konzept zum Einsatz, welches in der Programmierung als Polymorphie bezeichnet wird und relativ kompliziert ist.
Im Grunde beschreibt es ein Konzept um eine Klasse um Zusatzfunktionen zu erweitern. Zum Beispiel ist unser Window_Preview eine Unterklasse von Window_Selectable. Was das bedeutet ist, dass unser Window_Preview auch ein Window_Selectable ist, aber ein spezielleres.
Da das Window_Preview ein Window_Selectable ist erbt es alle Attribute und Funktionen eines Windows. Unter anderem erbt es "ox" und "oy", aber auch vieles mehr.
Wenn du an einer Stelle "super" verwendest dann sagst du explizit, dass du die Funktionalität der Oberklasse haben möchtest und eben nicht von der Klasse selbst, welche du gerade schreibst. Man benutzt es in der Regel nur, wenn man eine Funktion der Oberklasse erweitern will und die alte Funktion vollständig beibehalten möchte.
Zum Beispiel unser "super(x, y, width, height)" in der Methode "initialize" sagt aus, dass wir an dieser Stelle die Methode "initialize" von Window_Selectable aufrufen möchten.
Aber ich glaube das wird ein bisschen zu viel Theorie für einen Anfänger.
...
O.K. bin zwar nicht sicher ob ich alles jetzt so wirklich verstanden habe, aber funktioniert hat es und ich hab jetzt ein akzeptables Ergebnis. Ich guck mir das morgen nochmal an und seh dann ob ichs noch etwas feiner hinkrieg, aber jetzt geh ich erstmal ins Bett.
Nicht immer. Z.B. im Code von Cornix' letzten Beitrag ist es ein Muss. Wie soll Ruby ohne self hier zwischen Aufruf einer Methode und Zuweisung an eine lokale Variable unterscheiden? Andere Programmiersprachen wie C++ haben ein ähnliches Problem (Paramater vs. member variable), jedoch bevorzuge ich das Schreiben eines Schlüsselwortes (self oder this) anstatt eines Unterstriches am Ende des Bezeichners.
contents.width anstatt self.contents.width zu schreiben ist jedoch okay (und in RGSS 1 und 2 vernachlässigbar schneller). Manche Leute bevorzugen es eben explizit oder wissen es nicht besser, weil die Standardskripte es so machen.
Aber sollte das nicht sowieso immer implizit so sein? Ich frag nach, weil ich das self von Ruby auch nicht so wirklich durchschaue. In der Methode wird ja kein anderes contents benutzt.
...
Ja, es ist implizit so und kann meistens weg gelassen werden, allerdings wenn man einmal in eine Situation gerät wo man es braucht um einen Namenskonflikt auf zu lösen und es vergisst hat man sehr schnell einen Bug in seinem Code.
Für einen Anfänger würde ich daher ersteinmal empfehlen immer das Schlüsselwort "self" zu verwenden, einfach um sicher zu sein.
@Lil_Lucy
Es freut mich, dass du dein Problem lösen konntest.