Legacy GM Help Simplifying my 8-directional grid based movement code

A

AnimusRex

Guest
So, I have 8 directional movement on a grid. Here's what I have that governs the actual movement;

Code:
//Setting up animation frames
facing = image_index;

//Code for turning on the spot
if (ismoving == false){
        if (keyboard_check_pressed(vk_right))
        {
            image_index += 1;
        }
        if (keyboard_check_pressed(vk_left))
        {
            image_index -= 1;
        }
}
       
if (keyboard_check(vk_up) and ismoving = false)
    {
    switch (facing)
        {
        case 0: //Moving North
            {
                ismoving = true;
                movetimer = 32;
                speedy = -movespeed;
                speedx = 0;
                break;
            }
        case 1: //Moving North East
            {
                ismoving = true;
                movetimer = 32;
                speedy = -movespeed;
                speedx = movespeed; 
                break;
            }
        case 2: //Moving East
            {
                ismoving = true;
                movetimer = 32;
                speedy = 0;
                speedx = movespeed;
                break;
            }
        case 3: //Moving South-East
            {
                ismoving = true;
                movetimer = 32;
                speedy = movespeed;
                speedx = movespeed;
                break;
            }
        case 4: //Moving South
            {
                ismoving = true;
                movetimer = 32;
                speedy = movespeed;
                speedx = 0;
                break;
            }
        case 5: //Moving South-West
            {
                ismoving = true;
                movetimer = 32;
                speedy = movespeed;
                speedx = -movespeed;
                break;
            }
        case 6: //Moving West
            {
                ismoving = true;
                movetimer = 32;
                speedy = 0;
                speedx = -movespeed;
                break;
            }
        case 7: //Moving North-West
            {
                ismoving = true;
                movetimer = 32;
                speedy = -movespeed;
                speedx = -movespeed;
                break;
        }
So whichever direction you're facing, up moves you in that direction.

For the collision code I have

Code:
//Horizontal Collisions
if (grid_place_meeting(x+speedx, y))
{
while (!grid_place_meeting(x+sign(speedx), y))
{
    x+= sign(speedx);
}
    ismoving = false
    speedx = 0;
}
Which is all well and good. Movement inside the grid works, using this plus my vertical collision code. The problem is as follows.

I want to make it so that if I (@) am against walls like this:
@
XXXXX
and I'm facing South East, for example, and I press forward, the character moves to the East instead.

Similarly for West, basically I want the character to be able to slide along walls rather than just return that there is a tile in the direction you're trying to go and that's it.

As well, with the code as it exists now, if I am like this:
@
XX
And I try to move to the South East, even though it's an open square, it returns a collision because of the square to the South.

I've been toying with this for a couple days but all my solutions are convoluted if statements in my switch for directions, and it turns into a mess way too fast. I'm sure there's a much cleaner way to do it, if anyone has any thoughts I'm all ears.

My collision code is as follows, just in case it matters;

Code:
///Checking collisions
var xx = argument[0];
var yy = argument[1];

//Remember player position
var xp = x;
var yp = y;

//Update position for the mask calculations
x = xx;
y = yy;

//Check for an x meeting on the grid in either left or right according to the sprites bounding box
var x_meeting = (Level.grid[# bbox_right div CELL_WIDTH, bbox_top div CELL_HEIGHT] != FLOOR) ||
                (Level.grid[# bbox_left div CELL_WIDTH, bbox_top div CELL_HEIGHT] != FLOOR);

//Check for a y meeting on the grid in either top or bottom according to the sprites bounding box             
var y_meeting = (Level.grid[# bbox_right div CELL_WIDTH, bbox_bottom div CELL_HEIGHT] != FLOOR) ||
                (Level.grid[# bbox_left div CELL_WIDTH, bbox_bottom div CELL_HEIGHT] != FLOOR);
               
//Move back if collided
x = xp;
y = yp;

//Return true or false
return x_meeting or y_meeting;
 
H

HammerOn

Guest
You have the direction indexed so you can "use a circle" to make things easier. That long switch code can be simplified to this:
Code:
var dir = facing * ((2*pi) / 8);

ismoving = true;
movetimer = 32;
speedx = cos(dir) * movespeed;
speedy = -sin(dir) * movespeed;
About the collision, are you trying to move the object away from the wall? The code seems like the opposite.
Shouldn't it be something like this?
Code:
while (grid_place_meeting(x, y))
{
    x -= sign(speedx);
}
 
Last edited by a moderator:
A

AnimusRex

Guest
You have the direction indexed so you can "use a circle" to make things easier. That long switch code can be simplified to this:
Code:
var dir = facing * ((2*pi) / 8);

ismoving = true;
movetimer = 32;
speedx = cos(dir) * movespeed;
speedy = -sin(dir) * movespeed;
This totally borked my movement code, and I don't really understand what it means. As it is, inputting this instead of my switch code causes movement in directions not related to the correct facing, which could just be a simple sub-image swap for the sprite, but it also throws off my diagonal movement and makes it so the character doesn't line up with the grid anymore. It stops a few chars short of the actual grid tile.

About the collision, are you trying to move the object away from the wall? The code seems like the opposite.
Shouldn't it be something like this?
Code:
while (grid_place_meeting(x, y))
{
    x -= sign(speedx);
}
No, the collision code checks if there is a wall tile there and cancels movement before it begins if there would be a collision in the next tile. Without doing that it complicates the grid-based movement I have, and was allowing strange collision issues.

Thanks!
 

HayManMarc

Member
Check the horizontal and vertical movements separately (independent from each other) when trying to move diagonally. This way if the vertical movement is blocked, it won't stop the horizontal movement, and vice versa.
 

jo-thijs

Member
@HammerOn, There are 3 things wrong with what you suggested:
1) The direction indicated by the variable facing moves clockwise, whereas radians for cos and sin are counter clockwise.
Because GameMaker has an inverted y-axis, the code you've given rotates the wrong way.
2) Movement is not on a circle, horizontal movement is completely unrelated to vertical movemet.
The object moves faster diagnally than horizontally or vertically.
3) You didn't answer the question of the OP, you answered the title, but AnimusRex actually wanted something completely different than what he put in the title.

Also, I think you would like to read up on lengthdir_x and lengthdir_y, they are a bit nicer to use there.

@HayManMarc, exactly.
To apply this, you just simply change this in the horizontal collision code:
Code:
    ismoving = false
    speedx = 0;
to this:
Code:
    speedx = 0;
    if speedy == 0
        ismoving = false;
@AnimusRex, I'll also answer the question in your title:
Actual movement code:
Code:
if !ismoving {
    image_index += keyboard_check_pressed(vk_right) - keyboard_check_pressed(vk_left);
    if keyboard_check(vk_up) {
        ismoving = true;
        movetimer = 32;
        speedx = ((image_index > 0 && image_index < 4) - (image_index > 4)) * movespeed;
        speedy = ((image_index > 2 && image_index < 6) - (image_index < 2 || image_index == 7)) * movespeed;
        if grid_place_meeting(x + speedx, y)
            speedx = 0;
        if grid_place_meeting(x, y + speedy)
            speedy = 0;
        if speedx == 0 && speedy == 0 {
            ismoving = false;
            movetimer = 0;
        }
    }
}
Collision code might not even be necessary anymore with this.
 
Last edited:
A

AnimusRex

Guest
Check the horizontal and vertical movements separately (independent from each other) when trying to move diagonally. This way if the vertical movement is blocked, it won't stop the horizontal movement, and vice versa.
How? The collision code I'm using just checks what the grid place value is, there's not a horizontal or vertical check, per se.

Edit:

Jo; can you break down your codes logic a bit? It's definitely a lot more succinct, but I don't fully understand it. Also; vertical movement going directly upwards isn't working with that code. NE and NW slide along the X axis as if there's a wall, but I can't see why it would be doing that.
 
Last edited by a moderator:

jo-thijs

Member
No, I didn't see the edit before.

I saw I made a typo in my previous code, leading to you not being able to move upwards.

The important part was this though:
To apply this, you just simply change this in the horizontal collision code:
Code:
ismoving = false
speedx = 0;
to this:
Code:
speedx = 0;
if speedy == 0
ismoving = false;
 
A

AnimusRex

Guest
I'd removed that part entirely and it's still working fine, minus moving directly north. I can't see what typo you could have made because I don't fully understand your movement code, I guess.
Code:
 if keyboard_check(vk_up) {
        ismoving = true;
        movetimer = 32;
        speedx = ((image_index > 0 && image_index < 4) - (image_index > 4)) * movespeed;
Specifically the last line for speedx, it equals ((0-4 image index - image index greater than 4) times movespeed? Isn't that just all the image index sprites?

I'm reading this as a range. I think I realize now it's subtraction. I still don't understand how this works, though.
 

jo-thijs

Member
The type I made was I wrote && where there's a || now.
(image_index > 4) will return 1 if image_index is bigger than 4 (so, if it is 5, 6 or 7) and it will return 0 otherwise.
(image_index > 0 && image_index < 4) will return 1 if image_index is bigger than 0 and smaller than 4 (so if it is 1, 2 or 3) and it will return 0 otherwise.
The minus sign is indeed subtraction.
You could write it like this as well:
Code:
if image_index > 0 and image_index < 4
    speedx = movespeed;
else if image_index > 4
    speedx = -movespeed;
else
    speedx = 0;
 
A

AnimusRex

Guest
So with the code looking like this;
Code:
//Code for turning on the spot
if !ismoving {
    image_index += keyboard_check_pressed(vk_right) - keyboard_check_pressed(vk_left);
    //Code for moving in the direction faced
    if keyboard_check(vk_up) {
        ismoving = true;
        movetimer = 32;
        speedx = ((image_index > 0 && image_index < 4) - (image_index > 4)) * movespeed;
        speedy = ((image_index > 2 && image_index < 4) - (image_index < 2 || image_index == 7)) * movespeed;
        //checking for collisions
        if grid_place_meeting(x + speedx, y)
            speedx = 0;
        if grid_place_meeting(x, y + speedy)
            speedy = 0;
        if speedx == 0 && speedy == 0 {
            ismoving = false;
            movetimer = 0;
        }
    }
}

//Horizontal Collisions
if (grid_place_meeting(x+speedx, y))
{
while (!grid_place_meeting(x+sign(speedx), y))
{
    x+= sign(speedx);
}
    speedx = 0;
    if speedy == 0 ismoving = false;
}
Movement in all directions is working; however, going towards a corner on a diagonal offsets the character by a couple pixels off the grid.

All the things I tried, such as variations of;
Code:
//Diagonal collisions
if (grid_place_meeting(x+speedx, y+speedy))
{
speedx = 0
speedy = 0
ismoving = false
}
Basically just dicking about with using both x and y at the same time for collision scripts produced some interesting errors but nothing more.
 
A

AnimusRex

Guest
So for some reason now

Code:
//Diagonal Collisions
if (grid_place_meeting(x+speedx, y+speedy))
{
    speedy = 0
    speedx = 0   
    ismoving = false
}
is fixing it. No more walking through corners. Super weird, but I've tested it again and it's working. I assumed I did something different until I came back and looked at the old post.. I guess I just didn't apply the changes before testing before or something.

Thanks a ton, jo, this really cleans up my movement script.
 
Top