Die Möglichkeit, in Lingo objektorientiert zu programmieren gibt es in der jetzigen Form seit Director 5. Allerdings kommen Objektskripte in der Praxis nicht oft zum Einsatz: Director wird zum Großteil von Multimedia-Autoren, nicht von gelernten Informatikern programmiert, und die schätzen an Director vor allem die visuelle Arbeitsweise. In Programmiersprachen ohne ein visuelles Interface, z.B. Java oder C++, wird dort, wo in Lingo das Drehbuch das Hauptwerkzeug ist, auf Skripting gesetzt, d.h. Animationen werden in einer externen Datei oder internen Ressource definiert, häufig in von Menschen lesbarem Textformat. Diese Möglichkeit bietet Director natürlich auch; aus einer Text-Datei bzw. einem Textdarsteller können z.B. die Eigenschaften bestimmter Variablen oder Objekte generiert werden. Das Einlesen dieser Dateien wird auch als Parsing bezeichnet. Diesen Parser, also der Programmteil, der das Auslesen übernimmt, kann man entweder selbst schreiben oder den seit Director 7 mitgelieferten XML-Parser benutzen. XML ist ein flexible Datenbeschreibungsformat, das HTML, also dem Datenbeschreibungsformat des World Wide Web ähnelt. Auf XML näher einzugehen, würde den Rahmen dieser Arbeit sprengen. Wenn man jedoch auf das schnelle Auslesen größerer, mehr oder weniger abstrakter Datenmengen angewiesen ist, ist XML für Director die richtige Wahl.
XML kann so aussehen, wie dieser Ausschnitt einer XML Datei aus einem Phantombildgenerator-Programm. Es definiert die verfügbaren Einzelteile, ihren Zusammenhang, ihre Tiefeninformation etc.
<?xml version="1.0"?>
<PHANTOMBILDER>
<SET NAME="Mensch">
<!-- Augen -->
<ELEMENT>
<MEMBER>A-augen1</MEMBER>
<Z-INDEX>80</Z-INDEX>
<GRUPPE>Augen</GRUPPE>
<DEFAULTLOC X="-50" Y="27"/>
<BRUDER>
<MEMBER>B-augen1</MEMBER>
<Z-INDEX>80</Z-INDEX>
<DEFAULTLOC X="51" Y="26"/>
</BRUDER>
<EXKLUSIV/>
<AUTOSCALE/>
<AUTOROTATE/>
</ELEMENT>
<ELEMENT>
<MEMBER>A-augen2</MEMBER>
<Z-INDEX>80</Z-INDEX>
<GRUPPE>Augen</GRUPPE>
<DEFAULTLOC X="-45" Y="19"/>
<BRUDER>
<MEMBER>B-augen2</MEMBER>
<Z-INDEX>80</Z-INDEX>
<DEFAULTLOC X="46" Y="18"/>
</BRUDER>
<EXKLUSIV/>
<AUTOSCALE/>
<AUTOROTATE/>
</ELEMENT>
...
Wenn immer möglich ist in Director die Montage von Layouts und Animationen
im Drehbuch dem Skripting vorzuziehen. Die Schnittstelle zwischen Drehbuch und
Skript stellen die Verhalten dar: sie kommunizieren Interaktion an das Programm
weiter. Verhalten sind Objekte, die genau wie Parent-Skripte eingesetzt werden
können. Ihr entscheidender Nachteil ist jedoch ihre Anonymität: Sie
werden vom Drehbuch generiert und lassen sich nur umständlich direkt ansprechen.
Child-Objekte sind dagegen direkt über eine Referenzvariable mit aussagekräftigem
Namen zugänglich. Sie eignen sich generell besser für die Sammlung
von Daten und Funktionen. Verhalten dagegen sind Spezialisten in der Verarbeitung
von Ereignissen. Es liegt daher nahe, beide zu koppeln: die Ereignisse werden
von den Verhalten verarbeitet und diese wiederum erzeugen Objekte bzw. geben
Werte an Objekte weiter. Zum Beispiel könnte ein Verhalten auf einer Spielplangrafik
am Programmanfang die Spritenummer und die Dimensionen des Spielplans an ein
Spielplanobjekt kommunizieren; diese Werte müssten nicht hart kodiert werden
und bei einem Wechsel der Größe des Spielfelds oder der Verschiebung
um einige Spritekanäle müsste keine Zeile Programmcode geändert
werden.
Das folgende Beispiel soll die Vorteile dieser Vorgehensweise verdeutlichen.
Das Parent-Skript BallObjekt bewegt ein Sprite auf der Bühne auf einer geraden Bahn; wenn es am Rand eines Rechtecks angekommen ist, prallt es an der jeweiligen Kante ab. Dieses Rechteck ist mit festen Werten programmiert; der Abprall-Algorithmus ist nicht sonderlich ausgefeilt, es wird lediglich die Bewegungsrichtung umgekehrt, doch für dieses Beispiel ist dies ausreichend. Als Parameter sind das Sprite zu übergeben und die anfängliche Richtung als Bewegungsvektor in Punktform (point(dx, dy)). Da die Bewegung in einem stepframe-Handler stattfindet, ist das Objekt der actorlist hinzuzufügen.
property mSprite, mBewegungsvektor
on new me, spriteNummer, anfangsVektor
-- Sprite-Kanal
mSprite = spriteNummer
-- anfänglicher Bewegungsvektor: Werte aus Behavior einlesen
mBewegungsvektor = anfangsVektor
return me
end new
on stepframe me
newLoc = me.calculateNewLoc()
if (newLoc.locH < 75) or (newLoc.locH > 515) then
-- Ball prallt von senkrechter Linie ab
mBewegungsvektor = mBewegungsvektor * point(-1, 1)
newLoc = me.calculateNewLoc()
end if
if (newLoc.locV < 75) or (newLoc.locV > 355) then
-- Ball prallt von waagerechter Linie ab
mBewegungsvektor = mBewegungsvektor * point(1, -1)
newLoc = me.calculateNewLoc()
end if
sprite(mSprite).loc = newLoc
end stepframe
on calculateNewLoc me
return (sprite(mSprite).loc + mBewegungsvektor)
end calculateNewLoc
Das Erzeugen der Objekte kann in einem startMovie-Handler
stattfinden:
on startMovie
clearGlobals
(the actorList).add(script("BallObjekt", 1, 30, 40)
(the actorList).add(script("BallObjekt", 2, 10, -10)
(the actorList).add(script("BallObjekt", 3, -1, -4)
end startMovie
Hier werden 3 Objekte erzeugt, die Sprite 1 bis 3 bewegen. Der clearGlobals-Befehl hat die Funktion, die actorList von eventuell aus einem vorherigen Programmablauf erzeugten Objekten zu befreien.
Mit dieser Vorgehensweise haben wird jedoch das Problem, dass bei jeder Verschiebung eines Sprites das Skript aktualisiert werden muss und ebenso, wenn ein neues Sprite hinzugefügt wird.
Das BallVerhalten schafft hier Abhilfe: es erzeugt selbständig ein neues Objekt mit dem richtigen Sprite und entfernt es, sobald das Sprite endet (das schließt das Beenden des Films mit ein). Das Hinzufügen eines neuen Balles ist denkbar einfach: das Verhalten wird einfach auf den Darsteller auf der Bühne gezogen. Als Dreingabe kann der Anfangsbewegungsvektor komfortabel in einer Dialogbox mithilfe von Schiebereglern eingestellt werden.
property spriteNum, mObjekt, mSpeedX, mSpeedY
on beginSprite me
mObjekt = script("BallObjekt").new (spriteNum, point(mSpeedX, mSpeedY))
(the actorlist).add(mObjekt)
end beginSprite
on endSprite me
(the actorlist).deleteOne(mObjekt)
end endSprite
on getPropertyDescriptionList me
description = [:]
addProp description,#mSpeedX,[#default:0,
#format:#integer,#comment:"horizontale Geschwindigkeit:",
#range:[#min:-20, #max: 20]]
addProp description,#mSpeedY,[#default:0,
#format:#integer,#comment:"vertikale Geschwindigkeit:",
#range:[#min:-20, #max: 20]]
return description
end getPropertyDescriptionList
on getBehaviorDescription
me
return "Behaviour für den springenden Ball."
end getBehaviorDescription
Natürlich wäre es auch möglich, die gesamte Funktion in einem einzigen Verhalten zu vereinen. Das würde das Programm jedoch unnötig verkomplizieren. Objektorientierte Programmierung bietet die Möglichkeit der Abstraktion, und das Aufteilen in eine Ereignisebene und eine Funktionsebene stellt ebene eine solche dar.
5.10 Ermitteln der Verhalten zu einem Sprite mit the scriptInstanceList und the scriptList | 5.12 Beispiel 10: Erweiterung des Ball-Beispiels |