Ergebnis 1 bis 17 von 17

Thema: RMXP > Ruby-Script: Bräuchte Hilfe bei grafischem Menu-Window!

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    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:
    Code:
    class Window_Preview < Window_Base
      
      def initialize
        x = 0
        y = 64
        width = 640
        height = 128
        super(x, y, width, height)
      end
      
      def set_item(item)
        # kommt später
      end
      
    end
    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:
    Code:
    #==============================================================================
    # ** Window_Item
    #------------------------------------------------------------------------------
    #  This window displays items in possession on the item and battle screens.
    #==============================================================================
    
    class Window_Item < Window_Selectable
      #--------------------------------------------------------------------------
      # * Object Initialization
      #--------------------------------------------------------------------------
      def initialize
        y = 64 + 128
        height = 480 - y
        super(0, y, 640, height)
        # Ab hier interessiert uns der Code nichtmehr...
        
        @column_max = 2
        refresh
        self.index = 0
        # If in battle, move window to center of screen
        # and make it semi-transparent
        if $game_temp.in_battle
          self.y = 64
          self.height = 256
          self.back_opacity = 160
        end
      end
     
     # Der Rest interessiert uns im Moment nicht
     
    end
    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:
    Code:
    @preview_window = Window_Preview.new
    Ebenfalls in der Main-Methode werden die Fenster wieder zerstört, das passiert über die "dispose" methode.
    Also:
    Code:
    @preview_window.dispose
    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:
    Code:
    @preview_window.update
    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:
    Code:
    class Window_Item < Window_Selectable
    
      attr_accessor :preview_window
    
      # Der Rest interessiert uns ersteinmal nicht
    
      #--------------------------------------------------------------------------
      # * Help Text Update
      #--------------------------------------------------------------------------
      def update_help
        @help_window.set_text(self.item == nil ? "" : self.item.description)
        # Informiert das Preview-Window über das ausgewählte Item
        if @preview_window != nil
          @preview_window.set_item(self.item)
        end
      end
    end
    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:
    Code:
    @item_window.help_window = @help_window
    auf die gleiche Art und Weise gehen wir jetzt mit unserem Preview-Window vor:
    Code:
    @item_window.preview_window = @preview_window

    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:
    Code:
    class Window_Preview < Window_Base
      
      def initialize
        x = 0
        y = 64
        width = 640
        height = 128
        super(x, y, width, height)
        
        @item = nil
      end
      
      def set_item(item)
        if item != @item
          @item = item
          self.contents = RPG::Cache.icon(item.icon_name)
        end
      end
      
    end
    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:
    Code:
    self.contents = RPG::Cache.picture(item.icon_name)
    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.

  2. #2
    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.

    Auf jedenfall vielen lieben Dank schon mal!

    MfG

    Lil_Lucy

  3. #3
    Zitat Zitat von Lil_Lucy Beitrag anzeigen
    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.
    Code:
    def set_item(item)
      if item != @item
        @item = item
        begin
          self.contents = RPG::Cache.icon(item.icon_name)
        rescue
          self.contents = RPG::Cache.icon("platzhalterbild")
        end
      end
    end
    Alternativ geht auch File.exist?, allerdings weiß icht nicht, ob sich das mit verschlüsselten Dateien verträgt.
    Code:
    def set_item(item)
      if item != @item
        @item = item
        if File.exist?("Graphics/Icons/#{item.icon_name}")
          self.contents = RPG::Cache.icon(item.icon_name)
        else
          self.contents = RPG::Cache.icon("platzhalterbild")
        end
      end
    end

  4. #4
    Zitat Zitat von Lil_Lucy Beitrag anzeigen
    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:
    Code:
    self.width
    gibt die Breite des Fensters in Pixeln an, und
    Code:
    self.contents.width
    gibt die Breite des Bildes für das Item in Pixeln an. Natürlich muss das Bild dafür davor gesetzt worden sein.

    Zitat Zitat von Lil_Lucy Beitrag anzeigen
    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:
    Code:
      def set_item(item)
        if item != @item
          @item = item
          path = "Graphics/Pictures/"+item.icon_name+".png"
          # Prüft ob das Picture im Picture-Ordner existiert oder nicht
          if FileTest.exists? (path)
            self.contents = RPG::Cache.picture(item.icon_name) # Lädt ein Picture aus dem Picture-Ordner
          else
            self.contents = Bitmap.new(1, 1) # erstellt ein neues, leeres Bitmap
          end
        end
      end
    Vielleicht hilft dir das ja weiter.

  5. #5
    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" ^^"

  6. #6
    "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:
    Code:
    self.ox = 156
    self.oy = self.height / -3
    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.

  7. #7
    Zitat Zitat
    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.

  8. #8
    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.

  9. #9
    Zitat Zitat von Kelven Beitrag anzeigen
    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.

  10. #10
    Zitat Zitat von Cornix Beitrag anzeigen
    "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:
    Code:
    self.ox = 156
    self.oy = self.height / -3
    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.

    Danke soweit und bis zum nächsten Problem

    MfG

    Lil_Lucy

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •