GameMaker Vertical Jump Through Platform Hell

JeanSwamp

Member
Hello guys

I've been for three days trying to find a solution to my vertical jump through platform. I modidied it multiple times, fixing one stuff, but then messing something else.

The jump through platforms work pretty good on themselves, is the vertical moving one that is giving me hell.

I tried making the platform move the player, that worked pretty good for my needs, as I plan out the level design around that to avoid player collision with other solids while standing, problem was a pretty small hickup barely noticiable while landing, where would land, go back to air, and land again almost imposible to notice.

I've tried working with player's vsp, snapping the player to the top of the platform, most of them always end up having the player drop out when the platform reaches the lower level and changes direction.

This is what my collision code looks like.

Code:
//Vertical Collision Jump Through

var falling = vsp > 0;
var platformBelow = instance_place(x, y+vsp, par_through);

if(falling)
{
    if(platformBelow != noone)
    {
        var notClipping = !place_meeting(x, y, platformBelow);
        if(notClipping)
        {
            while(!place_meeting(x, y+1, platformBelow))
            {
                y+= 1;
            }
            vsp = 0;
        }
    }
}

var onPlatform = collision_rectangle(bbox_left, bbox_bottom, bbox_right, bbox_bottom+1, par_through, false, false);


if(onPlatform != noone)
{
    var notInsidePlatform = !collision_rectangle(bbox_left, bbox_bottom-1, bbox_right, bbox_bottom, onPlatform, false, false);   
    if(notInsidePlatform)
    {
        if (input_check_pressed(E_INPUT_SLOT.JUMP) && (input_check(E_INPUT_SLOT.DOWN)))
        {
            image_index = 0;
            var amount = 4;
            y += amount;
            state = states.dropdown
        }
    }

}


var onVerticalPlat = instance_place(x,y+1,oMovPlatVer)

if(onVerticalPlat != noone)
{

    var notInsidePlat = !instance_place(x,y-1,oMovPlatVer)
    if(notInsidePlat)
    {
        vsp = onVerticalPlat.vmove
        grv = 0
    }
}



//Vertical Collision Solids

if (place_meeting(x,y+vsp,par_solid))
{
    while (!place_meeting(x,y+sign(vsp),par_solid))
    {
        y = y + sign(vsp);
    }
    vsp = 0;   
}

y = y + vsp;
Hopefully someone can drop some insight,

Thanks!
 
M

MoncefFennich DZDev

Guest
Bro You Forget
Code:
else
{
y=y+vsp;
}
In The Last Sentence
 

NightFrost

Member
I handle the stuff like this: I check for wall and one-way platform collisions separately, grabbing them to different variables. I will not care about the one-way result if vsp is zero or less, so they basically do not exist when jumping up. When falling down, I do take it into account if the platform's y-position is greater than player's - that is, the player is not already falling through it. Player y-origin is at their feet, platform's is at its top.

But from what you say, your platforms can also be moving on their own?
 

JeanSwamp

Member
I handle the stuff like this: I check for wall and one-way platform collisions separately, grabbing them to different variables. I will not care about the one-way result if vsp is zero or less, so they basically do not exist when jumping up. When falling down, I do take it into account if the platform's y-position is greater than player's - that is, the player is not already falling through it. Player y-origin is at their feet, platform's is at its top.

But from what you say, your platforms can also be moving on their own?
Yeah I check walls and one-way separately, everything works alright in that matter. Now, the problem comes with my vertically moving platform (or elevator, whatever you want to call it), which is a children of the jump through one-way platform, and this is what is giving me hell.

So yeah, my platforms can also move on their own.
 
R

Raff

Guest
Hello !!,

Had the same trouble some time ago. This worked for me:

In the step event of the vertical platform after "y" velocity incrementation :

Code:
if(instance_place(x, bbox_top -1, object_player) && !instance_place(x, lerp(bbox_top+1, bbox_bottom, 1), object_player))
{
    with(object_player)
    {
        y += other.velY;
    }
}
But as you said, some times you fix something and another thing starts to go wrong. Hope it helps you.
 
Last edited by a moderator:

NightFrost

Member
So yeah, my platforms can also move on their own.
Lifts / elevators / moving to any direction and affecting others is a whole other ballpark. I recently implemeted to good effect the idea presented in this blog post. The basic idea is, when an elevator moves, it checks if anyone is riding it (stands on top) and makes a list of them. Then it tries to move itself and checks if there are overlaps with other things. If so, it tries to push them into movement direction by the amount of overlap. Once done, it moves the riders to same direction. Turning that into GML, I made my standard movement into scr_move_x(amount) and scr_move_y(amount) scripts. Player, when moved normally, calls them as usual. But when an elevator pushes something, it calls the script in context, like with(_Collision){ scr_move_x(_Overlap)}. This way, player may get moved multiple times a step, by controls and by anything that pushes them or has them for a ride.

Implementing all that may take major code rewrites if you want to test it, but in the end it works really well.
 

Bentley

Member
Lifts / elevators / moving to any direction and affecting others is a whole other ballpark. I recently implemeted to good effect the idea presented in this blog post. The basic idea is, when an elevator moves, it checks if anyone is riding it (stands on top) and makes a list of them. Then it tries to move itself and checks if there are overlaps with other things. If so, it tries to push them into movement direction by the amount of overlap. Once done, it moves the riders to same direction. Turning that into GML, I made my standard movement into scr_move_x(amount) and scr_move_y(amount) scripts. Player, when moved normally, calls them as usual. But when an elevator pushes something, it calls the script in context, like with(_Collision){ scr_move_x(_Overlap)}. This way, player may get moved multiple times a step, by controls and by anything that pushes them or has them for a ride.

Implementing all that may take major code rewrites if you want to test it, but in the end it works really well.
Thanks for posting that link. I found it extremely helpful.

I get the idea behind setting "collideable" to false in the player's move_x/y scripts so that he will ignore a particular solid. But what would be an example of when that would be used?

At first I thought it was used to prevent the actor from colliding with the solid that's pushing him. But that can't be right because the solid pushes him out of the way so that there's no overlap.
 

NightFrost

Member
Thanks for posting that link. I found it extremely helpful.

I get the idea behind setting "collideable" to false in the player's move_x/y scripts so that he will ignore a particular solid. But what would be an example of when that would be used?

At first I thought it was used to prevent the actor from colliding with the solid that's pushing him. But that can't be right because the solid pushes him out of the way so that there's no overlap.
The code in the blog sets Collideable to false exactly for that reason. The way the code has been written, it first moves the solid and only then checks for collisions, instructing all overlapping actors to move out of the way. So while the actors are being told to move, they are in collision with the solid that told them to move, and have to ignore it. The reason for this procedure is, when they are actually overlapping you can calculate from their bounding box edges how much they overlap, and have the actor move that amount.

I adapted that part a little differently, making it more like the while-loop check you see a lot in GMS. First my code checks for collisions on the whole move distance, the usual place_meeting(x + vspd, y, obj_solid_parent) thing. If there are no collisions, the solid is moved the whole distance. If there are, a while(vspd != 0) loop will run instead. It will check for collisions with instance_place(x + sign(vspd), y, obj_solid_parent) and if it meeting anything, instructs them to move one pixel (that is sign(vspd)). Then vspd is decremented by its sign, so the while-loop will eventually end. The vspd obviously needs to be an integer for this, so I also coded in the decimal part wrangling with the Remainder-variables.

That way, you don't need to add the Collideable-state checking into the code. This is of course slightly slower as in case of collisions you are making a collision check for each pixel moved, instead one for the entire move, but that's how the commonly used collision checking in GMS has always operated.

Also note that if you expect your solids to push multiple actors at the same time (mine didn't) you should use instance_place_list() instead to find all the actors. The blog's code does that, and more actually as it loops through every single actor to find the ones in collision. I guess the language it is written in (C# apparently) doesn't have dedicated collision detection commands.
 

Bentley

Member
it first moves the solid and only then checks for collisions,
Oh my goodness haha. I forgot the solid doesn't move 1pixel at a time.

Thanks for the explanation.

I adapted that part a little differently, making it more like the while-loop check you see a lot in GMS. First my code checks for collisions on the whole move distance, the usual
place_meeting(x + vspd, y, obj_solid_parent)
thing. If there are no collisions, the solid is moved the whole distance. If there are, a
while(vspd != 0)
loop will run instead. It will check for collisions with
instance_place(x + sign(vspd), y, obj_solid_parent)
and if it meeting anything, instructs them to move one pixel (that is
sign(vspd)
). Then
vspd
is decremented by its sign, so the while-loop will eventually end. The
vspd
obviously needs to be an integer for this, so I also coded in the decimal part wrangling with the
Remainder
-variables.
Ah so you have it where the solid also moves 1 pixel at a time? And then if it runs into an actor it moves that actor 1 pixel at a time as well? That seems like it gives you more control. I'll probably end up doing it that way.
 
Last edited:
Top