GML [SOLVED] Moving platform collisions

C

ChiHelios

Guest
!SOLVED! Thanks everyone for your help
Solution below for reference

Thankyou everyone, I got everything working, including staying grounded and sticking. I'm really happy with the result. I set an actor parent object so all my other characters can benefit from the collisions.

I stuck with place_meeting and instance_place but now thanks to your explanations I understand WHY I'm using it. The actor collisions always set speed to zero, but recognise grounding, then after I update position I put grounded/sticking velocity back in.

Just like you suggested I did not factor in the platform movements with the player.

///Detect collision

//temporary variables can be used to make sure x and y don't alter
//until the correct part of the step.
var i, tempx, tempy;
tempx = x; tempy = y; canjump = false; sticking = false; grounded = false;
dColl = false; sideColl = false; dobj = self; sideobj = self;

/// X collisions
if (place_meeting(x+vx, y, owall))
{
sideColl = true; sideobj = instance_place(x+vx,y,owall)
while(!place_meeting(tempx+sign(vx),y,owall))
{tempx += sign(vx);}
robj = instance_place(x,y+1,owall);
if sign(vx) = sign(sideobj.vx) and sideobj.vx < 4
{sticking = true;}
vx = 0;
x = tempx;
}
///////////////////////////////////////////////////////////
// Collisions in the y direction
if (place_meeting(x, y+vy, owall))
{
while(!place_meeting(x,tempy+sign(vy),owall))
{tempy += sign(vy);}
if sign(vy)=1 {dobj = instance_place(x,y+vy,owall);
dColl = true;
grounded = true;}
vy = 0;
y = tempy;
canjump =true; inair = false;
}

///////////////////////////////////////////////////////////
// Collisions in x and y direction
if (place_meeting(x+vx, y+vy, owall))
{
//Move to contact
while(!place_meeting(x+vx,tempy+sign(vy),owall))
{tempy += sign(vy);}
y = tempy;
while(!place_meeting(tempx+sign(vx),y+vy,owall))
{tempx += sign(vx);}
x = tempx;
//UPDATE speeds
vx = 0;
vy = 0;
}

///Position

x += vx;
y += vy;

if grounded {vy= dobj.vy;}
if sticking {vx= sideobj.vx;}

After getting the player solid, I used this code for the moving walls. The platforms only push actors like the player, and detect if there will be a crush using place_meeting. I'm planning to implement some different crush strategies by giving each object a script to follow. For the moment though, nice and happy, looking forward to seeing the levels I can make with this.

There was a very specific case where you could be crushed by a corner moving diagonally which caused a bug so I put another clause in which resolves this.

//
/*
Detect if the object is going to collide with an actor
if the actor can be moved then move it
If there will be a crush, stop the object
*/

crushing = false;

//X collisions
while instance_place(x+vx,y,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x+vx,y,oactor);
if coll_obj != noone
with coll_obj
{
//Check for crush
if !place_meeting(x+sign(other.vx),y,owall)
{
//no crush then move
x+=other.vx;
vx = other.vx;
}
else
{
//move up to the crush point
while !place_meeting(x+sign(other.vx),y,owall)
{x+=sign(other.vx);}
other.vx = 0; vx = 0;
}
}
}

//Y collisions
while instance_place(x,y+vy,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x,y+vy,oactor);
if coll_obj != noone
with coll_obj
{
//Check for crush
if !place_meeting(x,y+sign(other.vy),owall)
{
//no crush then move
y+=other.vy;
vy = other.vy;
}
else
{
//move up to the crush point
while !place_meeting(x,y+sign(other.vy),owall)
{y+=sign(other.vy);}
other.vy = 0; vy = 0;
}
}
}

//XY collisions
while instance_place(x+vx,y+vy,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x+vx,y+vy,oactor);
if coll_obj != noone
with coll_obj
{
//Check for x crush
if !place_meeting(x+sign(other.vx),y,owall)
{
//no crush then move
x+=other.vx;
vx = other.vx;
}
else
{
//move up to the crush point
while !place_meeting(x+sign(other.vx),y,owall)
{x+=sign(other.vx);}
other.vx = 0; vx = 0;
}
//Check for y crush
if !place_meeting(x,y+sign(other.vy),owall)
{
//no crush then move
y+=other.vy;
vy = other.vy;
}
else
{
//move up to the crush point
while !place_meeting(x,y+sign(other.vy),owall)
{y+=sign(other.vy);}
other.vy = 0; vy = 0;
}
}
}

I've left the code for reference and will update my first post with my solution. Thanks again for all your help and feel free to offer any suggestions.

ORIGINAL POST
---------------------------------
Hi all!

I'm having some trouble with moving platforms in my platformer. I'm using vx and vy as my velocity vector, and this takes place just before I update the x and y values. This is for my player.

Stationary walls are okay, but when the walls move they overlap the player and it means the wall can pull the player. On top of that, when the moving wall changes moving right to left while pushing the player the player translocates to the right of the screen.

Any advice?

Thanks :)


Code:
///Detect collision

//temporary variables can be used to make sure x and y don't alter
//until the correct part of the step.
var i, tempx, tempy;
tempx = x; tempy = y;
//If no collision to happen, remove existing Coll objects.
if !place_meeting(x+vx,y,owall) or !place_meeting(x,y+vy,owall) or !place_meeting(x+vx,y+vy,owall)
{
rColl = false; dColl = false; uColl = false; lColl = false;
canjump = false;
robj = self; lobj = self; uobj = self; dobj = self;
}
 
/// X collisions
if (place_meeting(x+vx, y, owall))
    {
    if sign(vx) > 0
       {rColl = true; robj =instance_place(x+vx,y,owall);
       while(!place_meeting(tempx+sign(vx-robj.vx),y,owall) and sign(vx)=sign(robj.vx) and vx>robj.vx)
               {tempx += sign(vx-robj.vx);}
       vx = robj.vx;
       }
    else
       {lColl = true; lobj =instance_place(x+vx,y,owall);
       while(!place_meeting(tempx+sign(vx-lobj.vx),y,owall) and sign(vx)=sign(lobj.vx) and vx>robj.vx)
               {tempx += sign(vx-lobj.vx);}
       vx = lobj.vx;
       }
    x = tempx;
    }
///////////////////////////////////////////////////////////
// Collisions in the y direction
    if (place_meeting(x, y+vy, owall))
    {
    if sign(vy) > 0
       {dColl = true; dobj =instance_place(x,y+vy,owall);
       vy = 1.1*dobj.vy;
       while(!place_meeting(x,tempy+sign(vy-dobj.vy),owall) and sign(vy)=sign(dobj.vy) and vy>dobj.vy)
               {tempy += sign(vy-robj.vy);}
       if vy>dobj.vy or sign(vy)!=sign(dobj.vy) {vy = dobj.vy;}
       }
    else
       {uColl = true; uobj =instance_place(x,y+vy,owall);
       while(!place_meeting(x,tempy+sign(vy-uobj.vy),owall) and sign(vy)=sign(uobj.vy) and vy>robj.vy)
               {tempy += sign(vy-lobj.vy);}
       if vy<uobj.vy or sign(vy)!=sign(uobj.vy) {vy = uobj.vy;}
       }
    y = tempy;
    canjump =true; inair = false;
    }
 
Last edited by a moderator:

TheouAegis

Member
well sounds like your first problem is because you are using place_meeting, but that does not care where in proximity to the player the wall is, it only cares if the wall actually collides with a player.
 
C

ChiHelios

Guest
Thanks for your reply, I'm not sure I follow. As I understand it place_meeting moves the instance to where you specify and checks for a collision, then moves it back. Position_meeting just checks the single point. Is this difference enough to make an impact on my code?

From my testing the problem seems to be that the player doesn't know it's going to collide with a moving wall if they move towards each other so they overlap. Not sure how to check for that.
 
T

tomsaram

Guest
Well the strategy you are trying to use here will not work for moving platforms anyway, because place_meeting(x+vx,y+vy,owall) takes into account only the movement of the player and not that of the platform. So there will always be missed collisions especially when the player is not moving.

What I would suggest is a drastic change in which you deal with collisions AFTER the objects has moved and acquired their new position. This way you only need to move the player object out of the wall along the correct direction. The general strategy will look like this:

obj = instance_place(x,y,owall);
while( obj >0 )
{
while(place_meeting(x,y,obj))
{
//move the object by 1 pixel​
}
obj = instance_place(x,y,owall);​
}
 
C

ChiHelios

Guest
Ahh thank you. I knew I needed to account for this but I thought of doing it in the wall code. This would be more manageable. I'll give it a go and update here
 

TheouAegis

Member
Consider this image:
Code:
                OO
XXXXX     OO
                OO
            XXXXX
The X's are the moving platforms, the O's are the player. A check like place_meeting(x,y+1,owall) in this case would not be advisable because it could detect either of the platforms. So the player could be riding on the lower platform and then if he at any time collides even part-way with the upper platform, the upper platform's mechanics could override the one the player is on. place_meeting() cares more about the entire bounding area of the instance than it does about the coordinates. position_meeting() alleviates that issue by ignoring the bounding box and being concerned entirely with the coordinates provided. So (x,y+1) actually means something with position_meeting(), whereas it is almost negligible with place_meeting().

Then there is scenario 2:
Code:
       OO
       OO
       OO
XXXX XXXX
Which owall is the player colliding with? If one is moving up and the other is moving down, for example, using place_meeting() would tell you the player has a platform under him but doesn't tell you which of the two it is. Logically, the one moving up should take precedence, but you can't know definitively which one that is using place_meeting(). Using position_meeting() or instance_position() you can find out precisely which one is moving up and which one is moving down.
 
How I handle moving platforms:

1. Move the player treating all walls/platforms as static.

2. Move the moving platforms pushing the player as they collide or stand on.

2a. With each moving platforms, check 1 px up for players and enemies. Make a list of them.

2b. With each moving platform, check 1 px in moving direction for players and enemies. Add those to list if not in list.

2c. With all in list, move (if possible) 1 px in direction platform is moving.

2d. Check once more for collision in direction moving 1 px. If there is a collision, decide how to resolve it (kill instance or stop moving platform)

2e. Move platform, if no collisions, 1 px.

2f. Repeat for speed of moving platform.

It's cleaner to move instances up to a collision than to move instances out of a collision. Trying to move instances out of a collision may land that instance into another collision.
 
C

ChiHelios

Guest
Thanks everyone for your help :)

TheouAegis thanks, that makes sense to me now. Placemeeting includes so much area that I don't want to consider. Thank you for taking the time to write that out. You mentioned things I hadn't considered yet

So what I can do then too bout need to fully restructure my code is to remove any relative motion from my existing code to treat the other objects as stationary, then build a list of objects which will collide with the player when they move. Then in the players end step I can move the player or implement some crush detection like you said strawberry

Is that right? Cheers
 

DesArts

Member
Are there any platforms you stand on that move down?

Just in case:
That wont work with 'push' code since its not pushing the player its moving away, so you'll get jittering and have the player fall, connect, fall, connect etc.

Typically what you need to do is just use normal collision code, but you must figure out the instance below you at all times. Then just before you actually move the player due to the collision, add the instance under you's motion to the player's x and y which means it'll move you wherever you need, even down. The platform speeds would need to be calculated before the player's collision code also. To keep this working, you need to check more than one pixel under the player, up to the amount the platforms move down (only if they're still on the ground, only check one under if they're in the air)

Order of events:
-Platform moves
-If grounded (which they are, they're stand on the platform in this example), player checks around 4px below for the object it's on. (This only works if the platform moves no faster than 4px, so change that variable to the maximum downward platform speed, shouldnt be any bigger than 6-7 though or you'll have other issues)
-Platform is found, player is moved with it's y speed
-Normal collision code at adjusted position

With left and right it's the same, but simply move by the platform's x speed, no need for any other crazy checks.

Not sure if any of this matters but I'm leaving it here as reference anyway.
 
Last edited:
C

ChiHelios

Guest
Thanks, I will consider that because I'm hoping to use these 'platforms ' as the base for some big enemies so you can get pushed around and run on top of them.

It may have been easier with physics but I wanted to do out this way to really understand whatI was doing a bit better. I'm okay with some downward moving objects getting away from the player
 

Yal

🐧 *penguin noises*
GMC Elder
From my experiences, there's several different cases where the player needs to move, and if you don't realize this, it's easy to make mistakes.
  • The platform is moving into a player. (There's 4 cases of this, and you need to check both where the player is compared to the platform AND which direction it's moving in to tell if any of them applies)
  • The player is standing on top of the platform while it moves sideways or down. (When a platform moves diagonally upwards, make sure this case doesn't trigger both this and the previous case)
So in total, there's 7 cases to care about... less for platforms that will only ever move along one axis.
 
C

ChiHelios

Guest
Thank you everyone, I got everything working, including staying grounded and sticking. I'm really happy with the result. I set an actor parent object so all my other characters can benefit from the collisions.

I stuck with place_meeting and instance_place but now thanks to your explanations I understand WHY I'm using it. The actor collisions always set speed to zero, but recognise grounding, then after I update position I put grounded/sticking velocity back in.

Just like you suggested I did not factor in the platform movements with the player.

///Detect collision

//temporary variables can be used to make sure x and y don't alter
//until the correct part of the step.
var i, tempx, tempy;
tempx = x; tempy = y; canjump = false; sticking = false; grounded = false;
dColl = false; sideColl = false; dobj = self; sideobj = self;

/// X collisions
if (place_meeting(x+vx, y, owall))
{
sideColl = true; sideobj = instance_place(x+vx,y,owall)
while(!place_meeting(tempx+sign(vx),y,owall))
{tempx += sign(vx);}
robj = instance_place(x,y+1,owall);
if sign(vx) = sign(sideobj.vx) and sideobj.vx < 4
{sticking = true;}
vx = 0;
x = tempx;
}
///////////////////////////////////////////////////////////
// Collisions in the y direction
if (place_meeting(x, y+vy, owall))
{
while(!place_meeting(x,tempy+sign(vy),owall))
{tempy += sign(vy);}
if sign(vy)=1 {dobj = instance_place(x,y+vy,owall);
dColl = true;
grounded = true;}
vy = 0;
y = tempy;
canjump =true; inair = false;
}

///////////////////////////////////////////////////////////
// Collisions in x and y direction
if (place_meeting(x+vx, y+vy, owall))
{
//Move to contact
while(!place_meeting(x+vx,tempy+sign(vy),owall))
{tempy += sign(vy);}
y = tempy;
while(!place_meeting(tempx+sign(vx),y+vy,owall))
{tempx += sign(vx);}
x = tempx;
//UPDATE speeds
vx = 0;
vy = 0;
}

///Position

x += vx;
y += vy;

if grounded {vy= dobj.vy;}
if sticking {vx= sideobj.vx;}

After getting the player solid, I used this code for the moving walls. The platforms only push actors like the player, and detect if there will be a crush using place_meeting. I'm planning to implement some different crush strategies by giving each object a script to follow. For the moment though, nice and happy, looking forward to seeing the levels I can make with this.

There was a very specific case where you could be crushed by a corner moving diagonally which caused a bug so I put another clause in which resolves this.

//
/*
Detect if the object is going to collide with an actor
if the actor can be moved then move it
If there will be a crush, stop the object
*/

crushing = false;

//X collisions
while instance_place(x+vx,y,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x+vx,y,oactor);
if coll_obj != noone
with coll_obj
{
//Check for crush
if !place_meeting(x+sign(other.vx),y,owall)
{
//no crush then move
x+=other.vx;
vx = other.vx;
}
else
{
//move up to the crush point
while !place_meeting(x+sign(other.vx),y,owall)
{x+=sign(other.vx);}
other.vx = 0; vx = 0;
}
}
}

//Y collisions
while instance_place(x,y+vy,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x,y+vy,oactor);
if coll_obj != noone
with coll_obj
{
//Check for crush
if !place_meeting(x,y+sign(other.vy),owall)
{
//no crush then move
y+=other.vy;
vy = other.vy;
}
else
{
//move up to the crush point
while !place_meeting(x,y+sign(other.vy),owall)
{y+=sign(other.vy);}
other.vy = 0; vy = 0;
}
}
}

//XY collisions
while instance_place(x+vx,y+vy,oactor)
{
//Find the instance to be pushed
coll_obj = instance_place(x+vx,y+vy,oactor);
if coll_obj != noone
with coll_obj
{
//Check for x crush
if !place_meeting(x+sign(other.vx),y,owall)
{
//no crush then move
x+=other.vx;
vx = other.vx;
}
else
{
//move up to the crush point
while !place_meeting(x+sign(other.vx),y,owall)
{x+=sign(other.vx);}
other.vx = 0; vx = 0;
}
//Check for y crush
if !place_meeting(x,y+sign(other.vy),owall)
{
//no crush then move
y+=other.vy;
vy = other.vy;
}
else
{
//move up to the crush point
while !place_meeting(x,y+sign(other.vy),owall)
{y+=sign(other.vy);}
other.vy = 0; vy = 0;
}
}
}

I've left the code for reference and will update my first post with my solution. Thanks again for all your help and feel free to offer any suggestions.
 
A

Anton[RUS]

Guest
How to add this stuff at this code?


//begin_y_collisions

var getdown; // jump by combination of two buttons
if (key_down==1 && key_jump_held==1){getdown = 1};else{getdown = 0};

//
troll = instance_place(x,y+vsp,obj_wall);
insidetheplatform = (instance_place(x,y,troll));
if (troll != noone){//
if (y > troll.y){
while (!place_meeting(x,y+sign(vsp),troll)){y-=1};
if (troll.platform == 1){};
if (troll.platform == 0){vsp = 0};
if (troll.breakable == 1){vsp = 0; instance_destroy(troll)};
};};//

troll2 = instance_place(x,y+vsp,obj_wall);
insidetheplatform2 = (instance_place(x,y,troll2));
if (troll2 != noone){//
if (y < troll2.y){
while (!place_meeting(x,y+sign(vsp),troll2)){y+=1};
if (troll2.platform == 1) && (getdown != 0){grounded = 0; jumped =1};
if (troll2.platform == 1) && (insidetheplatform2 != noone){};
if (troll2.platform == 0){grounded = 1; jumped = 0; jumps = jumps_max; vsp = 0};
if (troll2.platform == 1) && (getdown == 0) && (insidetheplatform2 == noone) && (troll2.moving == 0)
{grounded = 1; jumped = 0; jumps = jumps_max; vsp = 0};
};};//
 
J

Jumper

Guest
Do you put the moving wall code in the wall object step? How do you get it to move? I use the follow path and I cant seem to "extract" the velocity values, vx and vy in your code, so it just crashes. Collision code worked flawlessly with only minor adjustments, even alongside my tilecollision, thank you very much for that !!
 
Top