kH_
%Europe/Berlin %810 %2005, 20:26
Ok, nou...dan zal ik nu even een enorm verhaal gaan opstellen inzake classes in het algemeen, waarna ik het verder zal toelichten specifiek voor AS 2.0 (ActionScript 2.0), aangezien AS 2.0 eindelijk een redelijk 'fully-fledged', dus vrijwel volledige object-georienteerde taal is, MITS correct gebruikt!
ActionScript 2.0 Classes in een notendop door kH ;)
Inleiding: Wat is een class?
Mensen vragen zich vaak af, wat is dat nu eigenlijk een class. Welnu, de Nederlandse benaming hiervoor is dus klasse en wat het nu precies is hoop ik in deze paragraaf uit te leggen.
Door de jaren heen is gebleken, dat mensen voor het begrijpen van complexe materie het fijn vinden om vergelijkingen vanuit het dagelijkse leven te zien. Dit is immers een wereld die wij allemaal redelijk begrijpen en daardoor is ons 'beeld' van de meeste standaard zaken ook gelijk.
Nu heb je in het dierenrijk bijvoorbeeld verschillende soorten dieren. We hebben de zoogdieren, reptielen en amfibieen bijvoorbeeld. Binnen de groep zoogdieren treffen we bijvoorbeeld weer olifanten aan, walvissen, paarden en ik weet niet wat nog meer.
Nu noemen we zo een groep dieren een 'klasse' van dieren. Als we bijvoorbeeld praten over een dier in het algemeen, dan hebben we het over een levend wezen met bijvoorbeeld de volgende eigenschappen:
- Kan zich voortbewegen
- Heeft een leeftijd
- Heeft een naam
- Kan sterven
Nu zijn deze eigenschappen voor IEDER dier aanwezig. Als we ALLE eigenschappen nu van een dier op papier zetten, dus eigenschappen die voor ALLE dieren gelden, dan hebben we een soort 'blauwdruk' gemaakt van een dier. We hebben dus een tekstuele beschrijving gemaakt van een dier. Dat is nou precies wat een klasse is, simpelweg een blauwdruk.
Maar nu komt een belangrijke vraag: "Ja ok, dat is allemaal leuk een aardig, maar een olifant heeft bijvoorbeeld wel een slurf, maar een zebra niet....hoe leg je dat dan vast?"
Dat is zeker een goede vraag en om dit uit te leggen komen we op het concept 'sub-klasse'.
Nu is het duidelijk dat er voor wat betreft dieren de klasse 'dier' de minimale eigenschappen bevat. Dit is dan ook de 'hoofd' klasse, ook wel 'root-class' genoemd. Ieder dier 'overerft' zogezegd alle eigenschappen van de klasse 'dier'. Overerven is simpelweg het 'in zich hebben', of 'verkrijgen' van de eigenschappen van hetgeen waarvan overerfd wordt. De Engelse benaming voor dit overerven wordt 'to inherit' genoemd.
Maar nu weer even bij het verhaal blijven, hoe leg je nu de eigenschappen van een olifant vast? Dat doe je niet in de klasse 'dier' natuurlijk, aangezien je dan bijvoorbeeld zou zeggen, dat een slang ook een slurf heeft en dat is natuurlijk niet zo.
Nee, je maakt simpelweg een NIEUWE klasse aan met de naam 'olifant' en die laat je overerven van de klasse 'dier'. Dus de klasse 'olifant' neemt ALLE eigenschappen van de klasse 'dier' over.
De eigenschappen die echter UNIEK zijn voor een olifant, die leggen we apart vast in de klasse 'olifant'. De klasse olifant kan er dan bijvoorbeeld als volgt uitzien:
Is een dier
- Heeft een slurf
- Heeft grote oren
Maar natuurlijk heeft de olifant ook alle eigenschappen uit de klasse 'dier'. Die schrijf je echter niet op, aangezien je dat aangeeft met de zin 'is een dier'. Een VOLLEDIGE beschrijving van een olifant (volledig in het kader van deze tekst in ieder geval) ziet er dus als volgt uit:
- Kan zich voortbewegen
- Heeft een leeftijd
- Heeft een naam
- Kan sterven
- Heeft een slurf
- Heeft grote oren
Nu is de zin 'is een dier' ENORM belangrijk, want je geeft hiermee aan, dat een olifant beschouwd kan worden als een dier. Dat is ook zo, aangezien een olifant alle eigenschappen van een dier in zich heeft. Andersom is echter NIET het geval!! Een dier is niet altijd een olifant!! Niet ieder dier heeft immers een slurf!
Dit lijkt misschien logisch, maar het is een ontzettend belangrijk punt wat je straks zult zien wanneer ik specifiek zal ingaan op ActionScript 2.0.
Goed, ik denk dat nu goed duidelijk is geworden wat nu precies een 'klasse' is. Ik had het echter ook over een 'sub-klasse'. Welnu, de klasse olifant is een voorbeeld van een sub-klasse. Het is namelijk een sub-klasse van de klasse dier. Het is immers een beest in 'de orde der dieren'. We zeggen dan ook, dat 'dier' de basis-klasse (base-class) is voor de klasse olifant. Voor IEDER dier is het zelfs zo, dat de klasse 'dier' de 'hoofd-klasse' is. Deze klasse staat immers helemaal aan de top.
Aangezien een plaatje meer zegt dan duizend woorden, heb ik hieronder een illustratie opgenomen om deze hierarchische verdeling in het dierenrijk duidelijk te maken. Daar waar een pijl naar boven gericht staat betekent dit niets anders dan 'overerft van', oftewel 'is een':
http://n.domaindlx.com/kaHu/oo.PNG
Maar nu iets belangrijkers! Ok, we hebben een klasse olifant, maar dat is dus in principe geen levend wezen. Nee, dat is correct! Een klasse is een beschrijving van iets, dus een concept.
Maar wat nu als ik die olifant Dikke Bertha ergens in Zuid-Afrika wil beschrijven? Nou, dan vullen we dus voor iedere eigenschap van de 'klasse' olifant een waarde in, waardoor er een beschrijving wordt gegeven van de specifieke olifant Dikke Bertha. En DAT noemen we nu een object! Het aanmaken van een object (concrete waarden toekennen aan de eigenschappen) noemen we ook wel het 'instantieren' van een klasse. Ok, laten wij eens een instantie maken van de klasse olifant, zodat we een beschrijving hebben van Dikke Bertha. Daarvoor moeten we echter eerst kijken of olifant wel de hoofdklasse is, zo nee, dan gaan we eerst helemaal terug naar de hoofdklasse, vanaf waar we waarden gaan invullen. Welnu, in ons voorbeeld (niet de figuur die ik gegeven heb!) was de hoofdklasse van olifant de klasse 'dier'. Ok, laten we nu even concrete waarden invullen:
- Kan zich voortbewegen -> GEEN WAARDE!!
- Heeft een leeftijd -> leeftijd is 30
- Heeft een naam -> naam is Dikke Bertha
- Kan sterven -> GEEN WAARDE!!
- Heeft een slurf -> Vullen we niet in, puur voor illustratie! Hier zie je het verschil met de werkelijkheid en de virtuele programmeer wereld. Het is zinloos om dit op te slaan. Straks zullen we zien hoe dit gaat werken.
- Heeft grote oren -> Vullen we niet in, puur voor illustratie (idem als hierboven)
We hebben nu dus voor twee eigenschappen een waarde ingevuld, MAAR...we zien ook twee eigenschappen, waarvoor geen waarde ingevuld kan worden. We zouden 'kan sterven' kunnen zien als een Ja/Nee waarde, dus Ja invullen wanneer Dikke Bertha dood is. Dat bedoel en we er echter niet mee. 'Kan zich voortbewegen' is een volledige beschrijving van HOE een olifant zich voortbeweegt. Deze beschrijving wordt dan ook NIET specifiek voor een object ingevuld, maar is een beschrijving in de klasse zelf. Ik moet nu even in programmeertermen gaan praten om dit duidelijk te krijgen. De eigenschap zoals 'Kan zich voortbewegen' is een 'class-method', ofwel 'klasse-methode', ofwel 'klasse-operatie', dus een functie. De eigenschap 'Heeft een leeftijd' is een 'class-attribute', ofwel 'klasse-attribuut', dus een variabele.
We hebben nu dus een object Dikke Bertha gemaakt, ofwel een instantie van de klasse olifant.
Nu schept deze beschrijving genoeg informatie om aan de gang te kunnen met ActionScript 2.0, of welke object-georienteerde taal dan ook. Voordat ik het vergeet, een object-georienteerde taal is een programmeertaal, waarbij objecten geprogrammeerd worden en tevens de manier waarop zij met elkaar een interactie aangaan. Dat is dus het specifieke systeem dat gebouwd wordt: een veelheid aan objecten die op een programmatische wijze met elkaar samenwerken en van elkaar gebruik kunnen maken.
Classes in ActionScript 2.0
Ik spreek nu niet meer van klasse, maar class, aangezien programmeertalen vrijwel altijd een Engelse syntax (lees, standaard notatievorm) hebben. Dit is ook logisch, aangezien Engels een wereldtaal is en op deze manier programmacode door vrijwel iedereen gelezen kan worden.
We gaan nu eerst de notatievorm (syntax) van veel aspecten binnen ActionScript 2.0 taal, voor wat betreft classes behandelen.
De Class
Allereerst, laten we de class 'dier' nemen, maar laten we dan ook syntactisch correct zijn en alleen nog gebruik maken van Engelse termen, dus de class 'Animal'. Hoe ziet zo een class er binnen de AS 2.0 syntax uit?? Op deze manier:
class Animal
{
}
Leuk en aardig, we hebben nu dus echt een Animal class gemaakt. Laten we nu eens een daadwerkelijk 'object' gaan maken van deze class. Dat doen we als volgt:
var animSomeAnimal:Animal=new Animal();
Natuurlijk is dit pure nonsens, het heeft geen zin om een concreet dier te 'maken', aangezien dat niet eens bestaat. Ik heb in ieder geval nog nooit een 'dier' zien grazen in de wei :)
Waarschijnlijk ken je deze syntax wel toen je een instantie ging maken van een MovieClip:
var mc_myMovie:MovieClip=new MovieClip();
Hier maken we dus een instantie van een MovieClip class. Ok, laten we verder gaan en nu met de eigenschappen.
Properties en Methods
Properties
Binnen ActionScript 2.0 worden de zogenaamde variabelen binnen een klasse een 'property' genoemd en een functie wordt een 'method' genoemd. Als we over al deze eigenschappen binnen een class praten, dan hebben we het over de 'members' van de class, dus de leden van de class. Voor de duidelijkheid ga ik hier stap voor stap de class invullen, zodat het verhaal echt goed lopend blijft en de brug tussen werkelijkheid en de virtuele wereld niet te zwak wordt.
Binnen AS 2.0 kan een property van een class twee vormen aannemen voor wat betreft de 'beveiliging'. We kennen een zogenaamd 'public' property en een 'private' property. Laten we eerst eens kijken naar de 'kale' vorm, dus zonder deze zogenaamde 'access modifiers'. Wanneer deze modifiers NIET worden gebruikt is een property namelijk gewoon 'public'. Leer jezelf echter aan, om voor de code leesbaarheid en de consistentie ALTIJD een modifier te gebruiken, zodat duidelijk blijkt dat je nagedacht hebt over je keuze. Je hebt dus DAADWERKELIJK voor die modifier gekozen.
Maar nu even voor het gemak, de kale vorm:
var numAge:Number;
Hier leggen we dus vast dat er een property is met de naam 'numAge' en van het type 'Number'. Leer je dit opgeven van het type ECHT aan, aangezien je op deze manier echt alleen maar nummers in de property 'numAge' kunt zetten en de 'Check Syntax' ook echt een fout zal geven, wanneer je er bijvoorbeeld een tekst in probeert te zetten.
Zie je trouwens ook, dat de property voorafgegaan wordt van de naam 'num'? Dit is een puur een overweging voor de consistentie van programmeren. Als IEDEREEN een vaste naamgeving hanteert, dan snapt IEDEREEN tijdens het lezen van de code, dat bijvoorbeeld 'numAge' een Number is en dat hier dus ook alleen een nummer aan kan worden toegekend. Noem je de property bijvoorbeeld 'mandarijn83', dan snap je zelf wel hoe stom je aan het programmeren bent.
Nu even verder met de 'access modifiers'. Zogezegd, de bovenstaande notatievorm is hetzelfde als je gebruik maakt van de modifier 'public'. Gebruik dus ALTIJD een modifier, aangezien dit consistent is!!
Op naar de notatie:
public var numAge:Number;
En wat zegt dit public nu eigenlijk? Dit betekent dat je een waarde kunt toekennen aan de property, BUITEN de class waarin het gedefinieerd is. Laten we dat eens doen. Allereerst nemen we de beschrijving van de class met daarin deze property:
class Animal
{
public var numAge:Number;
}
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.numAge=12;
Hoewel syntactisch HELEMAAL correct, we leggen nu de inhoud van de class helemaal bloot aan de buitenwereld. Dat is nu het effect van de modifier public. Wil je dit? Het ligt aan de toepassing, maar volgens de regels van OO is dit op dit moment niet goed. Een class moet namelijk zoveel mogelijk worden gezien als een 'blackbox'. We weten wat we ermee kunnen doen, we weten ook hoe we ermee moeten praten, maar we weten NIET hoe het precies werkt. En DAT is de gouden regel die je jezelf altijd voor ogen moet houden, wanneer je OO systemen bouwt. Wijk hier alleen vanaf indien strikt noodzakelijk (doe het dus liever niet!). Wanneer het wel kan ligt eigenlijk buiten het bereik van deze basis omtrent ActionScript 2.0 en OO (object oriented) ontwikkeling in het algemeen.
Het is nu duidelijk dat we dit dus niet willen. Stel je voor dat je dit zou toelaten. Dit zou betekenen, dat iemand die gebruik maakt van jouw Animal class zomaar een leeftijd van 9999999 zou kunnen invullen. Slaat dat ergens op? Nee en misschien heb jij je class zo ontworpen dat hierdoor de grootst mogelijke problemen door optreden!
Het is even noemenswaardig om nogmaals aan te geven, dat 'var bla:Number;' gelijk is aan 'public var bla:Number;', wanneer we dit in een class definieren.
Ok het is nu tijd om onze beschermende vriend 'private' in de strijd de gooien. Wanneer we in plaats van 'public' het woordje 'private' gebruiken, DAN mag de door het woord 'private' beschermde property alleen maar ingesteld worden binnen de class, waarin het gedefinieerd werd EN eventuele sub-classes van de betreffende class.
Dus wat met 'public' wel kon, mag met 'private' weer niet. Goed, maar hoe kun je die property dan toch opvragen/wijzigen? Dat kan op twee manieren, namelijk via speciale 'accessor' en 'mutator' methods, OF via een elegantere manier, met behulp van speciale 'get' en 'set' methods. Laten we allereerst eens induiken op hoe een 'class-method' eruitziet, voordat we induiken op de meer complexe details.
Methods
Als voorbeeld nemen we de eigenschap 'Kan zich voortbewegen'. Laten we hier een method van maken met de naam 'doMove'. Voor methods gelden trouwens dezelfde regels voor wat betreft de 'access modifiers' public en private. Maak een method dus ook ALLEEN public, wanneer de method buiten de class aangeroepen moet worden. Ok, op naar de syntax:
class Animal
{
private var numAge:Number;
public function doMove():Void
{
trace("MOVING ANIMAL!");
}
}
We zien hier een simpele method, welke eenvoudigweg de tekst 'MOVING ANIMAL!' in de debug console weergeeft, wanneer we de method aanroepen. Tevens zien we achter de method het zogenaamde 'return type' staan. In dit geval is dit 'Void'. Met dit return type geven we aan, dat de method NIETS teruggeeft. Dus zet je bijvoorbeeld in de method 'return true;', dan krijg je een foutmelding, wanneer je je Movie gaat testen (zelfs al bij Check Syntax in het Actions pane, gebruik die optie!!).
GEBRUIK DAN OOK ALTIJD EEN RETURN TYPE!!!
Ik vind dit persoonlijk belangrijk, aangezien je hiermee je code nog steeds consistent maakt, maar nog belangrijker, je zorgt ervoor dat de controle op syntax nog steeds goed plaatsvindt. Daarom zeg ik ook, AS 2.0 is vrijwel een echte OO taal, MITS je het goed gebruikt!
Goed, hoe roepen we deze method nu aan dan?? Als volgt:
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.doMove();
Dit ken je waarschijnlijk al wel.
Ok, laten we verder gaan met het opgeven van parameters aan een method. Je ziet dat de 'doMove()' method geen parameters vraagt, de '()' zijn immers leeg. Laten we nu als parameter een 'Boolean' geven, waarmee we aangeven of het beest snel moet bewegen, of juist langzaam:
class Animal
{
private var numAge:Number;
public function doMove(bFast:Boolean):Void
{
if(bFast)
{
trace("FAST MOVING ANIMAL!");
}
else
{
trace("NORMAL MOVING ANIMAL!");
}
}
}
Constructor
Laten we nu verder gaan met de behandeling van een ERG belangrijke method, namelijk de zogenaamde constructor. Deze method wordt aangeroepen, wanneer je een instantie maakt van een class. Zet je deze method er niet in, dan wordt er simpelweg een lege constructor gebruikt.
Goed, hoe ziet een constructor eruit? Welnu, dit is een method met EXACT dezelfde naam als de class, waarin de method staat. Tevens mag een constructor NOOIT een return-type hebben! Laten we eens een constructor maken voor de class Animal, waarin we DIRECT de leeftijd kunnen instellen:
class Animal
{
private var numAge:Number;
public function Animal(numAge:Number)
{
this.numAge=numAge;
}
}
We zien hier twee belangrijk dingen. Allereerst, de modifier 'public'. Doe dit ALTIJD, wanneer je een instantie van een class wilt kunnen maken. Zet je er 'private' neer, dan kun je GEEN instantie maken, maar dit is handig voor bepaalde doeleinden, die buiten het bereik van deze inleiding vallen. Het gaat hier bijvoorbeeld voor het gebruik van het zogenaamde 'Singleton' design pattern. Dit is wel even het vermelden waard, aangezien in de Help van Flash een ENORME fout staat. Ik heb even gekeken naar het Singleton pattern, zoals zij dat geimplementeerd hebben. Daar klopt echter geen SNARS van. Laat ik hierbij even mijn versie geven (met private constructor, zoals het hoort!):
class Singleton {
private function Singleton(){}
private static var instance:Singleton = null;
public function doSomething():Void
{
trace("SOMETHING!");
}
public static function getInstance():Singleton {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Ik ga hier niet verder op in, maar voor mensen met kennis van design patterns zegt dit genoeg denk ik.
Ok, hoe gebruik we nu zo een constructor? Tijdens de aanmaak van een object, namelijk als volgt:
var animSomeAnimal:Animal=new Animal(12);
Zie je het verschil? We geven de leeftijd mee in de CONSTRUCTOR. Ja inderdaad, door via 'new' een nieuw object aan te maken wordt automatisch de constructor aangeroepen. Als je nu niets zou invullen (geen parameter in de constructor), dan krijg je GEEN foutmelding. Het vreemde is echter wel, dat de constructor MET parameters wordt aangeroepen! Deze parameters zijn dan echter 'undefined'.
Dat mag namelijk binnen ActionScript 2.0 (in tegenstelling tot andere OO talen!). Persoonlijk vind ik het slecht, aangezien de programmeur hierdoor niet kan afdwingen dat zijn object op een vaste manier wordt aangemaakt (slechte zaak Macromedia mensjes ;)). Ik weet alleen waarschijnlijk wel, waarom de Macromedia mensen hiervoor gekozen hebben. Dit speelt een rol bij het 'overerven' van classes, maar dat zal ik straks vermelden.
In tegenstelling tot andere OO talen, kan een class in AS 2.0 maar 1 constructor in zich hebben. Daardoor kun je mijn bovenstaand negatieve punt ook niet omzeilen door een lege private constructor te gebruiken. Daarom is AS 2.0 dus ook VRIJWEL een echte OO taal (ze zijn er nog niet!) ;)
We zagen in de constructor ook nog het keyword 'this'. Wat bedoelen we daarmee? Met 'this' wordt het 'object' bedoeld, waarin het wordt gebruik. DUS, als je ziet staan 'this.numAge', dan bedoelen we daarmee the property 'numAge' van het object 'animSomeAnimal'. Ik spreek dus nadrukkelijk van object en NIET van class, aangezien 'this' geen class aanduidt!!
Nu had ik het ook nog over 'accessor' en 'mutator' methods. Dit is een erg simpel concept. Een 'accessor' moet je voor de consistentie altijd laten beginnen met 'get' en een 'mutator' met 'set'. Laten we nu deze methoden aanmaken voor onze Animal class om de leeftijd in te stellen:
class Animal
{
private var numAge:Number;
//Accessor
public function getAge():Number
{
//return this.numAge mag ook
return numAge;
}
//Mutator
public function setAge(numAge:Number):Boolean
{
if(numAge<0 || numAge>200)return false;
//Nu MOETEN we dus this gebruiken, aangezien anders niet bekend is
//welke numAge er bedoeld wordt (ambiguiteit)
this.numAge=numAge;
return true;
}
}
Simpel he? Zie je trouwens ook dat ik controleer of de leeftijd wel geldig is? Dat is nu zo belangrijk in OO, dat de verantwoordelijkheden van classes goed is vastgelegd! Je kunt nu op geen enkele manier een ongeldige leeftijd opgeven ;)
Laten we nu eens kijken naar een meer elegante method, die ik ook ken vanuit Microsoft Visual Studio .NET (in de talen C# en VB .NET). De zogenaamde 'get' en 'set' methode werkt vrijwel op dezelfde manier als met een 'accessor' en 'mutator', zei het wat eleganter. Waarschijnlijk heb je zin om het volgende te realiseren:
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.age=12;
Ik heb al gezegd, dit kan via de modifier 'public', maar dat is dus niet correct volgens OO. Wat als we nu het beste van twee werelden kunnen krijgen. EN beveiligde toegang/wijziging van properties EN een eenvoudige wijze van toegang/wijziging. Welnu, daar komen de 'get' en 'set' methods.
Laten we voor het gemak eerst de 'set' method nemen:
class Animal
{
private var numAge:Number;
public function set age(numAge:Number):Void
{
if(numAge<0 || numAge>200)return;
//Nu MOETEN we dus this gebruiken, aangezien anders niet bekend is
//welke numAge er bedoeld wordt (ambiguiteit)
this.numAge=numAge;
}
}
En nu hebben we dat beste van twee werelden. Elegante aanroep methode EN goede beveiliging. Wat wil je nog meer? ;)
De 'get' is net zo eenvoudig, waardoor bijvoorbeeld het volgende kan:
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.age=12;
trace(animSomeAnimal.age);
BELANGRIJK: Mensen denken vaak, dat hierdoor in de Debug Console wordt geprint, dat 'age' een 'function' is. Dat is NIET zo. Dat is ALLEEN zo, wanneer 'age' een public method zou zijn binnen de class en er geen 'get' method is gedefinieerd.
Ok, nu de syntax:
class Animal
{
private var numAge:Number;
public function get age():Number
{
return numAge;
}
}
GEBRUIK deze methoden, want ze zijn super handig!
Wat is static?
Nu we al best wat kennis hebben van een class wordt het tijd om een ander belangrijk keyword uit te leggen, namelijk het woordje 'static'. Wat bedoelen we hier eigenlijk mee? Welnu, zowel een property als een method kunnen 'static' worden gedefinieerd. Hiermee wordt bedoeld, dat de 'static' method of property geen deel uitmaakt van een OBJECT, maar van de class zelf!! Ik vind het moeilijk om uit te leggen wanneer je dit gebruikt (hoewel ik het veel heb gebruikt :)), maar ik heb iets gevonden.
Stel je wilt bijhouden hoeveel instanties jij in je systeem aanmaakt van een dier, met andere woorden, hoeveel dieren zijn er in je systeem? Dat kan in combinatie met een 'static' property. Logischerwijs moet je het volgende doen:
- Houdt een teller bij in de class Animal
- Verhoog de teller bij een aanroep van de constructor
Als je nu de teller gewoon in een object zou bijhouden, dan wordt deze teller dus bijgehouden voor IEDER object. Dat wil je natuurlijk niet. Om deze reden maak je de teller 'static'. Laten we naar het voorbeeld kijken, zodat we direct de syntax zien:
class Animal
{
public static var numCountAnimals:Number=0;
public function Animal()
{
numCountAnimals++;
}
public static function get countAnimals():Number
{
return numCountAnimals;
}
}
Merk op, dat het woord 'static' achter de 'access modifier' staat. Dit HOEFT niet, je kunt ook zeggen 'static public', maar voor de duidelijkheid is het toch beter om EERST de 'access modifier' neer te zetten.
Je ziet ook,dat de 'get' method om het aantal instanties op te vragen 'static' is. Waarom is dit zo? Omdat het zinloos is om deze method aan te roepen voor bijvoorbeeld de instantie 'animSomeAnimal'. Het is toch niet logisch om te vragen aan een dier: "Hoeveel dieren zijn er?". Je vraagt dat juist aan diegene die daar zicht op heeft, dus bijvoorbeeld aan een boswachter vragen hoeveel bomen er in zijn bos zijn, dat is wel nuttig, maar aan een boom vragen hoeveel metgezellen er in het bos zijn, tja....dat is toch niet normaal dacht ik zo :)
Nu we deze method echter static hebben gemaakt brengt dit wel consequenties met zich mee. We kunnen deze method dan namelijk ALLEEN aanroepen via de class. Dus dit mag niet:
var animSomeAnimal:Animal=new Animal();
trace(animSomeAnimal.countAnimals);
Maar zo moet het:
trace(Animal.countAnimals);
Het voorbeeld in actie:
new Animal();
new Animal();
new Animal();
new Animal();
trace(Animal.countAnimals);
En we zien netjes het getal 4 in de Debug Output verschijnen :)
Een Dynamic class
Nu is er nog 1 belangrijk ding te vermelden over een class, voordat we verder gaan met overerving, maar ik wil er nu alvast bij zeggen, dat het OO matig een SLECHTE method is, maar desondanks ENORM handig kan zijn.
Waarschijnlijk heb je wel eens het volgende gedaan:
var mc_ball:MovieClip=new MovieClip();
mc_ball.doMove=function()
{
_x++;
}
Het leuke is het feit dat dit mag! Ik noem het zelf het op runtime vastknopen van nieuwe methods aan een OBJECT!. Je kunt het echter ook doen met een 'class'. Dus bijvoorbeeld:
MovieClip.blaat=function()
{
trace("BLAAT");
}
Het is nu echter NIET zo, dat het volgende dan werkt:
var mov:MovieClip=new MovieClip();
mov.blaat();
Dit zal echter wel kunnen:
MovieClip.blaat();
Het op runtime vastknopen van nieuwe functies aan een object of class kan via een heel simpele handeling. Gebruik daarvoor het woord 'dynamic' voor de class. Dus:
dynamic class Animal
{
}
Prototype en __proto___
Nu kan ik me voorstellen, dat je het toch handig vindt om direct aan een class een functie toe te voegen, die dan ook voor alle verdere instanties van die class beschikbaar is, dus bijvoorbeeld:
Animal.blaat=function()
{
trace("BLAAT");
}
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.blaat();
Dit zal dus niet werken. Waarom niet? Eigenlijk kan ik dat beter uitleggen bij de paragraaf 'Inheritance', maar aangezien dit eigenlijk gewoon bij een class 'an sich' hoort, leg ik het hier uit.
Als je bovenstaande manier gebruikt wordt de methode aan de class gekoppeld, maar NIET als een standaard methode, maar als een 'static' method. Of dit daadwerkelijk zo is weet ik niet, maar vanuit het oogpunt van de programmeur lijkt dit wel zo.
Om nu wel datgene te realiseren wat je wilt moet je gebruik maken van het woordt 'prototype'. Dit is eigenlijk een ander woord voor 'blauwdruk', dus de class zelf!! Dit is niet iets AS 2.0 specifieks, maar ik vond het wel even noemenswaardig om te vermelden (aangezien het ook binnen AS 2.0 nog steeds handig is).
Dus bovenstaande veranderen in:
Animal.prototype.blaat=function()
{
trace("BLAAT");
}
Omdat ik zelf de Help van Flash voor wat betreft het verschil tussen 'prototype' en '__proto__' een beetje onduidelijk vind, geef ik de volgende uitleg:
prototype gebruik je voor een class
__proto__ gebruik je voor een object
Voorbeeld:
Animal.prototype.blaat=function()
{
trace("BLAAT");
}
var animSomeAnimal:Animal=new Animal();
animSomeAnimal.__proto__.blaat=function()
{
trace("HUH?");
}
animSomeAnimal.blaat();
Wat zal er nu geprint worden??
Inderdaad, "HUH?" ;)
Moet je voor de grap eens kijken naar de definitie van 'prototype' en '__proto__' in de classes van Macromedia (class Object):
static var prototype:Object;
var __proto__:Object;
Erg duidelijk nu dus, dat '__proto__' alleen van toepassing is op een Object en 'prototype' juist op een class (is immers static ;)).
Nu even het belangrijkste verhaal, namelijk de praktische kant. Waar type ik die code voor een class eigenlijk? Elnu, een class die je zelf maakt, type je in een apart bestand, die de naam MOET hebben van de class en als extensie '.as'. Misschien dat je dit wel kent van Java, dat werkt namelijk exact op dezelfde manier.
Als je dus een 'Animal' class hebt gemaakt, dan moet al die code in het bestand 'Animal.as' staan. Let hierbij WEL op, dat onderscheid wordt gemaakt tussen hoofd- en kleine letters. Dus als je zegt 'var animSomeAnimal:Animal=new Animal();', maar je hebt de code staan in het bestand 'animal.as', of 'AnImal.as', dan zal Flash aangeven, dat de class niet gevonden kan worden. Denk daaraan!! Standaard beginnersfout!
IEDERE class moet in een APART '.as' bestand komen te staan. Je kunt dus NIET meerdere classes definieren in 1 bestand (in tegenstelling tot Java, waar ook zoiets bestaat als een 'private class').
Goed, maar hoe weet Flash nu waar je classes staan dan?? Dat weet Flash aan de hand van de 'classpath'. Als je in Flash naar 'Edit>Preferences' gaat, vervolgens de 'ActionScript' tab selecteerd en drukt op de knop 'ActionScript 2.0 Settings', dan kun je daar aangeven waar Flash moet zoeken naar de betreffende classes. Hier staan dus alle paden naar classes.
Als classpath is standaard ingesteld, dat Flash zoekt in het pad, waar je FLA bestand, of wat voor document dan ook staat. Vandaar de '.' in de classpath, dat staat namelijk voor 'huidige directory'.
Je kunt desgewenst zelf allerlei paden toevoegen.
De classpath waar ik het over heb wordt de 'Global Classpath' genoemd. Je hebt ook een 'Document-level classpath', die specifiek voor een FLA bestand kan worden ingesteld. Dus stel dat je voor ieder FLA bestand een aparte projectmap aanmaakt met daarin de noodzakelijke classes, dan moet je de 'Document-level' classpath aanpassen.
Deze classpath is te vinden onder 'File>Publish Settings', dan de knop 'Settings'.
Nu is het ook handig, dat AS 2.0 ondersteuning biedt voor het keyword 'import'. Hiermee kun je namelijk al je classes in verschillende mappen zetten, zonder de classpath aan te passen.
Stel je voor, je classpath verwijst alleen naar de map, waarin je FLA staat. Nu heb je bijvoorbeeld in die map een map aangemaakt met de naam 'Animals'. Vervolgens zet je in die map de class 'Animal.as'. Je moet nu de definitie van die class als volgt aanpassen:
class Animals.Animal
{
}
Als je nu een instantie van die class wilt maken, dan kun je gebruik maken van het keyword 'import', om de namen van ALLES klassen binnen de map (package met een mooi woord :)) 'Animals' aan Flash kenbaar te maken. Als je dan dus een instantie maakt van de class 'Animal', dan kent Flash de classes uit de map 'Animals' ook. Dus als volgt:
import Animals.Animal;
var animSomeAnimal:Animal=new Animal();
Als er meer dieren staan in de map 'Animals', dan kun je het beste dit doen:
import Animals.*;
Met de '*' geef je dus aan, dat je wilt dat Flash ALLE classes binnen de map 'Animals' kent. Gebruik je dus alleen 'import Animals.Animal' en je zegt dan 'var fish:Fish=new Fish()', dan kan dat dus niet (als de class 'Fish.as' in de map 'Animals' staat).
Je hoeft de import niet te gebruiken. Je kunt ook zeggen:
var animSomeAnimal:Animals.Animal=new Animals.Animal;
Hopelijk heb ik nu vrijwel alles voor wat betreft classes en objecten uitgelegd. Laten we nu maar verder gaan met 'Inheritance'.
Inheritance (overerving)
Helemaal aan het begin had ik het over het 'is een' principe. Een Olifant 'is een' Dier, maar niet andersom. Dit heeft alles te maken met inheritance.
Als we in AS 2.0 een class willen laten overerven van een andere class, waardoor dus de eigenschappen overgenomen worden, dan moeten we het woord 'extends' gebruiken.
Laten we allereerst even de volledige class Animal geven:
class Animal
{
private var numAge:Number;
private var strName:String;
private function Animal(numAge:Number,strName:String)
{
this.numAge=numAge;
this.strName=strName;
}
public function get theAge():Number
{
return numAge;
}
public function get theName():String
{
return strName;
}
public function doMove():Void
{
trace("MOVING ANIMAL");
}
public function doDying():Void
{
trace("DYING ANIMAL :(");
}
}
Kijk HEEL goed naar de constructor. Zie je dat deze private is? Op deze manier dwing je af, dat er GEEN instantie van de klasse Animal kan worden gemaakt. Is dit handig? Ja, en dat zul je zo zien. Ten eerste is het handig, omdat ik al verteld heb dat er bijvoorbeeld geen 'dieren' in de wei grazen. Het is gewoon nutteloos om een instantie van een 'dier' te maken. Een instantie van de class 'Olifant' is daarentegen volkomen logisch.
Nu gaan we het hebben over overerving (inheritance). Binnen AS 2.0 wordt inheritance gerealiseerd door gebruik te maken van het woord 'extends'. Stel, je hebt de class Animal en vervolgens wil je een sub-class Elephant maken. Deze class moet dus overerven van Animal. Hoe ziet dat er dan uit? Als volgt:
class Elephant extends Animal
{
}
Nu denk je waarschijnlijk: "Hmmm, een lege class". Maar nee, ALLES wat ook stond in Animal staat OOK in Elephant. Maar nu het grote mysterie. Je zag in Animal een 'private' constructor. Als je nu dit doet:
var bertha:Elephant=new Elephant();
dan wordt die private constructor WEL aangeroepen!! Vreemd maar toch waar.
Maar wat willen we nu, we willen ons olifantje wel een leeftijd en een naam kunnen geven, dus dat doen we als volgt:
class Elephant extends Animal
{
public function Elephant(numAge:Number,strName:String)
{
super(numAge,strName);
}
}
Hee, wat zien we daar? Een nieuw keyword, namelijk 'super'. Met het woordje 'super' duiden we de DIRECTE super class aan van de aanroepende class. Met andere woorden, we roepen de private constructor aan van de class Animal. Maar dat kan toch niet?? Jawel, private methods van een class kunnen wel door sub-classes aangeroepen worden. Denk aan het 'een olifant is een dier' principe, dan is het vrij logisch!!!
Als je de constructor van de super-class wilt aanroepen (dus de class die er direct boven ligt), dan MOET dit de ALLEREERSTE aanroep zijn in de constructor, waarin je de constructor van de super-class aanroept.
Maar kun je de constructor van de super-class ook ergens ander in je class gaan aanroepen? Ja hoor, dat kan....maar waarom zou je dat willen doen :rolleyes:
Nu wil ik even terugkomen op de uitspraak 'een olifant IS EEN dier'. Als dit zo is, dan zou dus het volgende moeten mogen:
var animSomeAnimal:Animal=new Elephant(12,"Trijntje");
En dit mag dan ook. Als je dit niet snapt, dan moet je even aan het volgende denken:
Is een Elephant een Animal?? Is je antwoord JA, dan kan het, anders niet.
Dus omgedraaid kan weer niet en dat is ook logisch, want een Animal is niet altijd een Elephant.
Maar wat heb je hier nu aan? HEEL veel. Stel je voor dat je een Array hebt met ALLERLEI dieren. Van al die dieren wil je de naam uitprinten. Is het dan niet mogelijk om ieder dier gewoon te behandelen als de class 'Animal' en op die manier de 'get' methode 'theName' op te vragen? Ja, dat kan...hiervoor gebruiken we het principe 'casting'. We 'casten' een dier naar een 'Animal' en dat kan, omdat voor ieder dier x geldt dat 'x is een Animal'.
Hier een voorbeeld:
var animals:Array=new Array();
animals.push(new Tiger(13,"Lizy"));
animals.push(new Octopus(13,"LongArm"));
animals.push(new Elephant(15,"Dikke Bertha"));
animals.push(new Cow(12,"Tiny 86"));
animals.push(new Snake(14,"Pi"));
animals.push(new Elephant(19,"Johnny Boy"));
for(var i=0;i<animals.length;i++)
{
var anim:Animal=Animal(animals[i]);
trace(anim.theName);
}
En dit mag. Kijk goed naar het vetgedrukte, dit is dus NIET het aanroepen van de constructor, maar hier 'cast' je een 'Object' naar een 'Animal'. Kan dit, dus m.a.w kun je zeggen 'een dier is een Object'. Vanuit je gevoel zeg je nee, maar aangezien de class 'Object' de 'root class' is (het is uiteindelijk de base class van IEDERE class!) is dit wel zo. Zo is een MovieClip ook een Object.
IEDERE class is dus in ieder geval een sub-class van Object!
Is dit casting nou nodig? Nee, dit wordt automatisch uitgezocht. In talen zoals Java moet je wel zelf casten (hoewel de notatie dan iets anders is).
Ik heb het een tijdje geleden gehad over de eigenschap 'Heeft een slurf'. Dat heb ik echter niet ingevuld, aangezien dat mooier kan.
Stel we maken ALLERLEI dieren aan en we willen weten welke dieren allemaal een slurf hebben. Om daarachter te komen zoeken we dus naar alle dieren die van het type 'Elephant' zijn.
Hiervoor komt de operator 'instanceof' om de hoek kijken. We passen onze code als volgt aan:
var animals:Array=new Array();
animals.push(new Tiger(13,"Lizy"));
animals.push(new Octopus(13,"LongArm"));
animals.push(new Elephant(15,"Dikke Bertha"));
animals.push(new Cow(12,"Tiny 86"));
animals.push(new Snake(14,"Pi"));
animals.push(new Elephant(19,"Johnny Boy"));
for(var i=0;i<animals.length;i++)
{
var anim:Animal=Animal(animals[i]);
if(anim instanceof Elephant)
{
trace(anim.theName+" heeft een slurf");
}
}
We zullen dus netjes zien, dat zowel 'Dikke Bertha', als 'Johnny Boy' een slurfje hebben :)
Voor wat betreft inheritance is dit voldoende. Je kunt natuurlijk zo ver gaan als je zelf wilt. Je kunt dus een class Animal maken met als subclass 'Mammal' en daaronder weer 'HairyMammal' en daarna weer 'Horse' etc. etc.
Nu even iets voor wat betreft een 'dynamic' class. Is een sub-class van een dynamic super-class ook dynamic? Ja, behalve voor sub-classes van MovieClip. Hierbij moet EXPLICIET aangegeven worden bij de sub-class dat het dynamic moet zijn. De reden dat ze bij Macromedia deze keuze hebben gemaakt is simpel en logisch. Veelal maken mensen sub-classes van een MovieClip, maar willen niet altijd dat deze sub-classes at runtime met functionaliteit uitgebreid kunnen worden. Daarom is deze keuze gemaakt. Een dynamische sub-class van een MovieClip ziet er dus als volgt uit:
dynamic class Particle extends MovieClip
{
}
BIJNA klaar met deze toch al groot wordende inleiding. Ik wil alleen nog even afsluiten met het praten over 'polymorfisme' en 'interfaces'. Eerst maar even polymorfisme.
Polymorfisme
Iets is polymorf, wanneer het meerdere vormen kan aannemen. Juist bij inheritance is dit belangrijk. Kijk bijvoorbeeld eens naar de method 'doMove'. Is deze method voor iedere 'Animal' hetzelfde?? Nee, niet echt. We willen dus, dat deze methode verschillende vormen kan aannemen. Dat doen we simpelweg door een method zogenaamd te 'overriden'. Hoe doen we dat? Als volgt:
class Pigeon extends Animal
{
public function doMove():Void
{
trace("I CAN FLY!");
}
}
Dus we typen de method uit Animal EXACT over, maar we vullen de implementatie anders is. Nu heeft de doMove() method dus een andere 'gedaante' aangenomen voor een 'Pigeon', m.a.w, polymorfisme.
Hierbij moet wel opgemerkt worden, dat de signatuur van de methode wel EXACT hetzelfde moet zijn. Dit betekent dat de naam gelijk moet zijn. Nu vind ik nog een facet van AS 2.0 een beetje ERG stom. Je kunt namelijk GEEN methoden maken in een class, die dezelfde naam hebben. Je kunt je bijvoorbeeld voorstellen, dat je een methode 'doMove()' hebt zonder parameters, maar ook de methode 'doMove(bFast:Boolean)'. Dit kan in verschillende situaties handig zijn. Maar ja, binnen AS 2.0 worden methoden uniek aangeduid door de naam alleen. Dit is gewoon ronduit idioot te noemen. Als je naar andere programmeertalen kijkt, dan is de unieke aanduiding van een method de naam in combinatie met de parameters. Is ook veel handiger.
Nou ja, even een voorbeeldje van het polymorfisme:
var animals:Array=new Array();
animals.push(new Pigeon(3,"Snow White"));
animals.push(new Elephant(15,"Dikke Bertha"));
for(var i=0;i<animals.length;i++)
{
var anim:Animal=Animal(animals[i]);
anim.doMove();
}
Nu wordt op CORRECTE wijze allereerst de 'doMove()' method van de 'Pigeon' Show White aangeroepen en vervolgens de 'doMove()' method van Dikke Bertha (de 'Elephant'). Is dit handig?? Jazeker, want de code verandert niet!! Je kunt naar believen ALLERLEI nieuwe dieren gaan maken, maar het 'for' loopje hoeft niet veranderd te worden. Polymorfisme is dan ook een zeer belangrijk concept binnen OO. Maak er gebruik van als dit mogelijk is!!
Nu zijn we aangekomen bij het concept 'interfaces'. Ik heb dit altijd moeilijk gevonden om uit te leggen, maar ik ga het maar weer proberen in de volgende en tevens op een na laatste paragraaf.
Interfaces
Wat is een interface? Een interface is eenvoudigweg een class zonder properties met alleen maar public methods ZONDER body (dus GEEN code). Wat hebben we nu aan een interface? Meerdere dingen, maar ten eerste is het mogelijk om 'meervoudige overerving', ofwel 'multiple inheritance' te realiseren. Met extends kun je namelijk NIET tegelijkertijd van meerdere classes overerven. Je kunt bijvoorbeeld niet zeggen:
class Car extends FourWheelDrive,EngineBasedVehicle
{
}
Stel je nu voor, dat je een auto hebt. Nu kun je een auto 'starten'. Je kunt de auto 'uitschakelen', maar je kunt bijvoorbeeld ook 'wegrijden'. Dit zijn dus allemaal mogelijkheden van een auto. Dit kun je nu een interface noemen. Laten we eens een interface voor een 'dier' verzinnen. Een dier kan 'bewegen'. Laten we dat de 'Movable' interface noemen. Tevens kan een dier geluid maken, dit noemen we 'NoiseMaker'. Zo hebben we voor de illustratie wel even genoeg.
Een interface moet NET zoals een class in een apart ".as" bestand worden opgeslagen met dezelfde vereisten als voor een class. Laten we eens kijken naar de interface 'Movable':
interface Movable
{
public function doMove():Void;
}
Heel eenvoudige syntax, maar je ziet dus een lege body (geen implementatie). Stel dat we deze interface willen koppelen aan een class. Hoe doen we dat??? Met het keyword 'implements'. De class implementeert dus een bepaalde interface. Op naar de code:
class Animal implements Movable
{
public function doMove():Void
{
}
}
We zien dus, dat we de functie MOETEN implementeren, aangezien we via 'implements' aangeven dat we dit gaan doen. De method moet dan ook gelijk zijn aan die in de interface, dus OOK de 'access modifier'!! Alle interface methods zijn zoals gezegd ook public en daardoor is een interface dus ook ALTIJD voor de communicatie met de buitenwereld!! De buitenwereld kan gebruik maken van de interface.
Laten we nu eens kijken naar de interface 'NoiseMaker':
interface NoiseMaker
{
public function makeNoise():Void
}
Koppelen aan de class:
class Animal implements Movable,NoiseMaker
{
public function doMove():Void
{
}
public function makeNoise():Void
{
}
}
Wat nu helemaal mooi is, we kunnen nu dus ook zeggen 'een Animal is een NoiseMaker'. Dus het volgende is mogelijk:
var noisemakers:Array=new Array();
noisemakers.push(new Animal());
noisemakers.push(new Pigeon());
for(var i=0;i<noisemakers.length;i++)
{
var noisy:NoiseMaker=NoiseMaker(noisemakers[i]);
noisy.makeNoise();
}
Kan een interface dynamic zijn? Nee dat kan niet.
Wat is nu het voordeel van een interface, afgezien van meervoudige overerving? Ik heb echt geen flauw idee, maar het gebruik van interfaces is wel netjes :)
Koppel een class aan een MovieClip
Ok en nu eindelijk even een praktisch verhaal. Hoe koppel ik een class aan een MovieClip in mijn Library in Flash, zodat ik gebruik kan maken van de methods en properties van de class?
Als volgt:
- Maak een MovieClip aan in Flash en geef het de naam 'TestMC'
- Teken een vierkantje in de MovieClip
- Maak een '.as' bestand aan met de naam 'Test.as'
- Type de volgende code in deze class:
class Test extends MovieClip
{
public function animate():Void
{
for(var i=0;i<360;i++)
{
_rotation=Math.round(Math.random()*180);
}
}
}
- Sla het bestand op
- Ga in Flash naar je Library en klik met de rechtermuisknop op 'TestMC'. Kies 'Linkage' uit het menu dat tevoorschijn komt. Vul de gegevens in, zoals ze in onderstaande figuur weergegeven worden en klik op OK:
http://n.domaindlx.com/kaHu/attach_class.PNG
- Sleep een instantie van 'TestMC' vanuit de library naar de Stage
- Klik eenmaal op de instantie op de Stage
- Type de volgende code in het Actionscript pane:
on(press)
{
animate();
}
- Test nu je Movie
Als je nu telkens op de MC klikt, draait de MC onder een willekeurige hoek. Dat is nu eenvoudigweg alle stappen die noodzakelijk zijn om een class aan een MC te koppelen. Handig he?
Afsluiting
Het verhaal is langer geworden dan ik wilde, maarja..
Zal ik nu maar even specifiek op je vis spelletje ingaan?
Ik denk dat je nu wel een beeld hebt gekregen van OO en vrijwel alle facetten ervan binnen ActionScript 2.0. Maar voor de opheldering zal ik je even wat voorbeeld classes laten zien, dan krijg je ongeveer een beeld van hoe het kan. Alle interfaces en classes moeten dus WEL in een apart bestand komen natuurlijk!!
interface Swimmable
{
public function swim():Void;
}
interface BehaviourModifier
{
public function modify(mov:MovieClip):Void;
}
dynamic class Fish extends MovieClip implements Swimmable
{
private var behaviour:Array=new Array();
private var numWeight:Number;
private var bHooked:Boolean=false;
private function Fish(numWeight:Number)
{
this.numWeight=numWeight;
}
public function addBehaviourModifier(modifier:BehaviourModifier):V oid
{
behaviour.push(modifier);
}
public function get score():Number
{
return numWeight*100;
}
public function hookIt():Void
{
bHooked=true;
}
public function swim():Void
{
for(var i=0;i<behaviour.length;i++)
{
BehaviourModifier(behaviour[i]).modify(this);
}
}
}
class Pike extends Fish
{
public function Pike(numWeight:Number)
{
super(numWeight);
}
}
class ExtremeMover implements BehaviourModifier
{
public function modify(mov:MovieClip):Void
{
mov._x+=Math.random()*50;
}
}
Ok, ik hoop dat ik geen syntax fouten erin heb staan, aangezien ik het gewoon hier getypt en bedacht heb :)
En nu ff het gebruik ervan:
var thePike:Pike=new Pike(40);
thePike.attachBehaviourModifier(new ExtremeMover());
Je ziet al, hier heb ik iets heel simpels even gemaakt, maar er komt enorm veel code bij kijken. Het grote voordeel is dan echter wel, dat je HEEL makkelijk nieuwe gedragingen van vissen kunt toevoegen (BehaviourModifier), waardoor het een zeer onderhoudbaar en uitbreid baar spel zal worden.
Ik hoop dat dit alles jou op een idee heeft gebracht, dus veel succes ermee!
Hoe je jouw eigen spel qua opbouw in AS 2.0 eruit wil laten zien hangt af van je eigen creativiteit en kennis van ActionScript, maar ik denk dat mijn inleiding je voor wat betreft de ActionScript kennis al behoorlijk ver zal brengen.
vBulletin® v3.8.1, Copyright ©2000-2012, Jelsoft Enterprises Ltd.