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)

No comments: