SOLVED Doesn't use correct sprite when moving

Klanes

Member
Hello everyone. First of all I have to say that my English is not very good and that I am a real code noob.
Well, I am using an 8 position motion setup based on this code:
GML:
//movement
hsp = (keyboard_check(ord("D")) || keyboard_check(vk_right)) - (keyboard_check(ord("A")) || keyboard_check(vk_left));
vsp = (keyboard_check(ord("S")) || keyboard_check(vk_down)) - (keyboard_check(ord("W")) || keyboard_check(vk_up));

hsp *= move_speed;
vsp *= move_speed;

//moving
var moving = hsp!=0 || vsp!=0;

//states
switch(state){
    case "idle":
        //change to move
        if (moving) state = "move";
       
    break;
    case "move":
        //change to idle
        if (!moving) state = "idle";
    break;
    }

//direction
if (moving){
    var _dir = point_direction(0, 0, hsp, vsp);
    dir = floor(_dir/90);
}

//sprites
sprite_index = asset_get_index("spr_player_" + state + string(dir));

//movement
x += hsp;
y += vsp;
On the other hand I have 4 sprites for the "idle" and 4 sprites for "move":
1589820469279.png
Everything seems to be fine but when moving diagonally lower left use the sprite "spr_player_move2" (moving left), which is ok, but when moving diagonally lower right use the sprite spr_player_move3 "(moving right), which not what I want. I would like you to use the "spr_player_move0". The same goes for diagonal movements up. Here is the problem:

In summary, one of the move diagonals makes them with the correct sprite (running sideways) and the other does not (running front and from behind).

Help please! Thanks in advice!
 

Nidoking

Member
Well, you're doing floor on a direction, which will round it clockwise. If you want to round it a different way, you'll need to do your own calculation. Since there are only eight possible directions, why not use a switch or nested if with hsp and vsp?
 

Klanes

Member
Well, you're doing floor on a direction, which will round it clockwise. If you want to round it a different way, you'll need to do your own calculation. Since there are only eight possible directions, why not use a switch or nested if with hsp and vsp?
Thanks for your answer, but as I was saying, I am a complete noob (this setup is taken from a tutorial). How could I do what you say? :\
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
GML:
dir = floor(_dir/90);
Take that line and change it to:

GML:
dir = floor((_dir + 45)/90);
See if that helps.
 

Sabnock

Member
Hello everyone. First of all I have to say that my English is not very good and that I am a real code noob.
Well, I am using an 8 position motion setup based on this code:
GML:
//movement
hsp = (keyboard_check(ord("D")) || keyboard_check(vk_right)) - (keyboard_check(ord("A")) || keyboard_check(vk_left));
vsp = (keyboard_check(ord("S")) || keyboard_check(vk_down)) - (keyboard_check(ord("W")) || keyboard_check(vk_up));

hsp *= move_speed;
vsp *= move_speed;

//moving
var moving = hsp!=0 || vsp!=0;

//states
switch(state){
    case "idle":
        //change to move
        if (moving) state = "move";
      
    break;
    case "move":
        //change to idle
        if (!moving) state = "idle";
    break;
    }

//direction
if (moving){
    var _dir = point_direction(0, 0, hsp, vsp);
    dir = floor(_dir/90);
}

//sprites
sprite_index = asset_get_index("spr_player_" + state + string(dir));

//movement
x += hsp;
y += vsp;
On the other hand I have 4 sprites for the "idle" and 4 sprites for "move":
View attachment 31173
Everything seems to be fine but when moving diagonally lower left use the sprite "spr_player_move2" (moving left), which is ok, but when moving diagonally lower right use the sprite spr_player_move3 "(moving right), which not what I want. I would like you to use the "spr_player_move0". The same goes for diagonal movements up. Here is the problem:

In summary, one of the move diagonals makes them with the correct sprite (running sideways) and the other does not (running front and from behind).

Help please! Thanks in advice!
you might also want to look at this function

GML:
keyboard_set_map(key1, key2);
 

Klanes

Member
GML:
dir = floor(_dir/90);
Take that line and change it to:

GML:
dir = floor((_dir + 45)/90);
See if that helps.
Thanks for the code. Unfortunately it doesn't work well. Now in the lower left diagonal move it shows the front sprite, and in the lower right diagonal move the sprite disappears and shows nothing. And in the diagonals above it continues to function badly as before: https://i.ibb.co/CWL9jcX/movement.gif
 

Yal

🐧 *penguin noises*
GMC Elder
Try adding this line right after you change the sprite.
show_debug_message("spr_player_" + state + string(dir))
This tells you what GM is trying to set the sprite to. This could help you figuring out the problem when the character has the wrong sprite (e.g. if it tries to use spr_player_idle5 you might wanna cap dir to the range 0-4).
 

Klanes

Member
Try adding this line right after you change the sprite.


This tells you what GM is trying to set the sprite to. This could help you figuring out the problem when the character has the wrong sprite (e.g. if it tries to use spr_player_idle5 you might wanna cap dir to the range 0-4).
Oh thanks! That's very useful! Well, In addition to that as I said before, the diagonal characters are wrong since the last change, moving in the lower right diagonal tries to load a sprite, both in idle and in move that does not exist (the "4").
Gif: https://i.ibb.co/161Cp9S/movement-3.gif
 

Yal

🐧 *penguin noises*
GMC Elder
You could always embellish it with more safeguards...
GML:
dir = clamp((floor((_dir + 45)/90) mod 4),0,3);
Now it will always be between 0 and 3 because we clamp the final value, and 4 turns into 0, 5 to 1 and so on because we do it modulo-4.
 

Klanes

Member
You could always embellish it with more safeguards...
GML:
dir = clamp((floor((_dir + 45)/90) mod 4),0,3);
Now it will always be between 0 and 3 because we clamp the final value, and 4 turns into 0, 5 to 1 and so on because we do it modulo-4.
Thanks! Now a sprite is always shown, but it keeps happening that the movement of lower left diagonal and upper right diagonal show the front and rear sprite respectively :(
 

Klanes

Member
You've swapped dir with _dir. This is the problem with using similar variable names for different purposes.
GML:
dir = ceil((_dir - 45)/90);
The same is still happening, top left diagonal and bottom right diagonal move show wrong sprite 😓
 

Nidoking

Member
Well, then, you're just not going to get a simple mathematical formula that gives you what you want every time. I think you're going to have to use logic. Just manually set the sprite based on the value of dir (or _dir).
 

Nidoking

Member
Illustrator has nothing to do with it. What sprite do you want if dir is 0? How about if dir is 45? 90? etc. Don't try to do fancy stuff with strings and asset names. Just set the right sprite for the direction. I've seen you use if. You know how to do this.
 

Klanes

Member
Illustrator has nothing to do with it. What sprite do you want if dir is 0? How about if dir is 45? 90? etc. Don't try to do fancy stuff with strings and asset names. Just set the right sprite for the direction. I've seen you use if. You know how to do this.
Oh don't get me wrong. By "my illustrator's mind" I mean that I have a +10000 as a artist and a -10000 as a coder and my "logical" mind is like a 5 years child xD. I wish I didn't feel stupid asking questions that will be totally easy for you. I am trying things, soon I will tell you! and thanks for the patience and support!
 

Klanes

Member
Oh my god, I'm going to cry, I did it! hahaha. Surely it is the most absurd way to solve the problem, but it works. I have defined the move sprite according to the horizontal or vertical speed (hsp and vsp). And as for the idle, I have kept the initial version that I copied from the tutorial because it worked fine. *I tried to do it based on the direction, I search in the Game Maker documentation but I couldn't understand how it worked, so I opted for the other option.

GML:
//movement
hsp = (keyboard_check(ord("D")) || keyboard_check(vk_right)) - (keyboard_check(ord("A")) || keyboard_check(vk_left));
vsp = (keyboard_check(ord("S")) || keyboard_check(vk_down)) - (keyboard_check(ord("W")) || keyboard_check(vk_up));

hsp *= move_speed;
vsp *= move_speed;

//moving
var moving = hsp!=0 || vsp!=0;

//states
switch(state){
    case "idle":
        //change to move
        if (moving) state = "move";
       
    break;
    case "move":
        //change to idle
        if (!moving) state = "idle";
    break;
    }

//direction
if (moving) {
    var _dir = point_direction(0, 0, hsp, vsp);
    dir = ceil((_dir - 45)/90);
}

//sprites
// Controlar sprite según el movimiento

if (state = "move") {
    if (hsp > 0) {
        if (vsp < 0) {
            sprite_index = spr_player_move0;
        }
    }
    if (hsp < 0) {
        if (vsp < 0) {
            sprite_index = spr_player_move2;
        }
    }
    if (hsp > 0) {
        if (vsp > 0) {
            sprite_index = spr_player_move0;
        }
    }
    if (hsp < 0) {
        if (vsp > 0) {
            sprite_index = spr_player_move2;
        }
    }
    if (hsp = 0) {
        if (vsp < 0) {
            sprite_index = spr_player_move1;
        }
    }
    if (hsp = 0) {
        if (vsp > 0) {
            sprite_index = spr_player_move3;
        }
    }
    if (vsp = 0) {
        if (hsp > 0) {
            sprite_index = spr_player_move0;
        }
    }
    if (vsp = 0) {
        if (hsp < 0) {
            sprite_index = spr_player_move2;
        }
    }
}

if (state = "idle") {
    sprite_index = asset_get_index("spr_player_" + "idle" + string(dir));
}

//movement
x += hsp;
y += vsp;
 

Nidoking

Member
That's pretty close to what I was suggesting, indeed. You can clean up the logic by, for example, just having one check for hsp > 0 and then inside that, checking the three cases for vsp. It's a good idea to do that anyway. You're also checking hsp first in some cases and vsp first in others. It would be a good idea to be consistent, and to use else to cut down on the number of checks performed. But you have the basic idea.
 

ophelius

Member
fyi, you can use >= or <=, I was able to clean up your code a lot by using that

Code:
if (state = "move") {
    if (hsp > 0) {
        if (vsp < 0) sprite_index = spr_player_move0;
        if (vsp >= 0) sprite_index = spr_player_move0;     
    }
    if (hsp < 0) {
        if (vsp < 0) sprite_index = spr_player_move2;
        if (vsp >= 0) sprite_index = spr_player_move2;
    }
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }
}
Edit: Looking at it again, according to your logic, you can simply have this:

Code:
if (state = "move") {
    if (hsp > 0) sprite_index = spr_player_move0;
    if (hsp < 0) sprite_index = spr_player_move2;
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }
}
 

CMAllen

Member
Okay, since you seem to want only vertical-oriented sprites when moving on the diagonals, might I suggest a different approach?

First, sum the absolute value of your two axis key sets (vkey and hkey). If it's 0, then you know no movement is happening, and you can skip over the next part, which consists of checking the absolute value of your vkey against that absolute value of your hkey. If your vkey is greater than or equal to your hkey, the character is either moving vertically or diagonally. In which case, you check the sign of your vertical axis (vkey) to determine whether the character sprite should be using the front or back sprite. If your vkey is less than your hkey, then you are moving laterally, and you use the sign of your hkey to determine whether to use the left or right sprite. It's not as elegant, but it should get the job done.

Above in code form:
GML:
var moving = abs(hkey+vkey);

if moving
{
    if (abs(vkey) >= abs(hkey))
    {
        if(sign(vkey) == 1)
        {
            sprite_index = (whichever is front);
        }
        else
        {
            sprite_index = (whichever is back);
        }

    }
    else
    {
        if(sign(hkey) == 1)
        {
            sprite_index = (whichever is right);
        }
        else
        {
            sprite_index = (whichever is left);
        }
    }

}
It should be noted, like all versions of these kinds of codes that use keyboard inputs to determine facing direction, that you'll get odd behavior when diagonal movement stops. That's because it requires releasing both keys on the *same step* to maintain the diagonal value. Otherwise, when the code executes again on the next step, whichever key was released last will be the one that takes precedent. You can get around this by setting a flag-timer combo that limits how often the game can check to see if the active sprite can be changed (for ~5 steps or so, long enough that near-simultaneous key releases are treated as simultaneous), or you can change the way your code checks for movement direction.
 
Last edited:

Klanes

Member
fyi, you can use >= or <=, I was able to clean up your code a lot by using that

Code:
if (state = "move") {
    if (hsp > 0) {
        if (vsp < 0) sprite_index = spr_player_move0;
        if (vsp >= 0) sprite_index = spr_player_move0;    
    }
    if (hsp < 0) {
        if (vsp < 0) sprite_index = spr_player_move2;
        if (vsp >= 0) sprite_index = spr_player_move2;
    }
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }
}
Edit: Looking at it again, according to your logic, you can simply have this:

Code:
if (state = "move") {
    if (hsp > 0) sprite_index = spr_player_move0;
    if (hsp < 0) sprite_index = spr_player_move2;
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }
}
Thank you very much for your answer, now the code has been incredibly clean. Based on how you have adjusted it the code, I have created my code to control the idle of the character based on the sprite in motion that was on the screen. It works perfectly and has been much cleaner. Regards.

GML:
// Controlar sprite "move" según el movimiento

    if (hsp > 0) sprite_index = spr_player_move0;
    if (hsp < 0) sprite_index = spr_player_move2;
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }

// Controlar sprite "idle" según el movimiento


 if (hsp == 0 && vsp == 0) {
    if (sprite_index = spr_player_move0) {
        sprite_index = spr_player_idle0;
    }
    if (sprite_index = spr_player_move1) {
        sprite_index = spr_player_idle1;
    }
    if (sprite_index = spr_player_move2) {
        sprite_index = spr_player_idle2;
    }
    if (sprite_index = spr_player_move3) {
        sprite_index = spr_player_idle3;
    }
}
 

Klanes

Member
Okay, since you seem to want only vertical-oriented sprites when moving on the diagonals, might I suggest a different approach?

First, sum the absolute value of your two axis key sets (vkey and hkey). If it's 0, then you know no movement is happening, and you can skip over the next part, which consists of checking the absolute value of your vkey against that absolute value of your hkey. If your vkey is greater than or equal to your hkey, the character is either moving vertically or diagonally. In which case, you check the sign of your vertical axis (vkey) to determine whether the character sprite should be using the front or back sprite. If your vkey is less than your hkey, then you are moving laterally, and you use the sign of your hkey to determine whether to use the left or right sprite. It's not as elegant, but it should get the job done.

Above in code form:
GML:
var moving = abs(hkey+vkey);

if moving
{
    if (abs(vkey) >= abs(hkey))
    {
        if(sign(vkey) == 1)
        {
            sprite_index = (whichever is front);
        }
        else
        {
            sprite_index = (whichever is back);
        }

    }
    else
    {
        if(sign(hkey) == 1)
        {
            sprite_index = (whichever is right);
        }
        else
        {
            sprite_index = (whichever is left);
        }
    }

}
It should be noted, like all versions of these kinds of codes that use keyboard inputs to determine facing direction, that you'll get odd behavior when diagonal movement stops. That's because it requires releasing both keys on the *same step* to maintain the diagonal value. Otherwise, when the code executes again on the next step, whichever key was released last will be the one that takes precedent. You can get around this by setting a flag-timer combo that limits how often the game can check to see if the active sprite can be changed (for ~5 steps or so, long enough that near-simultaneous key releases are treated as simultaneous), or you can change the way your code checks for movement direction.
Thanks for your answer and for your code, I have to learn a lot! I can't quite understand how your example works, and I haven't gotten it to work for me, but since I've gotten a setup that works well, I'd rather not touch my current code much more. It is greatly appreciated to see how strangers take time to try to help me. Regards!
 

Klanes

Member
Thanks to all for your answers and support, the problem with sprites is overcome and I have learned a few things along the way;)

But I just saw with horror that now I have a new problem that I did not have. My character is "glued" to wall objects when it collides (and neither player nor wall object is marked as solid). The dode is taken from a demo in which everything works perfectly, what could be wrong? and I haven't touched anything. The "obj_wall" exists and has no code.

The problema gif:

*EDIT: Doing some tests I see that it is something that has to do with the collision mask of each sprite, which changes a little from one to another and I suppose that the wall object gets stuck when it is superimposed. I'm going to make some adjustments and hope to fix it.

My step obj_player code:

GML:
/// @description  Movement

if keyboard_check(vk_left) // This checks to see if you pushed the Left Arrow
{
    hsp = -movespeed;       // This hsp to a negative movespeed so it can move left
}

if keyboard_check(vk_right) // This checks to see if you pushed the Right Arrow
{
    hsp = movespeed;       // This hsp to a positive movespeed so it can move right
}

if keyboard_check(vk_up) // This checks to see if you pushed the Up Arrow
{
    vsp = -movespeed;       // This vsp to a negative movespeed so it can move up
}

if keyboard_check(vk_down) // This checks to see if you pushed the Down Arrow
{
    vsp = movespeed;       // This vsp to a positive movespeed so it can move down
}

if keyboard_check_released(vk_left) || keyboard_check_released(vk_right) // This checks to so if you released the Left or Right key and stops
{
    hsp = 0;
}

if keyboard_check_released(vk_up) || keyboard_check_released(vk_down) // This checks to so if you released the Up or Down key and stops
{
    vsp = 0;
}

// Sprite move control

    if (hsp > 0) sprite_index = spr_player_move0;
    if (hsp < 0) sprite_index = spr_player_move2;
    if (hsp = 0) {
        if (vsp < 0)  sprite_index = spr_player_move1;
        if (vsp > 0)  sprite_index = spr_player_move3;
    }

// Sprite idle control

if (hsp == 0 && vsp == 0) {
    if (sprite_index = spr_player_move0) {
        sprite_index = spr_player_idle0;
    }
    if (sprite_index = spr_player_move1) {
        sprite_index = spr_player_idle1;
    }
    if (sprite_index = spr_player_move2) {
        sprite_index = spr_player_idle2;
    }
    if (sprite_index = spr_player_move3) {
        sprite_index = spr_player_idle3;
    }
}

/// Collision with wall

if place_meeting(x+hsp,y, obj_wall)
{

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

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

x += hsp; // This makes you move on the X Axis
y += vsp; // This makes you move on the Y Axis

// Make sure you add the plus sign or it won't work
 
Last edited:

Yal

🐧 *penguin noises*
GMC Elder
By the way, you can set the "collision mask" property of the object (which affects the mask_index variable) if you don't want to have to make sure all sprites have the exact same collision mask. By default it is "same as sprite", which means it uses the current sprite's collision mask, but you can have it use a specific sprite so it always has the same hitbox even if you change sprites.
 

Klanes

Member
By the way, you can set the "collision mask" property of the object (which affects the mask_index variable) if you don't want to have to make sure all sprites have the exact same collision mask. By default it is "same as sprite", which means it uses the current sprite's collision mask, but you can have it use a specific sprite so it always has the same hitbox even if you change sprites.
Oh 💩💩💩💩, it's true! I knew that but I didn't remember it, and I spent a long time adjusting each of the collision masks hahahaha
 
Top