Collision detection
Last updated 31 January 2013
ExtremePhysics handles collisions automatically, so usually you don't have to do anything when two bodies collide. But sometimes you might want to do something special when two bodies collide, for example destroy one of the bodies. If you use the build-in GM collision events or functions, the results won't always be correct. This is because Game Maker calculates collisions based on the sprites, which is not very precise. If you use ExtremePhysics, it's usually better to use ExtremePhysics collision detection functions.
Collisions between existing bodies
Collisions between existing bodies can be detected with contacts. Whenever two bodies collide, ExtremePhysics will create a contact automatically when you call ep_world_update_contacts.
You can use this script to check whether two bodies are touching:
// bodies_touch(world,body1_id,body2_id)
// returns whether two bodies are touching
var s, c;
for(s = ep_body_first_shape(argument0, argument1); s!=0; s = ep_shape_next(argument0, argument1, s)) {
for(c = ep_shape_get_first_contact(argument0, argument1, s); c!=0; c = ep_shape_get_next_contact(argument0, argument1, s, c)) {
if ep_contact_get_body1(argument0, c)=argument2 or ep_contact_get_body2(argument0, c)=argument2 {
return true;
}
}
}
return false;
This isn't very efficient though, especially if you call this for all possible collisions. There's a better way to do it. First, do this for all bodies in the game, right after creating them:
ep_body_set_uservar(global.world, body, 0, id);
This will store the id of the object that created the body in user variable 0. Now you can use this to check all collisions for one instance:
var s, c, b, object;
for(s = ep_body_first_shape(global.world, body); s!=0; s = ep_shape_next(global.world, body, s)) {
for(c = ep_shape_get_first_contact(global.world, body, s); c!=0; c = ep_shape_get_next_contact(global.world, body, s, c)) {
b = ep_contact_get_body1(global.world, c);
if b=body {
b = ep_contact_get_body2(global.world, c);
}
object = ep_body_get_user_var(global.world, b, 0);
if object.object_index=obj_block {
// this instance is touching a block
}
}
}
Or if you want to check all collisions at once (this is sometimes the most efficient solution):
var c, b1, b2, object1, object2;
for(c = ep_world_first_contact(global.world); c!=0; c = ep_contact_next(global.world, c)) {
b1 = ep_contact_get_body1(global.world, c);
b2 = ep_contact_get_body2(global.world, c);
object1 = ep_body_get_uservar(global.world, b1, 0);
object2 = ep_body_get_uservar(global.world, b2, 0);
if object1.object_index=obj_block and object2.object_index=obj_player {
collision_block_player(object1, object2);
} else if object1.object_index=obj_player and object2.object_index=obj_block {
collision_block_player(object2, object1);
} else if object1.object_index=obj_player and object2.object_index=obj_ball {
collision_player_ball(object1, object2);
} else if object1.object_index=obj_ball and object2.object_index=obj_player {
collision_player_ball(object2, object1);
} /* ... */
}
collision_block_player and collision_player_ball are scripts you should write yourself. The two arguments hold the ids of the objects that are colliding. This way every collision is handled exactly once every step.
You can also use the GM function object_is_ancestor if you want to use parents. For example, if you have three types of blocks, and obj_block is the parent of all three types, you could do this:
if object_is_ancestor(object1.object_index, obj_block) and object2.object_index=obj_player {
collision_block_player(object1, object2);
} else if object1.object_index=obj_player and object_is_ancestor(object2.object_index, obj_block) {
collision_block_player(object2, object1);
}
Collisions with virtual shapes
Virtual shapes are shapes that don't actually exist, they are only used to check collisions. The collision functions that use virtual shapes will simply return whether a collision would have occurred if there was a shape at that position, but there is no real collision (i.e. the real body won't bounce back).
The following functions use one virtual shape and one real body/shape:
- ep_world_collision_test_box
- ep_world_collision_test_line
- ep_world_collision_test_circle
- ep_world_collision_test_polygon
- ep_body_collision_test_box
- ep_body_collision_test_line
- ep_body_collision_test_circle
- ep_body_collision_test_polygon
- ep_shape_collision_test_box
- ep_shape_collision_test_line
- ep_shape_collision_test_circle
- ep_shape_collision_test_polygon
After using one of these functions, you can call ep_world_get_collision_body and ep_world_get_collision_shape to get the id of the bodies and shapes that caused the collision.
The following functions use two virtual shapes:
Comments
This page used to have a comment section. It's closed now; what remains are the questions I answered.
Huder · 2 June 2011, 17:19
I'm making bullets and i wonder how get an instance's id which is hit by bullet.
I'm destroying this bullet if was hit scenery object or player, but here is problem. I want ignore collision with bullet owner. So every bullet have variable bulletOwnerId.
Maarten Baert author · 16 June 2011, 21:36
You can use ep_world_get_collision_body and ep_world_get_collision_shape. I've added this to the tutorial now.
B35a13 · 24 June 2011, 21:12
Is there a possibility that these codes don't work anymore?
When i try to use them, i get some errors.
I figured out that
=
.
Nevermind, i figured it out.
Maarten Baert author · 2 August 2011, 02:38
Thanks for telling me, I've corrected the code now.
Brunoxzx · 19 August 2011, 08:10
Hi Maarten. Do you think if i use a "switch" instead a "else if" in your code to get collisions the code will be quiker.
Example.
Thank you for your kind attention.
Sorry for my bad english.
Maarten Baert author · 22 August 2011, 22:49
@Brunoxzx: That won't always work:
1^4 = 001^100 = 101 = 5
3^6 = 011^110 = 101 = 5
Brunoxzx · 1 September 2011, 06:35
O.O you have all the reason XD.
I have another question, I'm making a simple bullet, creating a new static body with 32 of speed and all goes good except that when the bullet collides with a crate the generated impulse force its huge.
what can i do to reduce the impulse force of my bullet.
Sorry for my bad inglish (again).
Maarten Baert author · 11 September 2011, 01:38
Reduce the mass of the bullet. Or if you are using ep_body_calculate_mass, reduce the density of the shape(s).
Hai273996803 · 25 June 2012, 12:21
excuse me,"ep_world_get_collision_body" and "ep_world_get_collision_shape" how to use?? They "index" means what?I do not understand?
Killpill · 4 July 2012, 02:50
I'm having a bit of a problem with collisions.
One of your examples uses substeps to stop objects that move fast from going through walls and such. My game needs this.
However, when using substeps, the contacts from collisions are destroyed before the objects can check for collisions (most of the time, if the contacts are present on the last substep they'll detect the collision).
Is there a way around this without making a collision detection system that runs in the substep code and reports back to the objects?
Maarten Baert author · 6 July 2012, 19:49
index = 0 means the first collision found, index = 1 means the second collision, ...
The collision functions return the total number of collisions found.
No, unfortunately not. This is exactly what I did in The Machine.
You can speed it up a bit by only checking new contacts: use ep_world_last_contact to get the id of the newest contact, then use ep_contact_previous until you get a contact with an id that's less than or equal to the id of the newest contact in the previous substep (or 0 which means there are no more contacts). Now you won't be checking the same contact multiple times per step.
Killpill · 7 July 2012, 01:18
Woah, thank you. I can also use that to make sure "hit something" sounds don't play twice.
Is there a way to apply a circular impulse to the world that effects all bodies within a range? I'm trying to make an explosion that doesn't rely on multiple small bodies and only lasts one step. Here is what I'm using now:
I also don't know if there is a way to get the contact point of a virtual shape collision, so I used raycast to make a guesstimate. I have no idea if I did the force values correctly, something seems off sometimes. Using raycast in the first loop would reset the world collisions, so I had to use two.
Moving on.. I looked at the spiderball tutorial and it looked promising for making bodies "stick" to slopes instead of using them as ramps/falling down. I can't, however, get that to work unless I use a circle instead of a box shape. For my slopes, I am using a polygon triangle. Any advise you could offer for that?
I would upload the file, but the project is rather large and messy - as we are implementing Extreme Physics late in development.
Thanks for helping out - and for making this DLL. I hope we'll be able to release our game with your DLL powering the physics.
Maarten Baert author · 12 July 2012, 01:41
You can't, in fact I'm skipping most of that code completely to save time.
I don't really understand why you are using raycasting in the first place. You're always raycasting directly towards the center of the body. Since the force is pointing in exactly the same direction, the result would be exactly the same if you had applied the force directly to the center of the body, which is a lot easier to do.
It should work for any shape. Can you show me the code?
Killpill · 15 July 2012, 01:06
I do things that way to make objects rotate. Although I am pointing to the center, the force is not applied to the center.
I deleted the old code for sticking to slopes but the reason it didn't work was that is the spiderball example, you used rotation to make the ball move. The rotation wasn't affected by the force that pushed the ball into the wall, so the ball still moved smoothly. I can't use rotation with a box, so I use forces. By pushing the box into the slope, I change the speed it climbs the slope.
Maarten Baert author · 12 August 2012, 20:32
You can also use 'tangentvelocity' (ep_shape_set_material) instead. It should work better (just as good as rotation works for circles).
Mattinator123 · 5 September 2012, 05:56
im new here and i think im missing something
no longer exists?
plz help i need to get my water game working!
Maarten Baert author · 7 September 2012, 23:51
The right name is ep_body_first_shape. I will change it.
Claudiu_hash · 9 September 2012, 00:06
hey
i have just a little programming concept problem over here and after a few days of searching for a solution i kind of reached the end of my patience and understanding.
so there are "n" number of instances of blue_block and "m" number of instances of pink_block
both block types have health and mass
when a blue_block collides with a pink block the health of the blue_block is lowered by:
d = m*sqr(v)/2
where
d is the damage done to blue_block
v is the velocity of the pink_block
m is the mass of the pink_block
when the damage is higher then health then the object is destroyed.
the problems are:
1. i need the damage to be done only on first collision. so that if block_blue stays in contact with block_pink no damage should be done anymore.
2. if a pink_block touches a blue_block it does its damage and they remain in contact and another pink_block touches the same blue_block it should do its damage to.
3. and of course if a blue_block touches another blue_block there should be no damage.
4. when a dynamic block (blue or pink) touches a static block ... strange things happen because the static block does not have mass and velocity.
5. a pink_block touches a blue_block; after that they bounce and get in contact another time, damage should be done twice to the blue_block.
and the same damage system should apply to the pink_blocks on collision with the blue_blocks
any ideea would be of great help.
Maarten Baert author · 17 September 2012, 13:33
You can get the velocity difference with ep_contact_get_point1_normalveldelta/ep_contact_get_point2_normalveldelta. You probably want to add those two together. This should work for static bodies too. For the mass, try to use
That's that I use inside ExtremePhysics. Static bodies essentially have infinite mass, and 1 / infinite is 0, so if the first body is static it becomes
You can do this more easily if you save the inverted mass instead of the mass itself (i.e. 1 / mass1 instead of mass1) because that makes it possible to give bodies an infinite mass (just set the inverted mass to 0).
Normally the velocity should be almost zero after the first impact, but if you want to check that explicitly you can use the contact id for that. A few posts back (slightly different situation, but the idea is the same):
You can use user variables to store the id of the objects that the bodies represent, and once you have the ids you can use id.object_index to check whether it's a blue or pink block.
Kalspar · 6 April 2013, 02:35
hi... yeah im kinda new to the game making thing... and i am making a game called "The Individual" and the main character is a square, the other character's in this game(AI's)are circle's, and your character is now tired of being the only square and goes off to find someone just like him...
My problem is ,is that i can't make him roll (with physics) and move left and right when ever i press left...or right. i know how to make him move and rotate, but he wont move.
here... i will try and make more sense. say if i hit the right arrow key, he stay's there and rotates. only rotates, there is no roll or movement to the right...he just sit's there and rotates, and if i hit space bar he jump's up and moves to the right, and when he hit's the ground. it start's all over again. all i want to know is how to give him tilting physics and rolling physics. kinda like dice movement. hope you under stand and that you can help me :/. thank you
Maarten Baert author · 7 April 2013, 15:04
You are probably trying to change the position and orientation directly, i.e. with ep_body_set_position. You should never do this, because to ExtremePhysics it will look like the body just teleported to the new location. This means collisions won't be simulated realistically, like you saw. Instead you should use ep_body_set_velocity_center, and let ExtremePhysics handle the movement.
Melph93 · 12 June 2013, 11:08
I read in a comment above that I could use the ep_contact_get_point1_normalveldelta/ep_contact_get_point2_normalveldelta functions to get the difference in velocity between two objects (for example, my car and a barrel). I am unsure of how I would use them however. Where would I call them from, and what would I use as the contact_id argument? Thanks again!
Edit: sorry I did not just edit my previous post, it wasn't appearing before I made this comment.
Edit Again: I have figured out the answer to my own question, using posts you previously had made on other pages. Anyway, if anyone had the same question, this is how it worked for me.
After using ep_world_update_contacts() and ep_world_simulate_step() calls, I went through a for loop of all the contacts as demonstrated on this page...
http://www.maartenbaert.be/extremephysics/tutorials/collision-detection/
Then, using the contact in the for loop (variable c), you can call ep_contact_get_point1_normalveldelta/ep_contact_get_point2_normalveldelta functions to get the velocities (I added them together to get the difference in velocities). You can use the ep_body_get_mass() fuction to get the masses of the objects.
For my kinetic force function, I then did mass*velocity^2, but you can obviously use these variables to calculate whatever force you want.
Thanks a ton for the excellent scripts Maarten!
Maarten Baert author · 14 June 2013, 23:25
That's almost correct - you shouldn't add the two together, you should take the maximum of the two. Otherwise edge-edge contacts will have a velocity that's twice as high. This doesn't make a difference when one of the two shapes is a circle, since circles can't have edge-edge contacts (there are no edges). You should also check that the velocity is positive, because if it's negative that means the two objects are moving away from each other (you won't see this very often though).
If you want to do it 100% right, you have to check which contact points are active and only check the active ones. In your case it won't make a difference though, because ep_contact_get_point1_normalveldelta/ep_contact_get_point2_normalveldelta will just return 0 when the contact point is not active.
J_dotson · 19 June 2013, 05:58
Sorry for re-posting. There may still be something wrong with what I have done earlier, but I believe the tank does not react to the collision properly is because the force should be applied normal to the shape. Would there be a way using my current method to determine the normal of the edge that makes contact with the bullet?
Maarten Baert author · 19 June 2013, 15:36
First of all, 60 bodies per second shouldn't be a problem if you also destroy them fast enough. EP can handle hunderds of bodies. So I think the performance problem is somewhere else (maybe you forgot to destroy the bodies, or maybe the way the bullets are drawn is slow).
What you're looking for is raycasting. Take a look at the platform example, you can shoot lasers with the mouse and they also apply a force to the target.
I think the problem you have is caused by this code:
The first line overwrites xx, but you still need it in the second line. This produces incorrect results.
Also, the last argument of ep_body_apply_impulse should be 'true' in this case.
You could make the force normal to the shape, but I don't think it's more realistic. It would make sense if the bullets bounce off the tank, but they probably won't do that in reality ;).
J_dotson · 19 June 2013, 21:15
Thanks for the help! I fixed the problem with the coordinate conversions and it works great. I think I prefer this method to ray-casting because the effect isn't instant and is dependent on how far away the target body is.
As far as the normal issue, seeing it now, I agree that the bullets should not bounce perfectly off the tank, but I would like to calculate the normal for other purposes. Would you suggest just doing a line to line collision with each edge of the target polygon or is there a faster way?
As far as creating a new body for each bullet, I wasn't as much worried about the computation time needed to create the bodies as much as the large amount of potential contacts. For instance, in your explosion example, I had a significant fps decrease when the explosion occurred near several bodies. But if this is an option, do you think it would be faster or slower than the current method. I'm expecting significant graphical performance issues and I want to make sure this is as fast as possible. Thanks again! :)
Maarten Baert author · 23 July 2013, 19:59
If there's an actual collision, you can get the normals from the contact with ep_contact_get_normal_x and ep_contact_get_normal_y. Otherwise it's not possible.
Raycasting is definitely faster than creating separate bodies, because it has to be done only once. Bodies exist for multiple steps, and each time collisions are calculated. It gets worse when there are actual collisions because that also creates contacts (which are constraints, so they slow down the solver phase, especially if the number of iterations is high).
A55h411 · 14 July 2014, 14:52
Hello. I used this code to make collision detecting for player's object in my game:
I have a question: how can i get the id of the shape that collides with player's object?
Maarten Baert author · 19 July 2014, 21:22
Like this: