RGSS für Anfänger/Funktionen und Klassen

Aus Scientia
Wechseln zu: Navigation, Suche
RGSS für Anfänger Schwierigkeitsgrad Stern.pngSchwierigkeitsgrad Stern.png
Autor Ranmaru-kun
Thematik RGSS
Vorraussetzungen Programme: keine

Fähigkeiten: keine

Andere Teile

Funktionen

Der eigentliche Kern von Ruby und dem RGSS sind die Klassen. Mit denen funktioniert eigentlich das gesamte Programm. Da wir hier objektorientiert programmieren, sind Klassen die Basis von allem. Anschaulich erklärt ist ein Objekt eine einzelne Instanz einer Klasse und eine Klasse ist ein Datentyp, der verschiedene Werte haben kann und mit anderen Klassen interagieren kann. Um diese Interaktionen zu ermöglichen, gibt es Funktionen. Die heißen im englischen Ruby methods, aber "Methoden" klingt so beschissen, dann sag ich hier lieber Funktionen. Eine einfache Funktion sieht in Ruby so aus:

Aufbau

 def foo() 
   p "Hello World." 
 end

Die Funktion foo macht genau das, was zwischen def und end steht, also in diesem Fall "Hello World." ausgeben. Diese Funktion kann ich jetzt nach Belieben aufrufen.

 foo

Immer, wenn ich im Code foo schreibe, wird die Funktion foo aufgerufen. Im Prinzip ist alles eine Funktion, auch p (print). Die ist nur schon vorher eingebaut und muß nicht mehr von uns programmiert werden. Die zwei Klammern nach dem Funktionsnamen geben die Parameter an, welche die Funktion braucht, um zu funktionieren. In diesem Fall gar keine. Wenn man eine Funktion schreibt, die zum Beispiel rechnen soll, dann braucht man die aber. Schreiben wir als Beispiel einen einfachen Schadensalgorithmus für ein Kampfsystem.

Parameterübergabe

 def berechneSchaden(heldstr, monstercon) 
   return ((heldstr * 100) - (monstercon * 30) )
 end 
 schaden = berechneSchaden(50,40); p schaden # Ausgabe: 3800

Gehen wir den Code durch: zuerst wird die Funktion berechneSchaden definiert und es wird gesagt, daß sie zwei Parameter braucht, um zu funktionieren. Die Stärke des Helden und die Konstitution des Monsters. Dann wird der Schaden berechnet und mit dem Befehl return zurückgegeben. Und am Ende können wir sie dann außerhalb der Funktion weiterverwenden.

Hash

Ein erweiterter Schadensalgorithmus mit Hashes könnte so aussehen:

 alex = {"Name"=>"Alex", "STR"=>50} 
 blob = {"Name"=>"Blob", "CON"=> 40} 
 def berechneSchaden(held, monster) 
   schaden = (held["STR"] * 100) - (monster["CON"] * 30) 
   return schaden 
 end 
 schaden = berechneSchaden(alex,blob) 
 p schaden # Ausgabe: 3800

Hier können wir zum Beispiel einfache Charakter-Hashes benutzen, um den Schaden zu berechnen. Dazu wären nicht mehr so viele Variablen nötig, wie beispielsweise im RM2k und man braucht im Kampfsystem nur noch die Namen anzugeben, um zu berechnen. Die Hashes, die ich hier benutzt habe, sind natürlich sehr rudimentär, um es zu vereinfachen.

Klassen

Kommen wir nun zu den Klassen. Klassen sind, wie oben gesagt, Datentypen, welche Werte haben und interagieren können. Dazu benutzen diese Klassen solche Funktionen, wie wir sie oben gesehen haben. Eine Klasse könnten zum Beispiel die spielbaren Helden sein. Der Name einer Klasse muß immer mit einem großen Buchstaben anfangen!

 class SpielbareHelden 
   def initialize(name, alter, waffe, str=50, con=40)
     #Wenn str und con nicht übergeben werden, werden die Standartwerte genommen.
     @name = name 
     @alter = alter 
     @waffe = waffe 
     @str = str 
     @con = con 
   end 
   def info 
     p "Der Held heißt " + @name + ", ist #{@alter} Jahre alt und kämpft mit " + @waffe + "n." 
   end 
   def werte 
     p "Stärke: #{@str}, Konstitution: #{@con}" 
   end 
 end

Zunächst sollte jede Klasse eine initialize() Funktion haben. Diese Funktion wird dann aufgerufen, wenn ein neues Objekt angelegt wird. Alle Funktionen danach sind optional und für die Objekte nachher zu gebrauchen. Soll sie als Scene arbeiten, braucht sie auch eine main funktion. Die Variablen mit dem @ davor sind Instanzvariablen, welche nur innerhalb der Funktionen der Klasse verwendet werden können, es sei denn, man verwendet attr_accessor, aber dazu später mehr.

Zunächst legen wir mal einen neuen Helden mit Namen Alex an.

 alex = SpielbareHelden.new("Alex", 18, "Schwerter", 50)
 #Oder
 alex = SpielbareHelden.new("Alex", 18, "Schwerter", 50, 40)

Dies ruft die initialize() Funktion auf und speichert die Werte von Alex in das Objekt alex. Ab jetzt kann das Objekt alle Funktionen der Klasse verwenden.

Funktionen aufrufen

 alex.info # Ausgabe: Der Held heißt Alex, ist 18 Jahre alt und kämpft mit Schwertern. 
 alex.werte # Ausgabe: Stärke: 50, Konstitution: 40

Klassen sind ungemein praktisch, um gewisse Funktionen zu machen. So könnte man zum Beispiel den oben geschriebenen Schadensalgorithmus als Klassenfunktion für jeden Helden einbauen, um direkt darauf zugreifen zu können. Um Werte der Instanzariablen ändern zu können, verwenden wir den attr_accessor, der bei Ruby gleich dabei ist.

attr_accessor/attr_reader

 class SpielbareHelden 
   def initialize(name, level, str, con) 
     @name = name 
     @level = level 
     @str = str 
     @con = con 
   end 
   attr_accessor :level 
   attr_accessor :str 
   attr_accessor :con 
   attr_reader   :name
   def werte 
     p "LEVEL #{@level}: Stärke: #{@str}, Konstitution: #{@con}" 
   end 
 end

Jetzt können wir einen Helden anlegen, dessen Werte variabel sind. So kann man Levelaufstiege programmieren. Der Name braucht keinen attr_accessor, weil der nicht mehr verändert werden kann.

  • attr_accessor = Von Außen lesbar und beschreibar
  • attr_reader = Von außen lesbar
 alex = SpielbareHelden.new("Alex", 10, 50, 40) 
 alex.werte # Ausgabe: LEVEL 10: Stärke: 50, Konstitution: 40 
 alex.level += 1 
 alex.str += 10 
 alex.con += 8 
 alex.werte # Ausgabe: LEVEL 11: Stärke: 60, Konstitution: 48

Die Variablen, die den attr_accessor haben, werden also mit objekt.variable angesprochen. Ebenso werden die Klassenfunktionen mit objekt.funktion angesprochen. Mit Ausnahme der initialize() Funktion, die mit objekt = klasse.new angesprochen wird. Ganz einfach eigentlich.

Superklassen/Tochterklassen

Zuletzt noch eine Sonderart von Klassen: sogenannte Tochterklassen. Diese Klassen "erben" die Funktionen und Werte ihrer übergeordneten Klasse, welche in Ruby Superklasse genannt wird. Eine Tochterklasse legt man folgendermaßen an:

 class Foo 
   def initialize(name) 
     @name = name 
   end 
   def ausgabe 
     return "Hallo " + @name + "!" 
   end 
 end
 class Bar < Foo 
 end
 
 
 
 bla = Foo.new("Alex") 
 text1 = bla.ausgabe 
 p text1 # Ausgabe: Hallo Alex! 
 blubb = Bar.new("Hugo") 
 text2 = blubb.ausgabe 
 p text2 # Ausgabe: Hallo Hugo!

In diesem Fall würde die Klasse bar ohne Veränderungen alle Funktionen und Werte der Klasse foo erben. Wozu aber dann eine Tochterklasse anlegen? Nun, der Grund ist, daß die Tochterklasse die vererbten Dinge überschreiben kann. Das heißt im Klartext, daß ich eine Klasse kopieren kann und dann verschiedene Dinge ändern. Das ist dann praktisch, wenn ich als fauler Programmierer nicht nochmal die ganze Chose abtippen will.

Super

 class Foo 
   def initialize(name) 
     @name = name 
   end 
   def ausgabe 
     return "Hallo " + @name + "!" 
   end 
 end 
 class Bar < Foo 
   def ausgabe 
     return super + " Du bist foobar!" 
   end 
 end
 
 bla = Foo.new("Alex") 
 text1 = bla.ausgabe 
 p text1 # Ausgabe: Hallo Alex! 
 blubb = Bar.new("Hugo") 
 text2 = blubb.ausgabe 
 p text2 # Ausgabe: Hallo Hugo! Du bist foobar!

Der Befehl return super bedeutet also, daß das zurückgegeben wird, was die Superklasse zurückgibt und im Fall der Klasse Bar wird noch etwas hintendrangehangen, nämlich der Text "Du bist foobar!".