Ergebnis 1 bis 20 von 71

Thema: [VX-Ace] Script zeigt Bild nicht an

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1

    [VX-Ace] Script zeigt Bild nicht an

    Hallo zusammen ^^

    Ich arbeite gerade an einem Script für ein Memory und habe momentan ein großes Problem, mein Bild wird nicht angezeigt.

    Das Debakel sieht bis nun so aus:
    Code:
    def draw_graphic
          @graphic = Sprite.new
    
          if $modus == 1
            #@graphic.bitmap = Cache.picture("Grundkarte.png") rescue nil
          end
          if $modus == 0
            @graphic.bitmap = Cache.picture("Testkarte.png", 1, $x_1, $y_1, 100, 100,
            255, 0) rescue nil
          end
        
        end
    In den Variablen $x_1 und $y_2 stehen Werte. Eigentlich war es von mir angedacht damit das Bild richtig auf dem Screen zu platzieren. (Habe auch via Konsole geprüft ob in den Beiden was drin steht, tut es und auch der modus ist 0.)

    Mein Versuch "Number" mitzuliefern sieht wie folgt aus:
    Code:
    if $modus == 0
            @graphic.bitmap = Cache.picture[1].show("Testkarte.png", 1, $x_1, $y_1, 100, 100,
            255, 0) rescue nil
          end
    Bekomme aber nur einen schwarzen Bildschirm geliefert.
    Wenn ich das "rescue nil" wegnehme schmiert mir Spiel ab. So dann auch mal zwischendrin die Frage, was macht dieser Befehl überhaut. Habe ihn in anderen Scripten mit Bildern immer wieder gesehen.

    Ein Bild wird mir zudem angezeigt wenn ich einfach sage:
    Code:
    @graphic.bitmap = Cache.picture("Testkarte.png")
    Aber das ist ja nicht das was ich möchte. Dann ist das Bild stumpf oben links in der Ecke und zudem möchte ich ja auch mehrere anzeigen.

    Natürlich kann man so ein Memory auch leicht ohne Script basteln, aber ich möchte es unbedingt mit hinbekommen.

    Wäre super lieb wenn mir hier wer auf die Sprünge helfen könnte.

  2. #2
    Mit der Position hat das Bitmap nichts am Hut, das wird über den Sprite geregelt. Die beiden zusammen kann man sich ein bisschen vorstellen wie eine Leinwand (Sprite) auf die eben was gemalt wird (Bitmap). Die Leinwand mit dem Gemalten wird dann irgendwo platziert.

    Was du machen musst, ist also folgendes:
    Code:
    sprite = Sprite.new
    sprite.bitmap = Cache.picture('Testkarte.png')
    sprite.x = $_x1
    sprite.y  = $_y1
    Cache.picture holt einfach nur ein Bitmap aus dem Cache, wenn es da drin ist, ansonsten wird von der Festplatte geladen. Das spart Zeit und Speicher falls eben dieses Bitmap noch öfters gebraucht wird. Verschiedene Leinwände teilen sich dann quasi ein Gemälde. Wenn du hingegen fünf mal direkt Bitmap.new('Graphics/Pictures/meinbild.png') nutzt, hast du am Ende unnötigerweise fünf Mal ein identisches Bitmap im Speicher, das auch jedes Mal wieder neu von der Festplatte geladen werden muss.

    rescue nil ist übrigens eine dumme Idee, weil du damit den Fehler dort unterdrückst wo er passiert und dann je nach Script ziemlich sicher später an anderer Stelle eine Fehlermeldung bekommst, deren Ursprung auf den ersten Blick nicht zu erkennen ist.



    Edit: Achso, falsch verstanden. Du wolltest wissen, was es mit dem rescue auf sich hat, nicht mit dem Cache?

    Wie gesagt, damit werden Fehler abgefangen.

    In deinem Beispiel ist der Cache.picture-Aufruf fehlerhaft, deshalb wird stattdessen das getan, was hinter dem rescue steht, in deinem Fall einfach ein allein stehendes nil. Dem Sprite wird also nie ein Bitmap zugewiesen, deshalb bleibt der Bildschirm schwarz.

    Geändert von Cepanks (29.03.2015 um 10:32 Uhr)

  3. #3
    Klasse, danke.
    Ein Bild juhu

    Aber... wie bekomme ich jetzt mehrere Bilder auf einmal angezeigt?
    Immer wieder einen neuen sprite[nummer] = Sprite.new erzeugen klappt ja nicht (ja ich habs versucht). Da wird nur ein Bild angezeigt und das Vorherige ausgeblendet.
    Bin sowieso ein wenig verwundert, der Fehler liegt vermutlich weiter oben im Code, aber nach ein paar Sekunden verschwinden mein Bild auch einfach wieder.
    Ich glaube ich brauche noch einen Schubser in die richtige Richtung

  4. #4
    Sprites in RGSS haben keine Nummern. Du musst für jeden Sprite eine neue Variable verwenden, sonst wird der alte überschrieben. Was natürlich auch geht, ist ein Array, so werden auch die Pictures im Maker verwaltet. Das versuchst du scheinbar gerade.

    Um genau sagen zu können warum dein Bild wieder verschwindet, fehlen Informationen. Hast du für dein Memory eine neue Szene erstellt oder versuchst du das irgendwo anders mit reinzuschreiben? Am besten wäre, wenn du mal dein komplettes Script zeigst.

    Ein erster Tipp wäre, dass "sprite" nur eine lokale Variable ist, die wieder verfällt, sobald Ruby mit deiner Methode durch ist, in der der Sprite erstellt wird. (War in meinem Beispiel auch so, aber das war eigentlich nur zur Demonstration von Sprites^^)

  5. #5
    Also bis jetzt sieht das so aus:

    Code:
    class Memory < Scene_Base
    
    #-------------------------------------------------------------------------------
    # Startet nach und nach alle Methoden
    #-------------------------------------------------------------------------------
      def start
        super 
        gegebenheiten
        draw_graphic
      end
    
    #-------------------------------------------------------------------------------
    # Legt die Grundvariablen und Arrays fest. Verteilt zudem die Koordinaten.
    #-------------------------------------------------------------------------------
    
       def gegebenheiten
        # Hier wird der Modus grundlegend bestimmt.
    
        if $game_switches[1] == true
          $modus = 0
        end
        if $game_switches[2] == true
          $modus = 1
        end
        if $game_switches[3] == true
          $modus = 2
        end
        
        # IDs der Spielkarten, ihr Standort in dem Array bestimmt die Position
        # auf dem Spielfeld. Zahlen kommen doppelt vor, da es von jeder Karte
        # 2 gleiche gibt.
        random = [
        0, 0,
        1, 1,
        2, 2,
        3, 3,
        4, 4,
        5, 5,
        6, 6,
        7, 7
        ].shuffle
        # in diese Variablen werden die IDs aus dem Array gespeichert, zwecks 
        # Vergleich.
        # mal schaun ob ich das so brauche
        # $id_1 = 0
        # $id_2 = 0
        
        # Die Koordinaten lauten: 
        # 000 x 000 | 136 x 000 | 272 x 000 | 408 x 000
        # 000 x 104 | 136 x 104 | 272 x 104 | 408 x 104
        # 000 x 208 | 136 x 208 | 272 x 108 | 408 x 108
        # 000 x 312 | 136 x 312 | 272 x 312 | 408 x 312
        xy = [
        0, 0, 
        136, 0, 
        272, 0, 
        408, 0,
        #
        0, 104, 
        136, 104, 
        272, 104, 
        408, 104,
        #
        0, 208, 
        136, 208, 
        272, 208, 
        408, 208,
        #
        0, 312, 
        136, 312, 
        272, 312, 
        408, 312
        ]
        i = 0
        j = 0
        r = 0
        # Hier kommt zusammen was zusammen gehört!
        while i < 15 do
          $xi = xy[j]
          j = j + 1
          $yi = xy[j]
          j = j + 1
          $r = random[r]
          r = r + 1
          
          if i == 0
            $id_0 = $r
            $x_0 = $xi
            $y_0 = $yi
          end
          if i == 1
            $id_1 = $r
            $x_1 = $xi
            $y_1 = $yi
            p $x_1
            p $y_1
          end
          i = i + 1
        end
      end
    
    #-------------------------------------------------------------------------------
    # Sorgt für das Anzeigen der Bilder
    #-------------------------------------------------------------------------------
      
      def draw_graphic
          sprite = Sprite.new
          sprite1 = Sprite.new
    
          if $modus == 1
            
          end
          if $modus == 0
            sprite.bitmap = Cache.picture("Testkarte.png")
            sprite1.bitmap = Cache.picture("Spinne1.png")
            sprite.x = $x_0
            sprite.y = $y_0
            sprite1.x = $x_1
            sprite1.y = $y_1
          end
        
        end
    end
    Aufgerufen wird es mit dem Befehl halt
    Code:
    SceneManager.call(Memory)
    Bis jetzt noch seeeehr umständlich geschrieben, aber erstmal soll es ja laufen, verfeinern kann man ja immer noch :3

  6. #6
    Hi,

    du musst die Sprites "am Leben halten". Wenn du sie in lokale Variablen abspeicherst, werden sie irgendwann vom GarbageCollector gelöscht und die Grafik wird nicht mehr angezeigt.

    Hierfür verwendest du am besten Instanzvariablen (also die mit dem @).

    Globale Variablen solltest du möglichst vermeiden. Nimm am besten die globalen Variablen die die RGGS selbst definiert und füge dort deine eigenen Attribute hinzu.

    Überhaupt macht es Sinn deinen Code mehr in Klassen zu organisieren, sonst wirst du schnell den Überblick verlieren.

    Hier mal ein Beispiel wie das aussehen könnte:
    Code:
    class Game_Memory
      # hier kommen alle Attribute rein die dein Spiel so hat
      
      # filenames of the pictures of your cards
      attr_accessor :figure_names
      
      def initialize
        @figure_names = []
        @positions = []
        # number of cards horizontal
        @max_number_of_cols = 4 
        # size of cards
        @card_width = 64
        @card_height = 64
        # distance between cards
        @margin = 12
      end
      
      # returns x, y coordinates as well as the image name of
      # the card with the given index
      def get_card index
        [card_x(index), card_y(index), card_image(index)]
      end
    
      # return all cards
      def get_cards
        # create a new array with elements
        # for all indizes from 0 upto @positions.size
        (0...@positions.size).map do |index|
          # each element contains the result value of
          # get_card
          get_card index
        end
      end
      
      # x coordinate for a given card index
      def card_x index
        col_num = index % @max_number_of_cols
        col_num * (@card_width + @margin)
      end
      
      # y coordinate for a given card index
      def card_y index
        row_num = index / @max_number_of_cols
        row_num * (@card_height+@margin)
      end
      
      # filename of card image
      def card_image index
         @figure_names[@positions[index]]
      end
      
      # number of different figures/cards
      def number_of_pictures
        @figure_names.size
      end
       
      # add 2 cards for each figure into the field
      # shuffle the positions of all cards
      def shuffle_cards
        @positions.clear
        # this loop is invoked as often as the number
        # of different cards
        number_of_pictures.times do |picture_id|
          # for each invocation, two cards are added
          # into the array 
          @positions << picture_id << picture_id
        end
        # shuffle the array at the end
        @positions.shuffle!
      end
      
    end

    Das ist deine Game-Logik Klasse. Die verwaltet alle Attribute des Memory-Spiels sowie die Spiellogik dahinter. Was sie nicht macht ist das Anzeigen der Grafik und das entgegennehmen von Nutzereingaben. Das regelst du über die Scene. Als nächstes speicherst du dieses Memory-Objekt in ein bestehendes globales Objekt ab. z.B. Game_System

    Code:
    class Game_System
      # füge Memory als Attribut hinzu
      attr_accessor :memory
    end
    Jetzt kannst du ein neues Memory-Spiel mit
    Code:
    $game_system.memory = Game_Memory.new
    anlegen.

    Als nächstes legst du deine Szene-Klasse an. Hier musst du dir klar machen das Grafiken in einer Szene einen Lebenszyklus haben: Jede Grafik muss angelegt, geupdatet und am Ende der Szene wieder gelöscht werden. Dafür legst du entsprechend drei Funktionen an:
    Code:
    def initialize_graphics
      # for each card in the game
      @card_sprites = $game_system.memory.get_cards.map do |x, y, image|
        # create a new sprite
        sprite = Sprite.new
        # set its graphic and coordinates
        sprite.bitmap = Cache.picture(image)
        sprite.x, sprite.y = x, y
        # and "return" the sprite into the array
        sprite
      end
    end
    
    def update_graphics
      # update attributes of all sprites
      @card_sprites.each_with_index do |sprite, index|
        x, y, image = $game_system.memory.get_card(index)
        sprite.bitmap = Cache.picture(image)
        sprite.x, sprite.y = x,y
      end
    end
    
    def dispose_graphics
      @card_sprites.each do |sprite|
        sprite.dispose
      end
    end
    Das updaten ist erstmal nicht so wichtig, solange du die Positionen der Karten nicht änderst. Wenn du aber neue Funktionen einfügst, wie z.B. das der Nutzer Karten entfernen kann, müssen diese Aktionen Auswirkungen auf das Anzeigen der Grafiken haben. Das regelst du in dem Update-Zyklus. Sprich: Wenn der Nutzer eine Karte abhebt änderst du die Variablen in $game_system.memory. Beim nächsten Updaten der Grafiken werden diese Änderungen dann sichtbar.

    Ich hoffe ich hab dir nicht zu viel "vorgesagt". Aber ich denke es gibt noch genug zu tun. So ein Memory-Spiel ist eine gute Übung für das Scripten in der RGSS.

    Ein paar Vorschläge was man noch bessern könnte:
    - wenn du viele Grafiken verwalten musst, legt für diese eine neue Klasse Spriteset_Memory an. Dann musst du weniger Code in deine Szene-Klasse legen.
    - genauso macht es evtl. Sinn die Logik hinter den Spielkarten in eine eigene Game_MemoryCard Klasse auszulagern. Die Klasse Game_Memory verwaltet dann einen Array von Game_MemoryCard Objekten, die Methoden zum Abfragen der Position etc. haben
    - genauso kannst du eine Klasse Sprite_GameCard schreiben die eine Referenz auf Game_MemoryCard hat und weiß wie sie grafisch angezeigt werden

  7. #7
    Danke euch Zwei so weit, aber ich glaube ich noch was an dem Code von -KD- falsch verstanden.

    Zum Verständnis...
    In das Array
    Code:
    @figure_names = []
    kommen alle Bilder rein die ich anzeigen möchte oder?
    zum Beispiel sehe das dann so aus
    Code:
     @figure_names = ["Testkarte.png"]
    oder?

    Sollte das so weit stimmen, anderes Verständnisproblem.
    Code:
    def initialize_graphics
      # for each card in the game
      @card_sprites = $game_system.memory.get_cards.map do |x, y, image|
        # create a new sprite
        sprite = Sprite.new
        # set its graphic and coordinates
        sprite.bitmap = Cache.picture(image)
        sprite.x, sprite.y = x, y
        # and "return" the sprite into the array
        sprite
      end
    end
    .

    Muss ich das für jede Karte machen? Bis jetzt kam es mir so vor, als würde das der Code für alle schon regeln, glaube aber hier etwas arg falsch verstanden zu haben.
    Momentan kommt mir der dumpfe Gedanke... dass die Zeile
    Code:
     @card_sprites = $game_system.memory.get_cards.map do |x, y, image|
    so aussehen müsste:
    Code:
    @card_sprites = $game_system.memory.get_cards.map do |0, 0, "Testbild.png"|
    Wobei ich mich dann natürlich auch wieder frage, wofür ich vorher
    Code:
    @figure_names
    und
    Code:
    @positions
    habe. Mir war so genau diese müssten dafür da sein.
    Natürlich lagen die beiden Array in einer Klasse, wodurch das zusammenhängen kann das ich das noch einmal machen muss, dennoch bin ich momentan arg verwirrt, da ich stark davon ausgehe dass der Code von -KD- garantiert so funktioniert... unter Voraussetzung man setzt ihn richtig ein.
    Kompelieren tut er... nur wie immer sehe nichts XD ... wegen weil falsche Bedienung meinerseits.

    Hilfe ^^"

  8. #8
    Hi,

    ich hab den Code nicht ausprobiert. Gut möglich das da was noch nicht funktioniert.

    Zitat Zitat
    In das Array kommen alle Bilder rein die ich anzeigen möchte oder?
    Jap

    Zitat Zitat
    Muss ich das für jede Karte machen? Bis jetzt kam es mir so vor, als würde das der Code für alle schon regeln, glaube aber hier etwas arg falsch verstanden zu haben.
    Nein, das macht es automatisch für alle Karten. Die map Funktion bildet einen Array (oder eine beliebige andere Collection) auf einen neuen Array ab, in dem es eine Funktion auf jedes Element des Arrays ausführt.
    Beispiel: Du hast eine Liste von Zahlen [1,2,3,4,5] und wendest darauf ein map mit der Quadrat-Funktion an. Dann bekommst du eine Liste [1,4,9,16,25] zurück. Vom Code her würde das folgendermaßen aussehen:
    Code:
    zahlen = [1,2,3,4,5]
    # entweder
    quadratzahlen = zahlen.map {|x| x**2 }
    # oder
    quadratzahlen = zahlen.map do |x| 
      x**2 
    end
    
    # das ist exakt dasselbe wie
    quadratzahlen = []
    for zahl in zahlen do
      quadratzahlen << zahl**2
    end
    Ob du geschweifte Klammern oder do...end benutzt ist egal. Was da zwischen den |...| steht sind die Parameter der Funktion, die du map übergibst.

    In dem Beispielcode
    Code:
    # for each card in the game
    @card_sprites = $game_system.memory.get_cards.map do |x, y, image|
      # create a new sprite
      sprite = Sprite.new
      # set its graphic and coordinates
      sprite.bitmap = Cache.picture(image)
      sprite.x, sprite.y = x, y
      # and "return" the sprite into the array
      sprite
    end
    sagst du: Lege einen Array an: für jede Karte, die $game_system.memory.get_cards zurückliefert, erzeuge einen Sprite, setze dessen x und y Koordinaten auf die Koordinaten der Karte, sowie seine Grafik auf die Grafik der Karte. Füge diesen Sprite dann in den Array ein. Setze am Ende die Variable @card_sprites auf diesen Array.

    Der Code ist gleichbedeutend zu
    Code:
    @card_sprites = []
    for x, y, image in $game_system.memory.get_cards do
      # create a new sprite
      sprite = Sprite.new
      # set its graphic and coordinates
      sprite.bitmap = Cache.picture(image)
      sprite.x, sprite.y = x, y
      # and "return" the sprite into the array
      @card_sprites << sprite
    end

    map gehört zu den Iteratorfunktionen (in anderen Sprachen auch Funktoren genannt). Das sind Methoden, die eine Funktion (also ein Stück ausführbaren Code) als Parameter bekommen.

    In meinen Codebeispielen sind noch zwei weitere Iteratorfunktionen: each führt eine Funktion für jedes Element des Arrays aus (ohne dabei aber einen neuen Array zu erzeugen).

    each ist identisch mit der For-Schleife.
    Code:
    zahlen = [1,2,3,4,5]
    zahlen.each do |zahl|
      print zahl
    end
    # ist gleichbedeutend zu
    for zahl in zahlen do
      print zahl
    end
    n.times führt eine Funktion n Mal aus. Sie entspricht einer Zählerschleife in anderen Sprachen.
    Code:
    5.times do
      print "Hallo Welt"
    end
    # ist gleichbedeutend mit
    for i in 1..5 do
      print "Hallo Welt"
    end
    
    # times darf auch die Index-Variable als Parameter bekommen
    5.times do |i|
      puts "dies ist die #{i}te Wiederholung
    end
    # ist gleichbedeutend mit
    for i in 0..4 do
      puts "dies ist die #{i}te Wiederholung
    end
    Das Tutorial ist sehr veraltet und bezieht sich noch auf Ruby 1.8 (und den RPGMaker XP), aber evtl. hilft dir die dortige Erklärung zum Verständnis von Iteratorfunktionen: http://www.rpg-studio.org/scientia/R...2_-_OOP_Teil_2

  9. #9
    Hey ^^

    Ich befinde mich gerade auf Fehlersuche und bin schwerst verwirrt.
    Der scheint nur in die erste Methode einer Klasse zu gehen. Muss ich die anderen Klassen wie in meinem alten Code noch mal alle extra aufrufen?

    Jedenfalls... er geht soweit ich das sehe nur in die Methode "initialize" (immer und immer wieder) und in die Klasse "Game_System", genau einmal.

    Edit:
    Wie ich es im alten Code gemacht habe funktioniert es leider nicht... warum geht der denn nicht in die anderen Methoden rein Oo
    ... oder sehe ich das einfach nur nicht... der müsste mir doch mit:
    Code:
    irgendwas = 99
    p irgendwas
    den Wert von "irgendwas" in der Konsole ausgeben. Egal wo ich mich befinde.

    Geändert von Elster (02.04.2015 um 14:28 Uhr)

  10. #10
    Ja, vermutlich wird die Stelle im Code nicht erreicht. <initialize> wird immer dann aufgerufen, wenn ein neues Objekt der Klasse angelegt wird. Hast du beispielsweise die Klasse Auto und schreibst: auto = Auto.new, dann wird <initialize> von Auto einmal aufgerufen. Die anderen Methoden der Klasse werden dann aufgerufen, wenn du im Code explizit auto.[Name der Methode] schreibst.

  11. #11
    Automatisch geht er in keine Methode rein. Wie Kelven schon gesagt hat: die initialize Methode wird aufgerufen sobald du Klasse.new schreibst.

    Während das Spiel läuft wird ständig SceneManager.scene.main aufgerufen. Dadurch das du SceneManager.call(Memory) geschrieben hast, wird die Klasse Memory als aktive Szene gesetzt und zuerst die initialize Methode und danach die main Methode von Memory aufgerufen.
    Da deine Memory Klasse von Scene_Base erbt, übernimmt sie deren main Methode. Wenn du also wissen willst, was main macht, musst du in die Klasse Scene_Base reinschauen. Dort siehst du, dass main erst die Methode start aufruft, und danach in einer Schleife die Methode update aufruft bis die Scene beendet wird. Danach wird die Methode terminate aufgerufen.

    In der start-Methode hast du dann dein draw_graphic aufgerufen.

    Du müsstest jetzt noch die update Methode überschreiben und dort update_graphics aufrufen, sowie die terminate Methode überschreiben und dort dispose_graphics aufrufen (und wichtig dabei: in den Methoden immer am Anfang das Schlüsselwort super schreiben, damit die gleichnamige Methode der Superklasse Scene_Base aufgerufen wird).

Stichworte

Berechtigungen

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