Platformer: Stuck on ground, corners and walls

S

SlashmasterUniv

Guest
Greetings. I have been working on a platformer in Gamemaker: Studio. It has controls a bit different from most platformers. being inspired by many games, platformers or not. These games' (Shovel Knight, Smash Bros., Bayonetta) mechanics being fit into one platformer may cause more problems in the future. For now, let's focus on the task at hand.

I am using a modified version of Shaun Spaulding's platformer tutorial code. I am using an animated 32 x 32 sprite. It's collision mask is Precise, 0 Alpha Tolerance, Left = 0, Top = 0, Right = 33, Bottom = 32 and has "Precise collision checking" and "Seperate Collision Masks" ticked. My wall and ground is a solid 32 x 32 block.

If I fall from too high, my character's feet may get stuck in the ground. This inevitably happens when the player is holding left or right when hitting the ground. The player can also only move while in the air, another problem. When the player encounters a wall, they may either get stuck inside or teleport to the ground. The former of which can be negated by jumping.

I have not created any enemies yet, as I am trying to go step by step. This is my priority problem at the moment.

Here is my script:

Create: Initialize Variables:
Code:
///Initialize Variables
grav = 0.2;
hsp = 0;
vsp = 0;
jumpspeed = 7;
movespeed = 4;
atk = 1;
def = 1;
psy = 1;
Step: Execute a piece of code:
Code:
//Get the player's input

key_right = keyboard_check(vk_right);
key_left = -keyboard_check(vk_left);
key_jump = keyboard_check_pressed(vk_up);
key_jump_held = keyboard_check(vk_up);
key_down = keyboard_check(vk_down);

 //React to inputs
move = key_left + key_right;
hsp = move * movespeed;
if (vsp < 10) vsp += grav;

 if (place_meeting(x,y+2,obj_wall))
{
     vsp = key_jump * -jumpspeed
 }

 //Jumps
 
if (vsp < 0) && (!key_jump_held) vsp = max(vsp,0)

 //Diagonal Collision

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

 //Horizontal Collision
if (place_meeting(x+hsp,y,obj_wall))
{
     while(!place_meeting(x+sign(hsp+1),y,obj_wall))
     {
         x += sign(hsp+1);
     }
     hsp = 0;
}
x += hsp;

 //Vertical Collision
if (place_meeting(x+vsp,y,obj_wall))
{
     while(!place_meeting(x,y+sign(vsp+1),obj_wall))
     {
         y += sign(vsp);
     }     vsp = 0;
} y += vsp;

///There may be spaces when not wanted, due to the copy and paste from GM:S to Facebook to here.
Here is my sprite:
Picture -

In-game strip -

Enhanced strip -


I am giving as much information as I can as to get the best answer. To alleviate some confusion of the long strips, I am not sure if one can loop an idle animation for so many frames before adding a waiting animation.

Any help (or attempts) will be greatly appreciated. Thank you so much.

Warm regards, Slash.
 
P

ProcrastinationIsADrug

Guest
Don't use precise collision checking for your sprite, use the rectangle bounding box.
You don't need the Diagonal collision code as the horizontal and vertical code does this for you.
Remove the +1 from sign functions because it is unnecessary and could cause problems.
In the vertical collision code, you have x+vsp, +vsp should be on the y-axis because it is for your vertical collisions.

I would personally change the while loops to be if statements, this is only because if the player gets stuck in a wall, the loop could become infinite and crash the game. But this is not necessary.

Also, you should try using a parent object for the collision. This way for any object you want to be solid you can make it a child of the parent.
Here is a link to help you with understanding parent objects:

EDIT: Try to use local variables when you don't need a variable to have a global scope, such as:
Code:
var
key_right = keyboard_check(vk_right),
key_left = -keyboard_check(vk_left),
key_jump = keyboard_check_pressed(vk_up),
key_jump_held = keyboard_check(vk_up),
key_down = keyboard_check(vk_down);

 //React to inputs
var move = key_left + key_right;
All of this code can be made local because it isn't going to be needed outside of this script. Local variables get destroyed at the end of a block of code meaning they take up less memory.
 
Last edited by a moderator:
S

SlashmasterUniv

Guest
Thank you for this. This will be great help in the future. However, my problem is still present. Should I edit the code to fit more like this video's code? I am still confused on what to do. Thank you.
 

Slyddar

Member
Remove the +1 from all your sign checks in the collision code, and remove the diagonal collision code.

Also your vertical place_meeting check is incorrect. Needs to be
Code:
if (place_meeting(x, y + vsp, obj_wall))
Your sprite seems to replicate many frames. Is it just the same image with a blinking animation repeated? If so, you could have a separate blinking sprite that you swap to at times when he is idle. Doing something like that would save memory.
 
Last edited:
S

SlashmasterUniv

Guest
Your sprite seems to replicate many frames. Is it just the same image with a blinking animation repeated? If so, you could have a separate blinking sprite that you swap to at times when he is idle. Doing something like that would save memory.
Yes. I do not know how to do that. Also, that code made it so my player doesn't hit the ground, but rather stays 32 pixels above. Also jumping is now impossible.
 

Slyddar

Member
I suggest you go back and replicate Shaun's code.

Some changes I would do:
Code:
vsp += grav;

if key_jump and (place_meeting(x, y+1, obj_wall))
{
    vsp = -jumpspeed
 }
is more efficient as the place_meeting check will not get done if the key hasn't been pressed.

The collisions should be
Code:
 //Horizontal Collision
if (place_meeting(x + hsp, y, obj_wall))
{
    while(!place_meeting(x + sign(hsp), y, obj_wall))
    {
        x += sign(hsp);
    }
    hsp = 0;
}
x += hsp;

 //Vertical Collision
if (place_meeting(x, y + vsp, obj_wall))
{
    while(!place_meeting(x, y + sign(vsp), obj_wall))
    {
        y += sign(vsp);
    }
    vsp = 0;
}
y += vsp;
Also doing this
Code:
key_left = -keyboard_check(vk_left);
is not recommended because later if you want to check if the the key is pressed, you need to check if the value is -1.

I'd suggest just doing it like this.
Code:
var key_right     = keyboard_check(vk_right);
var key_left      = keyboard_check(vk_left);
var key_jump      = keyboard_check_pressed(vk_up);
var key_jump_held = keyboard_check(vk_up);
var key_down      = keyboard_check(vk_down);

 //React to inputs
var move = key_right + key_left;
As suggested above, using local variables, by defining them with 'var' means they are only used for this one events step and discarded. If you don't need them to be saved, create them like that as it's more efficient.

All of this is from Shaun video below, in some form or another.
 
Last edited:
Your sprite seems to replicate many frames. Is it just the same image with a blinking animation repeated? If so, you could have a separate blinking sprite that you swap to at times when he is idle. Doing something like that would save memory.
Actually, I swear I just heard someone on the forums recently say that GM is smart enough to recognised those duplicated frames in an animation and only save it to the texture page once.
 
S

SlashmasterUniv

Guest
Actually, I swear I just heard someone on the forums recently say that GM is smart enough to recognised those duplicated frames in an animation and only save it to the texture page once.
Yes, but I have blinking and waiting animations.
 
S

SlashmasterUniv

Guest
I am using Gamemaker: Studio Ver. 1.4.9999. Unfortunately not Gamemaker: Studio 2. Does this code still work for the GM:S 1.4.9999?
 

Slyddar

Member
Does this code still work for the GM:S 1.4.9999?
Ah ok, well that tutorial from Shaun is about 4 yrs old, and he has found better methods, so probably worth following the latest GMS2 one. The code I posted will be the same, as will most of the new tutorial. The main difference with GMS2 is instances get created on layers, instead of just getting a depth value. So in GMS2 instance_create_layer or instance_create_depth is the same as instance_create in GMS.
 
S

SlashmasterUniv

Guest
Ah, I see. Should I upgrade to GM:S 2? It was something I was thinking about for a while, so I'm not sure. I do like the room editor and the layers feature. Plus, Nintendo Switch porting is a nice touch. I'm not too familiar with the current code, so I think it would be good to get familiar with 2's instead. Any thoughts?
 
S

SlashmasterUniv

Guest
Alrighty, thanks! Can I transfer my current project? I have many sprites and objects made.
EDIT: Is there a specific version I should get? I want to maximize my abilities. I'm thinking GM:S 2 Desktop to start and then upgrading with other kits. Would this still work to transfer?
 

Slyddar

Member
Well the desktop is a good start obviously if you are targeting windows builds. Your project should export over to GMS2 usually quite nicely.
 
I suspect it is when you change sprite to the jumping animation, it gets stuck to the ground and walls when they change back. A simple fix will be to make all the bounding boxes of your animations the same size ie if the bounding box of your walk animation is 300px x400 px, ensure the jumping and idle animations are 300x400 px. place_meeting is a collision between the bounding boxes of the objects so if you have the same bounding box, you should be fine.
 
S

SlashmasterUniv

Guest
I only have an idle animation right now, as to avoid confusion. I would like to get the basic controls out of the way before animations, preferably. I now have Gamemaker: Studio 2 and am trying to import my project. Thank you for the help, everyone! I will most likely update you in a day or two.
 
S

SlashmasterUniv

Guest
So, GMS2. It works.... Better. New problems have arised, however. Now I am unable to move left and I appear to be 1 pixel in the ground, without any real effects.
I looked it over, checked with the tutorial, fixed some things, etc., but I can't fix it. Now, the former is probably a simple oversight, but still. I would like to get this up and running.
Code:
Code:
//Get the player's input
key_right = keyboard_check(vk_right);
key_left = -keyboard_check(vk_left);
key_jump = keyboard_check_pressed(vk_up);

//React to inputs
var move = key_right - key_left;

hsp = move * walkspeed;

vsp = vsp + grav;

if (place_meeting(x,y+1,obj_wall)) && (key_jump)
{
    vsp = -7;
}


//Horizontal Collision
if (place_meeting(x+hsp,y,obj_wall))
{
    while (!place_meeting(x+sign(hsp),y,obj_wall))
    {
        x = x + sign(hsp);
    }
    hsp = 0;
}
x = x + hsp;

//Vertical Collision
if (place_meeting(x,y+vsp,obj_wall))
{
    while (!place_meeting(x,y+sign(vsp),obj_wall))
    {
        y = y + sign(vsp);
    }
    vsp = 0;
}
y = y + vsp;
 

Slyddar

Member
key_left = -keyboard_check(vk_left);
GMS2, congrats! It takes some getting used to, but is a better product in the end imho.

For the left movement, remove the - sign.

For the ground issue, in the sprite, adjust the origin to make the player higher, or check your mask encompasses the whole player.
 
S

SlashmasterUniv

Guest
Aaa thank you so much! I didn't notice that. Again, I thought it was just a little thing I overlooked. Thank you, I shall try this updated.
 
S

SlashmasterUniv

Guest
Problem with sinking still in effect. I did both of these things, but to no avail. It still sinks into the ground. It's only by one pixel, but it looks quite off. Any help? I set the origin to 16x10, and changed mask to full image.
 
S

SlashmasterUniv

Guest
Hello! I'm just looking for a bit of help here. Thanks!
 
S

SlashmasterUniv

Guest
Yes. I am trying it with a square sprite to start off this time around.
 

Slyddar

Member
If you manually adjust the sprites mask by making it 1 pixel longer on the bottom, you can control how high the sprite will be displayed when it's standing/colliding with the obj_wall object.
 
Top