workshop11

Top  Zurück  Weiter

Workshop 11: Pointer

Ein Pointer speichert einen Verweis auf ein Objekt (ist sozusagen ein Zeiger, der auf ein Objekt hinweist). Stellen wir uns die folgende Situation vor: Sie haben vor, mit lite-C einen "Pong"-Klon zu programmieren:

w11_01

Sie wollen, dass der linke Schläger vom Player kontrolliert wird und der rechte soll vom Computer gesteuert werden. Das Problem ist nun, wie drücken Sie in Ihrem Code aus wer der Player und wer der Computer ist? Die Antwort ist einfach: Sie erstellen einen Pointer, der lediglich ein Verweis auf ein anderes Objekt ist. Dies ist genau die Methode, mit der das Pong Multiplayer-Spiel in Ihrem samples Ordner seine Schläger bewegt.

Eine typische Pointer-Definition sieht so aus:

ENTITY* the_player;

oder

STRING* my_name;

oder

PANEL* game_over;

oder

BMAP* health_picture;

oder sogar

var* my_number;

Gut, eine Pointer-Definition sieht also ähnlich aus wie eine Objektdefinition, nur ohne Objekt-Inhalt. Wir fügen dem Objekt-Typen einfach einen Stern "*" hinzu und unser Pointer ist einsatzbereit. Wir können einen Pointer auf jedes Objekt, jeden String, jeden Panel etc. "zeigen" lassen und dann können können wir mit dem Pointer ganz so umgehen, wie wir das Objekt, den String oder das Panel selbst behandeln würden.

Ich merke, Ihnen wird jetzt ein wenig schwindlig, also machen wir am besten gleich ein Beispiel: starten Sie Lite-C und öffnen Sie im Ordner workshop11 das script11 und starten Sie es gleich. Sie sehen ein Haus mit zwei kleinen Figuren davor.

w11_04

Oh, wie nett, die Zwillings-Zauberbrüder sind in rot und blau! Sie machen überhaupt rein gar nichts aber das werden wir nun ändern. Drücken Sie auf Ihrer Tastatur die [Tab]-Taste und tippen Sie die folgende Zeile ein:

wizard.x = 300;

Der rote Zauberer ist viel näher zur Kamera herangekommen!

w11_05

Aber warum hat sich der rote Zauberer überhaupt bewegt? Warum haben sich nicht alle beide gleichzeitig bewegt? Und warum hat sich nicht zuletzt auch noch das Haus bewegt?

Ich fürchte, so viele Fragen kann ich nicht auf einmal klären und doch lassen sie sich mit einem Satz beantworten: Ich habe für den roten Zauberer einen Pointer benutzt. Sie sehen, wir leben in einer grausamen Welt. Diesen fiesen Computern sind wir Menschen völlig egal. Sie und ich wissen, wie dieses Zauberer-Modell aussieht aber der Computer hat keinerlei Interesse daran, das zu lernen. Wenn Sie einem Computer erklären, er solle das rote Zauberer-Modell bewegen, wird er sie auslachen. Er weiß nicht wer und wo der rote Zauberer ist und es ist ihm auch vollkommen Wurscht was er für eine Skinfarbe hat. Wenn Sie also wollen, dass eine Entity von Ihrem Computer eine Extrabehandlung bekommt, müssen Sie einen Pointer verwenden.

Werfen wir einen Blick in die Skriptdatei mit dem Namen script11. Der Hauptteil ist dieser:

////////////////////////////////////////////////////////////////////

ENTITY* wizard;

////////////////////////////////////////////////////////////////////

function main()
{
 level_load ("work11.wmb");
}

action wizard_with_pointer()
{
	wizard = my;
	my.ambient = 100;
}
		
action wizard_simple()
{
	my.ambient = 100;
}           
Gott sei dank! Das Skript sieht zum Glück gar nicht kompliziert aus!

Wir setzen die Bildschirmauflösung, wir laden ein Level namens work11.wmb und dann haben wir zwei Aktionen, die den beiden Zauberern mithilfe von GameStudios's Level-Editor (WED) zugewiesen wurden. Über alledem haben wir einen als "wizard" definierten Pointer. Betrachten wir uns diese Codezeile einmal genauer:

ENTITY* wizard;

Wir haben einen Pointer definiert, der von einer Entity verwendet werden wird (einen Entity-Pointer demnach) und wir haben ihn wizard genannt. Wir können hier auch irgendeinen anderen Namen nehmen, es spielt keinerlei Rolle wenn wir z. B. für einen Affen einen Pointer namens robot_12 nehmen. Nun untersuchen wir die Aktion mit dem Namen wizard_with_pointer:

action wizard_with_pointer()
{
   wizard = my;

   my.ambient = 100;
}

Wir haben einen Pointer mit dem Namen "wizard" definiert, richtig? Die erste Code-Zeile in der Aktion weist der Entity, welche mit dieser Aktion verknüpft ist, den Pointer zu. Mit anderen Worten: "wizard = my" bedeutet soviel wie 'Ich bin der Zauberer (wizard)! Ich, die Entity, der diese Aktion zugewiesen ist, bin von jetzt an als "wizard" bekannt!' Und wissen Sie was? Die Aktion withard_with_pointer ist dem roten Zauberer-Modell zugewiesen und daher wird das rote Modell zu "wizard" und kann per Punkt-Methode gesteuert werden. Und genau das haben wir ja in der Konsole mit unserer Codezeile "wizard.x = 300;" gemacht.

Die zweite Code-Zeile der Aktion setzt den Ambient für den roten Zauberer auf 100 was ihn heller erscheinen lässt. Neugierig, den Unterschied zu sehen? Werfen Sie einen Blick auf die Abbildung unten:

w11_08

Die zweite Aktion verwendet keinen Pointer, alles was sie macht, ist den Ambient des Modells, dem sie zugewiesen ist, zu erhöhen.

action wizard_simple
{
     my.ambient = 100;
}

Sie haben es wieder erraten... diese Aktion ist mit dem blauen Modell verknüpft. Oh und ich habe vergessen zu sagen, dass auch "my" ein Pointer ist! "My" ist ein vordefinierter Pointer, daher konnten wir uns seine Definition sparen. Zerbrechen Sie sich jetzt nicht den Kopf, am Ende des Workshops besprechen wir das noch etwas eingehender.

Ok, wie erstellen wir also Pointer? Es sind zwei Schritte:

1) Wir definieren den Pointer in einer einzigen, kurzen Code-Zeile:

ENTITY* horse; // definiere einen Entity-Pointer

2) Indem wir innerhalb der uns interessierenden Aktion eine Zeile mit dem entsprechenden Namen einfügen, sagen wir dem Pointer wer sein künftiger Besitzer ist:

action players_horse
{
    horse = my;
    ...
}

Von nun an können Sie die Entity, der die Aktion players_horse zugewiesen ist, mithilfe der Punkt-Methode kontrollieren: object.eigenschaft:

Spielen wir doch noch ein wenig mit unserem Testlevel! Starten Sie es wieder und geben Sie diese Code-Zeile ein:

wizard.z = 100;

w11_09

Der Zauberer hat doch tatsächlich seine Höhe verändert! Sie können mit seinen x-, y- z-, Werten herumspielen oder pan, tilt, roll ausprobieren...

Die gute Nachricht ist die, dass Sie die Eigenschaften des roten Zaubermeisters aus jeder anderen Aktion oder Funktion Ihres Skripts heraus verändern können. Kopieren Sie den folgenden Code und fügen Sie ihn ganz oben in Ihre main-Funktion ein.

function move_up()
{
   wizard.z += 5; // addiere jedes mal, wenn diese Funktion aufgerufen wird 5 Quants auf die Höhe von wizard
}

Fügen Sie nun die folgende Codezeile in die Main-Funktion ein:

on_u = move_up;

Das überarbeitete Skript sollte nun so aussehen:

////////////////////////////////////////////////////////////////////

ENTITY* wizard;

////////////////////////////////////////////////////////////////////

function move_up()
{
	wizard.z += 5;
}

function main()
{
	level_load ("work11.wmb");
	on_u = move_up;
}

action wizard_with_pointer()
{
	wizard = me;
	my.ambient = 100;
}

action wizard_simple()
{
	my.ambient = 100;
}

Sie sehen, dass die Funktion mit dem Namen move_up, die Höhe (z) des Zauberers um 5 Einheiten erhöht. Lag seine Anfangshöhe z.B. bei 15 Quants, wird ihn diese Funktion nun auf 20 setzen usw. Diese Code-Zeile aber sieht jetzt seltsam aus:

on_u = move_up;

Nun, on_u bedeutet "wenn der Player auf der Tastatur "U" drückt" und move_up ist der Name der Funktion, der ohne Klammern geschrieben wird und die immer dann, wenn jemand die "U"-Taste betätigt, ausgeführt wird (Ob "U" oder "u" ist dabei egal). Das Ganze hat zur Folge, dass die Funktion "move_up" der Höhe des roten Zaubermanns mit jedem Tastendruck auf "U" weitere 5 Quants hinzufügt.

Viele Anfänger fügen versehentlich Klammern hinzu, wenn sie Funktionen Tasten zuweisen. Tun Sie das nicht! Wenn Sie "on_u = move_up( );" schreiben, wird die Funktion ausgeführt statt zugewiesen, und Drücken der Taste "U" produziert dann eine Fehlermeldung.

Zurück zum Skript: speichern Sie es, starten Sie es und drücken Sie, um dem Zauberer nach oben zu verhelfen, ein paar mal auf die "U"-Taste. Es ist offensichtlich, dass die Funktion namens move_up weiß, wer "wizard" ist.

Noch eine letzte Bemerkung: der wichtigste von der Engine benutzte Pointer ist "my". Kehren wir noch einmal zu der dem blauen Zauber-Modell zugewiesenen Aktion zurück:

action wizard_simple()
{
  my.ambient = 100;
}

Die Code-Zeile innerhalb der Aktion setzt den Ambient einer jeden Entity der sie zugewiesen wird, auf 100. "my.ambient = 100;" bedeutet: "Ich, die Entity, der diese Aktion zugewiesen ist, werde meinen Ambient auf 100 setzen". Verknüpfen Sie diese Aktion mit einem Sprite, so wird eben der Ambient des betreffenden Sprites auf 100 gesetzt und wenn Sie sie einer .wmb-Entity zuweisen, dann wird deren Ambient 100. "My" ist also ein universeller Pointer und jede Entity kann ihn verwenden, ohne ihn vorher definieren zu müssen. Außerhalb der Aktion bzw. Funktion, die ihn beinhaltet, ist er allerdings unbekannt. Wenn Sie es in der Konsole z. B. damit versuchen:

my.z = 100;

wird es nicht funktionieren, denn "my" ist außerhalb seiner Aktion respektive Funktion unbekannt.
 

Weiter: If - Else- Verzweigung


Mehr zum Thema: Gamestudio Handbuch ► Pointer