Lieber Besucher, herzlich willkommen bei: RPG Studio - Make your World real. Falls dies Ihr erster Besuch auf dieser Seite ist, lesen Sie sich bitte die Hilfe durch. Dort wird Ihnen die Bedienung dieser Seite näher erläutert. Darüber hinaus sollten Sie sich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutzen Sie das Registrierungsformular, um sich zu registrieren oder informieren Sie sich ausführlich über den Registrierungsvorgang. Falls Sie sich bereits zu einem früheren Zeitpunkt registriert haben, können Sie sich hier anmelden.

Redclaw

Rekrut

  • »Redclaw« ist der Autor dieses Themas

Motto: Beat down your enemy, you are your only true enemy!

  • Nachricht senden

1

Freitag, 17. Februar 2017, 12:43

Überprüfen ob ein Item im Inventar ein Schlüsselitem ist

Heyho, ich arbeite zurzeit an einem Projekt und wollte für mein Spiel was kleines Skripten.
Es geht einfach nur um eine Methode um ein zufälliges Item das sich gerade im Inventar befindet zu bestimmen.
Dabei sollen jedoch keine wichtigen Gegenstände ausgewählt werden.

Um es konkret zu benennen, ich habe vor einem Gegner einen Steal-Skill zu geben.
Das heisst wenn ein Gegner diesen Skill einsetzt, stiehlt er ein zufälliges Item das gerade im Inventar ist.
Natürlich wäre es jetzt doof, wenn dieser Gegner dabei einen wichtigen Gegenstand nimmt wie beispielsweise einen Schlüssel der nur einmal zu erhalten ist.
Dann wäre logischerweise die Forsetzung des Spiels nicht mehr möglich und das Game broken.

Schön und gut, ich dachte mir ich überprüfe das einfach mit der "key_item?"-Methode und filtere damit die wichtigen Gegenstände aus.
Doch scheinbar habe ich noch nicht den Durchblick, was die RGSS3-Klassen angeht.

Mit diesem Code habe ich es bereits versucht:

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
PARTY_ITEM_ID = 27 #Number of variable which will be used to save the id
PARTY_ITEM_TYPE = 28 #Number of variable which will be used to save the type
 
class Game_BaseItem
  attr_reader :class
  attr_reader :item_id
end
 
class Game_Interpreter
 
def random_int(min, max)
	rand(max - min) + min
end
 
 def rnd_partyitem(var_itemid = PARTY_ITEM_ID, var_itemtype = PARTY_ITEM_TYPE)
   items = $game_party.all_items
   x = random_int(0,items.size) #ID of the selected object (item, armor, weapon) from the party
   type = -1 #type from the item ¦ -1 means invalid type
 
   if items[x].class != nil
   case items[x].class
   when RPG::Item #&& !$data_items[x].key_item?
 	type = 1
   when RPG::Weapon
 	type = 2
   when RPG::Armor
 	type = 3
   end
 else
   type = -1
   end
	$game_variables[var_itemid] = items[x].id if items[x] != nil
	$game_variables[var_itemtype] = type if items[x] != nil
	end
  end


Wahrscheinlich ist euch jetzt aufgefallen, dass ich den Codeabschnitt

Ruby Quellcode

1
#&& !$data_items[x].key_item?
auskommentiert habe.
Dies habe ich getan, da beim Aufruf der Methode im Playtest ein NoMethodError auftrat.

Meine Frage ist jetzt, wie muss ich vorgehen um zu überprüfen, ob ein Item ein wichtiger Gegenstand ist oder nicht?
Sehr wahrscheinlich habe ich den Aufbau der Klassen in RGSS3 noch nicht ganz verstanden, jedoch will ich jetzt eigentlich auch nicht 4h lang den Code durchlesen nur damit ich weiss wie ich diese Sache überprüfen kann. Kann mir da jemand vielleicht helfen?
Signatur folgt...

2

Freitag, 17. Februar 2017, 18:03

Das Script im jetzigen Stand hat einige Fehler und unsauberkeiten. Gehen wir es doch mal gemeinsam durch. :-)

Zeile 4-7 kannst Du erstmal auskommentieren. Diese Definitionen brauchst Du nicht. Die Klasse Game_BaseItem bringt bereits eine Methode #class mit, die hat sie von der Klasse Object geerbt und liefert dir auch genau das zurück, was Du dir darunter vorstellst.

Zeile 20 und 29 bis 31 brauchst Du auch nicht. Kannst Du auskommentieren. Ein Objekt in Ruby hat immer eine Klasse. Selbst nil hat eine Klasse. Somit kann also items[x].class niemals keine Klassendefinition zurückgeben.

In Zeile 22 musst Du das .class entfernen. Das liegt daran, wie das case-Statement in Ruby funktioniert. Wenn dich das genauer interessiert, frag ruhig nach - dann schreibe ich da noch einen Beitrag dazu. Erstmal reicht es für uns aber zu wissen, dass wenn wir eine Fallunterscheidung nach Klassen machen möchten, wir die Klassennamen in die when-Klauseln schreiben und das zu testende Objekt in die case-Klausel.


Du kannst in die when-Klausel keine AND-Bedingungen hängen. Wenn Du ein && reinschreibst, dann wird erst der Term an sich ausgewertet und die Rückgabe davon (true, false) dann mit dem zu testenden Objekt verglichen, was natürlich für dieses Script keinen Sinn macht. Tatsächlich setzt Du ja auch etwas spät an. Sinnvoller wäre es, direkt wenn man sich die Items holt, alle key_items nicht weiter zu betrachten, also irgendwo zwischen Zeile 16 und 17 müsste dieses Kriterium angewendet werden.

Eine Möglichkeit ist da, ganz simpel alle Objekte rauszuwerfen, die zum einen Items sind und zum anderen die Bedingung key_item erfüllen.

Ruby Quellcode

16
17
    items = $game_party.all_items
    items = items.delete_if{ |i| i.is_a?(RPG::Item) and i.key_item? }


Anschließend sollte das Script schonmal das tun, was Du dir vorstellst.

Redclaw

Rekrut

  • »Redclaw« ist der Autor dieses Themas

Motto: Beat down your enemy, you are your only true enemy!

  • Nachricht senden

3

Freitag, 17. Februar 2017, 18:51

Vielen Dank Playm, ich habe das Skript jetzt entsprechend angepasst und jetzt funktioniert es, du bist super :D
Ich habe mir gerade überlegt, ob ich daraus direkt ein komplettes Skript mache, dann könnte ich das mit den Variablen weglassen aber dafür direkt wenn der angegebene Skill von einem Gegner ausgeführt wird, ein Item aus dem Inventar bestimmt und entfernt wird. Das wäre sicher eleganter, ich bin nämlich nicht gerade ein Fan von Event/Script Mischungen wenn es nicht unbedingt sein muss.

Zu der Funktionsweise von dem Case-Statement würde ich gerne mehr erfahren, wenn dir das nicht zu viel Aufwand zum erklären gibt ;)
Signatur folgt...

4

Samstag, 18. Februar 2017, 00:27

Warum nicht. Fehlt doch eigentlich nurnoch eine Methode, die rnd_partyitem aufruft und das zurückgegebene Item (wenn es eins gibt) dann der Gruppe wegnimmt.

case-Statement


In Ruby sind alles Objekte. Das ist Teil des Sprachkonzepts von Ruby. Alles drumherum, was wir benutzen wie

Ruby Quellcode

1
2
3
4
5
class ComparisonObject
  def say_hi
    print( "ComparisonObject: Hi!" )
  end
end
ist nur syntaktischer Zucker. Man kann das auch dank Metaprogrammierung so schreiben:

Ruby Quellcode

1
2
ComparisonObject = Class.new
ComparisonObject.send(:define_method, :say_hi){ print( "ComparisonObject: Hi!" ) }


Aber das ist halt unnötig. Sollange man seinen Code nicht möglichst obfuscaten möchte, gibt es keinen Grund das so zu machen. Man nimmt direkt die hübsche Variante, wo man einen class-Block öffnet und da dann gut lesbar seine Klasse definiert.

Auch sind Operatoren zwischen Objekten wie + eigentlich Methoden. Die Klasse String definiert zum Beispiel die Methode + als Konkatenation von Strings.

Mit dieser Idee im Kopf wollen wir jetzt mal auf einen case-Block gucken:

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
    direction = 4
    case direction
    when 2
      msgbox( 'DOWN' )
    when 4
      msgbox( 'LEFT' )
    when 6
      msgbox( 'RIGHT' )
    when 8
      msgbox( 'UP' )
    end 

Was passiert hier? Auch hier werden im Hintergrund einfach Methoden von Objekten aufgerufen und zwar wird für jede when-Klausel die Methode === aufgerufen. Man kann das ganze schreiben als:

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
  direction = 4
  if 2 === direction
    msgbox( 'DOWN' )
  end
  if 4 === direction
    msgbox( 'LEFT' )
  end
  if 6 === direction
    msgbox( 'RIGHT' )
  end
  if 8 === direction
    msgbox( 'UP' )
  end

Wichtig hierbei: Die Methode === ist nicht kommutativ:

Ruby Quellcode

1
2
Integer === 5 #=> true
5 === Integer #=> false


Die Methode === ist für Klassen so definiert, dass geguckt wird, ob das zu vergleichende Objekt eine Instanz dieser Klasse oder einer Subklasse ist. (Class#===). Deswegen klappte es bei deinem Beispiel das Item hinzuschreiben und in die when-Klauseln die Klassen.
Wie ein case-Block also tatsächlich ausgewertet wird ist nicht magisch - man kann es sehr genau steuern, indem die Vergleichsobjekte in den when-Klauseln ihre case-equal Methode === auf bestimmte Art und Weise definieren. Die Zahl 5 ist eine Fixnum (Subklasse von Integer) und definiert ihre ===-Methode so, dass geguckt wird, ob das andere Objekt den selben Wert hat (Fixnum#===). So kommen dann auch die beiden unterschiedlichen Ergebnisse vom Beispiel oben raus: Es werden einfach andere Kriterien beim Vergleich benutzt.

Fazit: Ruby gibt einem sehr viele Möglichkeiten seinen Code hübsch und vor allem lesbar aufzuschreiben. Dabei passiert aber nie etwas magisches zwischen Objekten, sondern man kann das alles auf Methodenaufrufe zurückführen und durch (um-)definieren von Methoden beliebig steuern.

Redclaw

Rekrut

  • »Redclaw« ist der Autor dieses Themas

Motto: Beat down your enemy, you are your only true enemy!

  • Nachricht senden

5

Dienstag, 21. Februar 2017, 17:05

Hey danke noch für deine Erklärung Playm das hat mir echt geholfen auch wenn
ich es mir zweimal durchlesen musste bis ich es verstanden habe haha XD
:D.
Und entschuldige die späte Antwort, war leider paar Tage aus
gesundheitlichen Gründen ausser Gefecht, jetzt kann ich aber endlich
wieder an meinem Projekt weiterarbeiten :3.
Signatur folgt...

Ähnliche Themen

Social Bookmarks