Thursday, March 22, 2007

[TUTORIAL] De Blokhaken notatie

De blokhaken( '[' en ']') notatie is eigenlijk min of meer de opvolger van wat eerst eval() was. Het idee is eigenlijk precies hetzelfde als een array, maar in plaats van dat je getallen gebruikt om een bepaald items hierin aan te spreken, gebruik je in deze notatie strings. De array word dan vervangen door een MovieClip, of een ander Object. Het stelt je dus in feite in staat een MovieClip of Object te doorzoeken of deze een instatie bevat gelijk aan de string. Wanneer je dit nog nooit hebt gebruikt zal je wel denken: als ik deze string weet, waarom noteer ik het dan niet gewoon bijvoorbeeld zo:



movieclip.referentie.waarde = "string";





Het antwoord is: het is niet dynamisch. Wanneer je bijvoorbeeld aan de hand van for-loops een bepaald aantal MovieClips op je stage laat zetten, weet je niet hoeveel het er zijn en dus welke instanties er na de loop op je stage staan. Met de blokhaken notatie kan je dus in de in een movieclip zoeken naar een string(en dus ook een string in combinatie van één of meer variabelen). Hé? Hoor ik daar variabelen? Inderdaad, nu is het wel dynamisch.

[b]Hoe gebruik ik de blokhaken notatie[/b]

Stel je voor, je maakt dmv een for-loop een x aantal MovieClips aan. De nieuwe naam die je toekent word samengesteld uit de string 'mc' met daarachter de huidige waarde van variablele 'i'. Hoe spreek ik deze movielip dan het beste aan?



var aantal:Number = 5;
for (var i:Number = 0; i <= aantal; i++)
{
this.attachMovie("mcinlibrary", "mc" + i, this.getNextHighestDepth());
this["mc"+i].i = i;
this["mc"+i]._x = this["mc"+i].i*20;
}




Het idee zal nu wel duidelijk zijn. aangezien je de nieuwe instantie in 'this' attachd, zal je hierin gaan 'zoeken'. De referentie is zoals je bij de instance name van de geattachde MC hebt ingegeven is "mc"+i. Met this["mc"+i] zal je dus de MovieClip bedoelen in 'this' met de instancename gelijk aan de uitkomst van de samengestelde string.

Monday, March 5, 2007

[TUTORIAL] Auto beweging met toetsenbord

In deze tutorial ga ik het rijden van een auto in flash behandelen. Zeker voor de beginnende flasher kan dit al snel een leuk resultaat geven. Ik behandel zowel de simpele vorm, en de wat realistischere vorm.

De simpele vorm:


De wat realistischere vorm:


De simpele vorm

Moeilijkste bij nieuwe dingen is altijd bedenken hoe je het aan gaat pakken. Hoe zal je dit nou aanpakken? Denk even goed na welke variabelen afhangen van de x en de y waarden. Dat zijn natuurlijk de snelheid en de rotatie. Waneer je hier een plaatje van tekent zal het je misschien wel opvallen, wiskunde!

Je ziet met de rode stip aangegeven de huidige positie van de auto, en de andere rode stip de positie waar hij moet eindigen met deze rotatie en snelheid.
Als je het nog goed herinnert van school, is dit makkelijk uit te rekenen met cos, sin en tan:


this._x += Math.sin(this._rotation*(Math.PI/180))*speed;

this._y += Math.cos(this._rotation*(Math.PI/180))*speed*-1;



Let op: vergeet niet de rotatie te vermenigvuldigen met PI/180, aangezien de rotatie in flash in graden gaat, en je de rotatie in radialen moet hebben (180 graden = 1PI radialen).

Met een beetje basis moet de rest helemaal simpel zijn:



var speed:Number = new Number(0);

car.onEnterFrame = function() {

if (Key.isDown(Key.UP) && speed<7) {

speed += .2;

} else if (Key.isDown(Key.DOWN) && speed>-3) {

speed -= .1;

}
if (Key.isDown(Key.LEFT)) {

car._rotation -= 4;

}
if (Key.isDown(Key.RIGHT)) {

car._rotation += 4;

}
this._x += Math.sin(this._rotation*(Math.PI/180))*speed;
this._y += Math.cos(this._rotation*(Math.PI/180))*speed*-1;

speed *= .98;
};



De realistischere vorm

Wat gebeurd er nou meer/minder dan bij de simpele vorm. Hier is een simpel truckje voor. Wat in de simpele vorm de x en y waarde worden, worden nu eerst virtuele x en y posities. Deze punten zijn als het ware het 'streven', je auto wil daar heen bewegen. Maar om het slippen mogelijk te maken zal hij er niet helemaal komen. We laten hem er elk frame als het ware naar toe easen. Bij een Framerate van 31 fps:


var vx:Number = car._x;
var vy:Number = car._y;
var speed:Number = new Number(0);

car.onEnterFrame = function() {

if (Key.isDown(Key.UP) && speed<7) {

speed += .2;

} else if (Key.isDown(Key.DOWN) && speed>-3) {

speed -= .1;

}
if (Key.isDown(Key.LEFT)) {

car._rotation -= 7;
speed -= .15;

}
if (Key.isDown(Key.RIGHT)) {

car._rotation += 7;
speed -= .15;

}
vx += Math.sin(this._rotation*(Math.PI/180))*speed;
vy += Math.cos(this._rotation*(Math.PI/180))*speed*-1;

this._x += (vx-this._x)/15;
this._y += (vy-this._y)/15;

speed *= .98;
};



Zoals je misschien wel is opgevallen, er is nu bij het draaien ook voor gezorgd dat je iets af remt, probeer het maar eens zonder en zie het resultaat.

[TUTORIAL] Rechtermuisknop Menu

Wat mij de laatste tijd vaak opvalt, zelfs bij professionele flash-websites is dat ze het rechtermuisknop-menu van flash laten voor wat het is. Dit geeft je het recht om in en uit te zoomen, de kwaliteit van je movie te bepalen, voor en achteruit in frames te gaan. Ik vind dit persoonlijk erg onprofessioneel en onhandig. Daarbij kan je ook je eigen items toevoegen, voor zowel algemeen, maar ook wanneer je rechtermuisknop klikt op een movieclip. In deze tutorial gaan we een eigen rechtermuisknop menu in elkaar zetten.

Build-in-Items

Voordat je begint met testen zal ik je vast van een hoop geprobeer af helpen: bij het testen van je movie (ctrl+enter) zie je rechtermuisknop menu NIET! Je moet je bestand dus openen in een 'gewone' flashplayer voor je het resultaat kan zien!

De standaard items zoals hierboven al aangegeven zijn gemakkelijk uit te schakelen. Eerst maak je een ContextMenu aan met de ContextMenu klasse, daarna ken je hem toe aan het menu. In onderstaand voorbeeld zie je hoe je ook weer bepaalde items aan kan zetten. In dit geval de print functie:



var my_cm:ContextMenu = new ContextMenu();

my_cm.hideBuiltInItems();

my_cm.builtInItems.print = true;

this.menu = my_cm;



Op deze manier is het dus ook makkelijk bijvoorbeeld alleen de zoom functie uit te schakelen. Het is dus niet de bedoeling alles uit te schakelen, en dan weer alles in te schakelen behalve een bepaald item!



var my_cm:ContextMenu = new ContextMenu();

my_cm.builtInItems.zoom = false;

this.menu = my_cm;



Custom Items

Het is ook mogelijk om eigen items toe te voegen aan het menu, met zelf gemaakte functies. Zo is het bijvoorbeeld mogelijk om een items toe te voegen om Een website te bekijken. Dit is bijvoorbeeld handig in flash signatures. Je menu bestaat uit een array, gevult met ContextMenuItems. Onderstaand voorbeeld laat zien hoe je een link naar www.google.nl toevoegt aan je menu:



var menu:ContextMenu = new ContextMenu();

menu.hideBuiltInItems();

var siteItem:ContextMenuItem = new ContextMenuItem("Visit Google", openSite);

menu.customItems.push(siteItem);

this.menu = menu;

function openSite() {

getURL("http://www.google.nl/", _blank);

}



Deze custom-items zijn ook toe te kennen aan MC's. Je kan hier leuke dingen mee doen, hier een voorbeeldje van hoe je hem kan laten roteren.



var menuMc:ContextMenu = new ContextMenu();

var hoofdMenu:ContextMenu = new ContextMenu();

hoofdMenu.hideBuiltInItems();

var siteItem:ContextMenuItem = new ContextMenuItem("Rotate 90 degrees", rotate);

menuMc.customItems.push(siteItem);

mc1.menu = menuMc;

this.menu = hoofdMenu;

function rotate(target:MovieClip) {

target._rotation += 90;

}



onSelect()

Het onSelect() event geeft ons de mogelijkheid een actie uit te voeren wanneer de rechtermuisknop is ingedrukt, nog voordat het menu zichtbaar word. Als laatste voorbeeld heb ik hier een script om door middel van het indrukken van je rechtermuisknop om op die plek een vierkantje aan te Stage toe te voegen:





this.createEmptyMovieClip("square", this.getNextHighestDepth());
square.lineStyle(1, 0x000000, 100);
square.moveTo(-5, -5);
square.lineTo(5, -5);
square.lineTo(5, 5);
square.lineTo(-5, 5);
square.lineTo(-5, -5);
square._x = 1000;

var n:Number = 0;

var xm:Number = new Number();
var ym:Number = new Number();

var my_cm:ContextMenu = new ContextMenu();
var addsquare:ContextMenuItem = new ContextMenuItem("Add Square", addSquare);
my_cm.customItems.push(addsquare);
my_cm.onSelect = save;
this.menu = my_cm;

function save(obj:Object, menu:ContextMenu) {

xm = _root._xmouse;
ym = _root._ymouse;

}

function addSquare() {

n++;
square.duplicateMovieClip("square"+n, _level0.getNextHighestDepth());
_level0["square"+n]._x = xm;
_level0["square"+n]._y = ym;

}


Wanneer je een site maakt, of iets anders in Flash, raad ik toch echt aan van deze mogelijkheden gebruik te maken. De gebruiker kan anders ongewenste acties laten uitvoeren, waardoor je movie niet meer goed werkt!

Sunday, March 4, 2007

[EXPERIMENT] PhotoGallery

Misschien ben je het al wel eens tegen gekomen, een prototype 3d desktop genaamd BumpTop. Naar mijn idee is het idee geniaal, ik weet niet zeker of het handig werkt. Hier een preview van hoe het zal worden:

Dit heeft mij ertoe gezet er een beetje mee te gaan experimenten in Flash. In mijn geval heb ik het dan wel gewoon 2d gehouden. Even wat functies en uitleg voordat ik je het resultaat tot nu toe zal laten zien:

Bovenin staat het menu. Deze kan je openen en sluiten door op menu te drukken. In dit menu staat een textvak met standaard de text "URL of Picture goes here...". Daar kan je de URL invullen van een foto dit in dezelfde map als het .swf bestand, of een URL van een plaatje van internet. Wanneer je dan op de 'Add Picture' knop druk zal je afbeelding in het midden van het scherm verschijnen.

Er zijn 2 mode's: de 'Explore Mode' en de 'Arrange Mode'. Bij de 'Arrange Mode' is het mogelijk je foto's te slepen en te gooien. Zo kan je foto's bij elkaar leggen en ordenen. In de 'Explore Mode' is het mogelijk om door middel van de muis te bewegen de foto's in een wat groter formaat te bekijken.

Onder de rechtermuisknop zitten nog 3 functies verscholen. Hier kan je een foto verwijderen, hem draaien, en een functie welke nog niet helemaal werkt: je foto in de browser bekijken.

Ik hoop dat het een beetje duidelijk is. Ik ben nog bezig functies te verbeteren/toe te voegen. Ik zal dit binnenkort weer updaten.

Hier vind je het resultaat

Thursday, March 1, 2007

[ARTIKEL] Collision Detection

De beginnende AS'er zal er geen problemen mee hebben: de hitTest() functie van AS. Wil je alleen geavanceerdere movies maken, dat werkt hitTest() namelijk helemaal niet naar behoren. In dit artikel kijken we naar hoe hitTest te werk gaat, en bespreken we hoe je snelle en nauwkeurige collision tussen verschillende objecten kunt achterhalen. Ook zal ik tijd besteden aan Frame-Independent Collision Detection.

hitTest()
Wat maakt hitTest() nou zo onhandig om mee te werken? Wanneer je 2 objecten wilt controleren op een collision, gebruik makend van hitTest() zijn er 2 manieren. De ene manier is gewoon simpel:

if(object1.hitTest(object2)){

trace("RAAK!");

}

Wanneer deze 2 objecten allebei vierkanten zijn werkt dit prima, maar wanneer dit 2 ander soort vormen zijn, komt hier nadeel 1 van de hitTest() functie: de bounding box. Flash zal een denkbeeldig vierkant om je object heen trekken, en kijken of deze 2 vierkanten elkaar raken. Dit is natuurlijk absoluut onnauwkeurig, zie onderstaand voorbeeld:


De vormen raken elkaar nog helemaal niet, maar de bounding boxen wel! De hitTest() functie zal dus true terug geven. Is hier binnen hitTest geen alternatief voor? Nou, er is binnen de hitTest functie wel een mogelijkheid om zonder bounding box te werken, alleen werkt dit niet met 2 objecten. Hij test in dat geval op een coördinaat (x,y) met een object. Dit kan je in principe weinig gebruiken. Ik gebruik het zelf eigenlijk alleen maar als alternatief voor onRelease(), aangezien die niet werkt bij mc's die in een andere mc zit waar je ook op kan klikken.

Alternatieven

Als de hitTest() functie niet goed werkt, hoe moet het dan? De oplossing ligt in de wiskunde. Met wiskunde is zijn een hele boel collisions simpelweg gewoon uit te rekenen. Voorbeelden zijn cirkel cirkel, lijn lijn, cirkel lijn, vierkant vierkant, enz. Ik hoor je al denken: "Maar in games zijn toch niet alle vormen pure wiskundige vormen?". Nee, inderdaad niet, maar ze zijn wel terug te brengen tot dit soort vormen. Een leuk weetje hierbij is dat de hoofdpersoon in het spel 'Tomb Raider' werd vergeleken als een cilinder, heb je daar ooit iets van gemerkt?

Vierkant-vierkant

Waarschijnlijk de makkelijkste in Collision Detection is wel de collision tussen twee vierkanten. Ik denk zelfs (ik heb er eigenlijk nooit naar gekeken) dat hitTest() exact hetzelfde werkt, maar voor het inzicht lijkt het mij belangrijk dit toch even te bespreken.

Hoe bereken je nou of 2 vierkanten elkaar raken? Als je deze vraag stelt kan je dit ook op een andere manier stellen: wanneer hebben de twee vierkanten minstens 1 gemeenschappelijk punt? Nu is het misschien al wat duidelijker. De truck is om te kijken of de hoekpunten (dit zijn natuurlijk de uiterste waarden) binnen de hoekpunten van de ander vallen, voor zowel de x en de y coördinaten. Wanneer ook maar één van de hoekpunten binnen de hoekpunten valt, is er al een collision.

function retangleRetangle(retangle1:MovieClip, retangle2:MovieClip):Boolean {

if (retangle1._x+retangle1._width > retangle2._x && retangle1._x < retangle2._x+retangle2._width && retangle1._y+retangle1._height > retangle2._y && retangle1._y < retangle2._y+retangle2._height) {

return true;

}

return false;

}


Cirkel-cirkel

Wat is de definitie van een cirkel? Wiskundig gezien is het een verzameling punten men gelijke afstand tot een punt(het middelpunt van de cirkel). Met deze wetenschap is het een heel stuk makkelijker te bepalen of 2 cirkels elkaar raken. De stelling is zoals je misschien al wel kon bedenken: "2 cirkels raken elkaar wanneer de afstand tussen de middelpunten kleiner is dat de som van de stralen". Klinkt misschien een beetje onlogisch, daarom hier een afbeelding:



De afstand tot de middelpunten is uit te rekenen via de stelling van Pythagoras.
De straal is simpelweg de halve hoogte van je mc.



function circleCircle(circle1:MovieClip, circle2:MovieClip):Boolean {
radius1 = circle1._height/2;
radius2 = circle2._height/2;

diffX = Math.abs(circle1._x-circle2._x);
diffY = Math.abs(circle1._y-circle2._y);

distance = Math.sqrt((diffX*diffX)+(diffY*diffY));

if (distance < (radius1+radius2)) {

return true;

} else {

return false;

}
}


Lijn-lijn

Lijn lijn detectie is een heel erg veel gebruikte collision detection in games. Waarschijnlijk zal je dat verbazen, want je kan je er zo snel niet een verzinnen. Dit komt weer omdat ze vormen hebben teruggebracht tot een simpelere vorm. Een van de meest voorkomende lijn-lijn collisions is die tussen een bal en een lijn (ja, klinkt raar he?). Dan heb ik het wel over een bewegende bal. Lijn 1 is de een oppervlakte, de 2e lijn is de baan die de bal tussen 2 frames beschrijft. Voordeel van deze manier, en niet gewoon met kijken of de bal en de lijn elkaar raken is dat de detectie terug gebracht op lijn-lijn frame-independent is. Dat wil zeggen dat er niet alleen op het frame gekeken word of ze elkaar raken. Dit kan namelijk nadelig zijn als je de collision tussen bijvoorbeeld een bal met een snelheid van 20 pixels per frame en een lijn van 3 pixels wilt testen. Op een bepaald frame is de bal nog voor de lijn, maar het volgende frame kan hij er al langs zijn. Flash zal dit zonder een frame-independent collision detection niet zien als een collision.

Even terug naar de middelbare school. Hier heb je (neem ik aan) geleerd wat de vergelijking is van een rechte lijn: f(x) = ax+b
Waarbij a de richtingscoëfficiënt (de steilheid) is, en b het snijpunt met de y-as (waar x = 0).

Ook is er behandeld dat lijnen met dezelfde richtingscoëfficiënt evenwijdig zijn, en wanneer ze niet gelijk zijn, de lijnen elkaar ergens moeten snijden (geld bij LIJNEN, en niet bij lijnSTUKKEN).

Dan heb je als het goed is ook geleerd wat een snijpunt tussen twee lijnen precies inhoud. Op het snijpunt van twee lijnen zijn zowel de x als de y waarden aan elkaar gelijk, ook wel te noteren als:
a1∙x+b1 = a2∙x+b2
Door herleiden anders op te schrijven geeft dit ons:
x = (b2-b1)/(m1-m2)

Dit zal ons de x coördinaat van het snijpunt geven. Om achter de y coördinaat te komen moeten we simpelweg de gevonden x coördinaat invullen in een van de twee formules (maakt niet uit welke, ze zijn immers gelijk op dat punt).

Nadat we weten waar het punt is dat ze snijden, moeten we alleen nog maar kijken of het gevonden coördinaat binnen de coördinaten van het lijnstuk valt (punt-vierkant detectie)