hopefully this will stop all the hitTest questions. If you have something helpful to post please do.
THINGS YOU SHOULD KNOW ABOUT HITEST
- hitest can find a collision between two movieclips or a point against a movieclip
- hit testing uses global coordinates
- the two methods of hit testing are: movieclip : movieclip or movieclip : point
- movieclip to point can accept a thrid parameter, called a shape flag, if this parameter is set to true, it will test the actual shape of a movieclip (this can be anything) if it's set to false it will check the bounding box of a movieclip, this is a bounding box is always a square or rectangle
- hit testing will return a boolean value, meaning: true or false, which also means you can store a hitTest in a variable
- hit testing does not position objects, it use is for checking a hit
- hit testing should be avoided with complex mathematical collisions such as Rigid body's
- hit testing is a good simple way to check a hit without slowing your game down
if you remember the above rules, then hitTesting will be a breeze
SIMPLE HITEST
MOVIECLIP : MOVIECLIP HIT TESTING
to perform a simple hit test between two movieclips:
Code:
if (player.hitTest(enemy)) {
trace("hit");
}
MOVIECLIP : POINT HIT TESTING
to perform a hit test on an objects actual shape and a point:
Code:
if (player.hitTest(enemy._x, enemy._y, true)) {
trace("hit");
}
the first two parameters are numbers, they don't have to be the enemy's position, they could be any number you want, the third parameter is the shapeflag, when set to true it checks the numbers against the players shape
to perform a hit test on an objects bounding box and a point:
Code:
if (player.hitTest(enemy._x, enemy._y, false)) {
trace("hit");
}
COLLISION RESPONSE
before any additional checking, nothing is known about the hitTest, we only know that it is true or false, so we need to use other methods to respond to the collision
IF STATEMENT
the "if statement" can be very limited, since we know nothing about the intersection of the hitTest, we can only guess, a common solution is:
Code:
var speed = 10
if (!landscape.hitTest(player._x, player._y+speed, true)) {
player._y += speed;
}
WHILE LOOP
this works by running a loop while an object has detected a collision, a loop is needed so that the object moves in the correct direction the exact amount of pixels required. A while loop is used here because we are not sure how far the movieclip has intersected another movieclip, so we do some operations "while" the condition is true
Code:
while (landscape.hitTest(player._x, player._y, true)) {
player._y--;
}
the above code checks the x and y coordinates of the player and moves it up 1 pixel every time a hitest is found. The drawback to this solution is that it can be slow with the more directions you are checking for.
ADVANCED COLLISION DETECTION
HIGH SPEEDS
sometimes an object will be moving very fast and needs to check collision between an object which is thinner than the movieclips current speed, odds are that the movieclip will miss the detection of a hit because its moving too fast. An easy way to overcome this is use a loop to check the positions between the current movieclips position and its next position, so that anything in between will be detected. This method uses the shapeflag hitest again.
Code:
//players inital velocity's
player.vx = 300;
player.vy = 300;
//code run on every frame
onEnterFrame = function () {
//grab the players actual speed
var speed = Math.sqrt(player.vx*player.vx+player.vy*player.vy);
var vx = player.vx/speed;
var vy = player.vy/speed;
//loop through the coordinates from the player's current position to its next position
for (var i = 0; i<speed; i++) {
var x = player._x+(i*vx);
var y = player._y+(i*vy);
//perform a shape based collision on the landscape based on the current coordinate
if (landscape.hitTest(x, y, true)) {
//position the player at the correct coordinate
player._x = x;
player._y = y;
//stop the movement of the player
player.vx = 0;
player.vy = 0;
//no need to continue checking additional points, so we break from the loop
break;
}
}
//update the players position
player._x += player.vx;
player._y += player.vy;
};
this code assumes the player and landscape are in one movieclip, and this code is put on frame 1 of that movieclip. You can easily change it to your needs by changing the names.
OPTIMISED HIGH SPEEDS
the majority of the above code is used here as well. Except we need to change how many pixels are checked in the loop, if we make this number smaller the collision checking will be faster and less strain on the CPU. But the accuracy of the checking will be smaller. By simply adding and changing the next lines we can optimise the checking:
Code:
//to lessen the strain of the loop, we want to only check a few points
//the higher the step, the less checking we have to do, but the less accurate the check will be.
//if our speed is 70 and the step divisor is 10, that means every that every 7 pixels between the start and end point will be checked
//always keep the step smaller than any part of the movieclip to be hitTested, this will stop anything from missing a collision
var step = speed/10;
var vx = player.vx/step;
var vy = player.vy/step;
//loop through the coordinates from the player's current position to its next position
for (var i = 0; i<step; i++) {
MULTIPLE OBJECTS
using loops again we can check collision between multiple objects.
If you wish to check collision between all movieclips within another movieclip (common scenario: “enemies” within an “enemy’s” movieclip) you use a “for in” loop to check. The same code can be used to check an array or an object. I recommend the object method: var myObjects = {} as the checking is much faster. Using the typeof command eliminates checking of anything in the enemyParent movieclip that is not a movieclip, like objects or variables. Using the "typeof" command is optional.
Code:
function checkHit(player, ob) {
return player.hitTest(ob);
}
for (var i in enemyParent) {
var enemy = enemyParent[i];
if (typeof enemy == "movieclip") {
if (checkHit(player, enemy)) {
trace("hit");
//this is where you can run your collision response function
}
}
}
By creating an additional function we can pass two movie clips, (which don’t have to be player and enemy) and perform a hitest between them, you could also run a collision response script from the returned Boolean
BASIC MATHEMATICAL COLLISION
CIRCLE : CIRCLE COLLISION
Circle to circle collision is actauly quite easy, all we need to do is get the distance between two objects and compare that distance to the sum of both the movieclips radius (since a circle is round we half its width to get the radius)
Code:
function checkCircleCollision(c1, c2) {
//get the radius
var radius = (c1._width/2)+(c2._width/2);
//get the x distance
var dx = c1._x-c2._x;
//get the y distance
var dy = c1._y-c2._y;
//get the overall distance
var distance = Math.sqrt(dx*dx+dy*dy);
//return a boolean indicating whether the distance is less than the radius
return distance<radius;
}
onEnterFrame = function () {
circle1._x += 2;
circle2._x -= 2;
//check the collision between both circles
if (checkCircleCollision(circle1, circle2)) {
trace("hit");
}
};
The above code assumes two movieclips have the names “circle1” and “circle2”. And both movieclips are centered around their origin's
Hopefully this has helped you, I’ve only covered some basic collision detection and responses, there are many ways to do them. These methods are generally not suited to tile based games and games which require realistic reaction. If your looking for such things, I suggest you try these links:
hitest can find a collision between two movieclips or two points against a movieclip
Wrong. It can find collision between 1 point against movie clip.
var speed = 10
player._y += speed;
if (landscape.hitTest(player._x, player._y, true)) {
player._y -= speed;
}
Actually I find the example bad. There is no need to move movie clip, hittest and then move the movie clip back. Because you can use numbers instead of coordinates you should use it like this:
Code:
var speed = 10
if (!landscape.hitTest(player._x, player._y+speed, true)) {
player._y += speed;
}
if (typeof enemy == "movieclip") {
I have never used typeof so I dont see much use for it. First of, I think it is bad idea to place anything other then enemy movie clips in enemyparent holder clip. But if you really must fill it useless junk, then hittesting with variable or function returns false anyway. Overall, I feel bringing typeof into the example only confuses the idea.
The above code assumes two movieclips have the names “circle1” and “circle2”.
The code also assumes graphics in both circles are centered into registration point. What if your circles have graphics aligned to the left?
You also should at least mention BitmapData.hitTest method which is different from the hittest you describe.
Got a quick question about the hitTest with the shapeflag thingy. How does that one work? I've mainly used the plain hitTest. I see that it takes it's object x and y numbers.
For instance, say you make a hill and I'd use the character for the hitTest to see if it'll go up the hill. I'm guessing that's what the shapeflag does? Thanks
thanks for the advice tony, i've updated the post. Never really wrote a help resource before and it probably shows as with "typeof" i'm using that because whenever you run a "for in" loop within a movieclip it doesn't just list movieclips within the parent movieclips but variables and objects as well. I guess i should explain the typeof better.
walsher, yes thats what a shapeflag can be used for.
Would you please explain where and how do you use code for multiple instance collisions?
Your example is very brief and doesn't tell where code goes, nor which variables need replacing with own instance names?
that stuff is very specific to your game. I just outlined a simple structure which doesn't invlove arrays. But the code should be placed on the root of your movie.
It should really be changed to use arrays, so it makes sense a little more. (with custom data). Maybe someone else can help you out now. I'm a little busy.
I am currently stuck in a project wherein I have created 2 shapes using action script and the main goal was if shape1 collides with shape2-shape2's height & width are displayed on a label.. Is it possible to retrieve shape2 variables when shape1 collides with it?Pls help i really don't know what to do next.....
Hi.
I´d like to know how to make a collision detection method for two movieclips with complex shapes. These two movieclips are child movieclips inside parent movieclips. I´ll call each parent movieclip as obj1 and obj2. So:
Here we have our two objects (movieclips) with its child clips:
_root.obj1.child
_root.obj2.child
How to make a collision detection method that would check the collision between the two child movieclips? And those child clips are not circules, they are shapes with complex format. I think I´d need a bitmap.hitTest or something like that, to test it. And, _root.obj1.child and _root.obj2 rotate on onEnterFrame what gets more complicated. As far as I know, I´d have to work with some matrix. Well, I don´t know matrix methods of use very well.
I know that if you want a precise collision detection of a object that rotates you should use matrix, but I don´t know how.
Tonypa, do you know how to do it?
Just for us to realize it better...it would be something like this:
Code:
function checkCollision(a:MovieClip, b:MovieClip):Boolean{
//Here I believe I´d have to make a localToGlobal for the child movieclip
var aCoord = {x:a._x, y:a._y}
a._parent.localToGlobal(aCoord)
var aPoint:Point = new Point(aCoord.x, aCoord.y)
var bPoint:Point = new Point(b._x, b._y)
var boolean:Boolean = a.hitTest(aPoint, 1, b, bPoint, 1)
return boolean
}
onEnterFrame=function(){
obj1.child._rotation +=3
obj2._rotation -=3
if(checkCollision(obj1.child, obj2)){
trace("hit")
}else{
trace("no hit")
}
}
I believe that the big problem here is:
_root.obj1.child rotate and obj2 rotate. So how can I do it?
Unless a shape-to-shape function was made that I'm unaware of, I know of 4 ways to do complex shape collisions
1) A big loop that tests many coordinates against both shapes using the shape flag; if a point is found that hits both, then the objects hit each other
2) Storing the vector information yourself, and using math to figure out intersections (this would be like creating your own shape-to-shape function, while suffering some speed/functionality)
3) If one of the shapes is convex, and your shapes aren't dynamic, there's a trick you can do that will use a single hitTest with the shape flag
4) There's another trick that employs bitmaps I think to check for collisions, and this may be your best option as I've seen it discussed here before
I could go in depth depending what option you want to go with
The reason I think a shape-to-shape function hasn't been programmed into flash is because they do want to use math (it's the most accurate of methods I listed), but I think vector images can be drawn using up to cubic beziers, and solving for its interaction with other cubic beziers has no general solution, and thus blah blah blah blah....
I use to do with bitmap.hitTest.
But if you have a lighter way to do it (a way that doesn´t use too much processing), better.
Just for you to know that, with bitmap.hitTest I don´t know a way that works, when the two movieclips that are used to test collision are rotating.
The shape of the objects I´m using are complex, their shape are close of a star shape.
I don´t want the way of inserting lots of empty movieclips inside a movieclip for testing points.
If you have another way...better.
Thanks
Last edited by marcelozep; 07-23-2010 at 03:31 PM.
Right, looks like that function was made with AS3 and I just didn't catch it
Seems completely viable
Let's say you have two movieclips on stage in all their vectory goodness
You can use the draw method just fine, the only problem being that the two images will be drawn relative to their own perspective
This can lead to the bitmap being cropped where it ought not be, or the image not being rotated correctly
This means you'll have to make sure the image is between the 0,0 point and whatever width/height you've applied to your bitmaps, and if you want to rotate the image, best rotate the image inside the movieclip instead of the entire movieclip itself
On the stage are two moveclips called "hit1" and "hit2", and inside them are star moveclips called "star" with 2 frames consisting of being purple and red
I made sure the 100x100 bitmap boxes were big enough to always capture the rotating star
And, yeah... is quick example, I don't know how good of quality that was
Right, looks like that function was made with AS3 and I just didn't catch it
Seems completely viable
Let's say you have two movieclips on stage in all their vectory goodness
You can use the draw method just fine, the only problem being that the two images will be drawn relative to their own perspective
This can lead to the bitmap being cropped where it ought not be, or the image not being rotated correctly
This means you'll have to make sure the image is between the 0,0 point and whatever width/height you've applied to your bitmaps, and if you want to rotate the image, best rotate the image inside the movieclip instead of the entire movieclip itself
On the stage are two moveclips called "hit1" and "hit2", and inside them are star moveclips called "star" with 2 frames consisting of being purple and red
I made sure the 100x100 bitmap boxes were big enough to always capture the rotating star
And, yeah... is quick example, I don't know how good of quality that was
I don´t know if it matters. The child movieclips have registration point at the center of the movieclip.
And both hit1.star rotates and hit2 rotates.
Can you code it again?
I´ve tried here and it doesn´t work with this settings.
Well the code's solid, if you have at least CS4 you can look at the source directly (I hate limited reverse-compatibility), though I can't imagine your trouble with recreating it
And it doesn't matter how the child movieclips look as long as the parent movieclips are set-up right
Even if you resize the parents, move them, or rotate them, etc. the content inside them will still be drawn as if though nothing was changed, in this case from (0,0) to (100,100), so don't do anything to them
If you want both stars rotating in this example, just put in
hit2.star.rotation--
as well
Why would you need to?
I already counteracted the only two reasons I could think to need to it in the example:
I might've needed it centered so the star would rotate correctly, but I centered the child movieclip instead
I might've needed it centered so it would follow the cursor better, but I just offset it instead with the "mouseX-39", etc.
Believe me I need the registration point of hit1 on the center and I need it to rotate .
Yes, that´s right.
But, there´s a but.
Inside hit1 I have two movieclips. One movieclip I want to test collision detection, the ship movieclip. The other
I don´t want to, the fire thrust. They compose a draw. But I´d only want to test
the ship.
So, hit1 is a ship with its thrust fire.
I need hit1 center registration point so I can rotate it both.
Last edited by marcelozep; 07-24-2010 at 09:28 PM.