GMS 2 "Stick" and re-orient physics object on-collision


Josh Dawkins

So, I have a grenade object that, when you get certain power-ups, should stick to the first thing it collides with, changing to a flatter sprite and fixture and rotating to an angle that actually looks like it is stuck to the surface of the other object. I'm using the built-in Box2D physics, so I have the code below inside the appropriate case (actually two cases, the first of which just falls into the second) of a switch statement on grenadeType, as part of the grenade's collision event with any other physics object. This successfully sticks the grenade to whatever it first hits, changes the sprite, fixture, and changing to the "set" version of its grenadeType...

The problem is that instead of finding the appropriate rotation, it just keeps spinning after this code executes. Which is strange, considering the rotation should just be set once and then this code shouldn't be executed again (the joints should prevent collision with the object it stuck to, and the grenadeType is different and a different case of the switch statement will be reached when it does collide with something).

For clarification, my solution is based on putting the grenade's origin at the farthest collision point from the other instance's origin (since this should be along the other fixture's edge), and then attempting to find a rotation at which the grenade's new, flatter fixture would overlap the other object (at the very least in bounding box, couldn't find a way to easily do this with the fixture) at both of its bottom corners (the origin is at the bottom center), but not at its top (meaning the top should be pointing away from the other instance). If this fails, then I simply use the angle from the other instance's center of mass to the grenade as a sort of "best guess."

Can anyone help me figure out either what is wrong with this code, or just a better way to handle this sort of problem?

//First, loop through all collision points and determine the one farthest from the other
// instance's origin (which should put it on the edge of its fixture)
var dist, maxDist = 0;
var pointX, pointY, maxDistX, maxDistY;

for(var i = 0; i < phy_collision_points; i++){
pointX = phy_collision_x;
pointY = phy_collision_y;
dist = point_distance(other.x, other.y, pointX, pointY);

if (dist > maxDist){
//This is the farthest point we've checked so far from other's origin, store it
maxDist = dist;
maxDistX = pointX;
maxDistY = pointY;​

//Then change the sprite, fixture, and grenade type
phy_rotation = 0;//Reset rotation to ensure sprite and the new fixture line up correctly
sprite_index = grenadeType == AmmoType.Sticky ? setStickySpr : setLandmineSpr;
physics_remove_fixture(id, fixture);
fixture = physics_fixture_bind(GameControllerObj.setGrenadeFixture, id);
grenadeType *= -1;//The "set" version has the negative of the same value as the "unset" version

//Move the grenade to the point found above
phy_position_x = maxDistX;
phy_position_y = maxDistY;

//Rotate the grenade until we find a point where both bottom corners will touch the
// instance we collided with, but the top won't, and use that as our rotation
var rotSet = false;
var bottomLeft, bottomRight, topCenter;
for(var i = 0; i < 360; i++){
bottomLeft = [phy_position_x + lengthdir_x(5, i + 180), phy_position_y + lengthdir_y(5, i + 180)];
bottomRight = [phy_position_x + lengthdir_x(4, i), phy_position_y + lengthdir_y(4, i)];
topCenter = [phy_position_x + lengthdir_x(3, i + 90), phy_position_y + lengthdir_y(3, i + 90)];

if (position_meeting(bottomLeft[0], bottomLeft[1], other)
&& position_meeting(bottomRight[0], bottomRight[1], other)
&& !position_meeting(topCenter[0], topCenter[1], other)){
phy_rotation = -i;//Remember, physics angles are opposite normal angles
rotSet = true;

if (!rotSet){
//No angle satisfied the conditions, so we'll use the angle from the center of mass instead
// Remember, it's negative because physics angles are opposite normal angles
phy_rotation = -point_direction(other.phy_com_x, other.phy_com_y, phy_position_x,

//Set the bottomLeft and bottomRight points so we can use them to create joints
bottomLeft = [phy_position_x + lengthdir_x(5, -phy_rotation + 180),
phy_position_y + lengthdir_y(5, -phy_rotation + 180)];​
bottomRight = [phy_position_x + lengthdir_x(4, -phy_rotation),
phy_position_y + lengthdir_y(4, -phy_rotation)];​

//Create the joints to stick the grenade to the other instance
var xInc = lengthdir_x(2, -phy_rotation - 90);//x distance from the grenade to other end of joint
var yInc = lengthdir_y(2, -phy_rotation - 90);//y distance from the grenade to other end of joint
physics_joint_distance_create(id, other, bottomLeft[0], bottomLeft[1], bottomLeft[0] + xInc,
bottomLeft[1] + yInc, false);​
physics_joint_distance_create(id, other, bottomRight[0], bottomRight[1], bottomRight[0] + xInc,
bottomRight[1] + yInc, false);​
break;//end of this case of the switch
Last edited by a moderator: