Legacy GM [SOLVED] Top-down sliding along walls

Lobos

Member
Hi! I'm trying to make a top-down shooter, but got stuck at the beginning (literally).

(the question is in the very end)

First, I wrote a super basic code for moving (in the step event):

Code:
///Player movement
var player_horizontalspeed = 0;
var player_verticalspeed = 0;


//Player input
if (keyboard_check(ord('W')))
{
    player_verticalspeed -= player_movement_forward;
}

if (keyboard_check(ord('A')))
{
    player_horizontalspeed -= player_movement_left;
}

if (keyboard_check(ord('S')))
{
    player_verticalspeed += player_movement_backward;
}

if (keyboard_check(ord('D')))
{
    player_horizontalspeed += player_movement_right;
}
and a collision check (still in the step event):

Code:
//Collision checking
if (place_meeting(x + player_horizontalspeed,y, obj_wall))
    while (!place_meeting(x + sign(player_horizontalspeed), y, obj_wall))
        x += sign(player_horizontalspeed);
else
    x += player_horizontalspeed;
  
    if (place_meeting(x, y + player_verticalspeed, obj_wall))
    while (!place_meeting(x, y + sign(player_verticalspeed), obj_wall))
        y += sign(player_verticalspeed);
else
    y += player_verticalspeed;
  

//Look at the direction of the mouse
player_point_direction = point_direction(x,y,mouse_x,mouse_y);
image_angle += sin(degtorad(player_point_direction - image_angle)) * player_movement_rotationspeed;

Thats working reasonably well, except if there is a wall: I could glued to it, so when I read this article:
https://www.yoyogames.com/blog/432/buttery-smooth-tech-tips-movement
I copied it (the script):

Code:
var spd = argument0;
var dir = argument1;
var xtarg = x+lengthdir_x(spd,dir);
var ytarg = y+lengthdir_y(spd,dir);
if place_free(xtarg,ytarg) {
    x = xtarg;
    y = ytarg;
}
else {
    var sweep_interval = 10;
  
    for ( var angle = sweep_interval; angle <= 80; angle += sweep_interval) {
        for ( var multiplier = -1; multiplier <= 1; multiplier += 2) {    
            var angle_to_check = dir+angle*multiplier;
            xtarg = x+lengthdir_x(spd, angle_to_check);
            ytarg = y+lengthdir_y(spd, angle_to_check);    
            if place_free(xtarg,ytarg) {
                x = xtarg;
                y = ytarg; 
                exit;      
            }  
        }
    }
}
with the following step event:

Code:
var move_xinput = 0;
var move_yinput = 0;
for ( var i = 0; i < array_length_1d(movement_inputs); i++){
    var this_key = movement_inputs[i];
    if keyboard_check(this_key) {
        var this_angle = i*90;
        move_xinput += lengthdir_x(1, this_angle);
        move_yinput += lengthdir_y(1, this_angle);
    }
}
var moving = ( point_distance(0,0,move_xinput, move_yinput) > 0 );
if moving  {
    var move_dir = point_direction(0,0,move_xinput, move_yinput);
    move(move_speed_this_frame, move_dir);
}


My problem is: it is using place_free, which require solid walls, but as the 14th commandments of Game Maker said, you don't use solid. But without it (and with this code), the player go throu everything, including walls.


Could you help me to modify the script (or suggest an easy-to-use better one), so the player/enemies stop next to the wall without solid walls?

At first I simply tied to replace place_free with place_meeting [like: if !place_meeting(xtarg,ytarg,obj_wall) ], but thats not working, nor my other attemps (or I screwed up something).
 

Calvert

Member
Could you help me to modify the script (or suggest an easy-to-use better one), so the player/enemies stop next to the wall without solid walls?
Step Event:

Code:
if (keyboard_check(ord("W")) && !place_meeting(x,y-3,obj_wall)){
    y=y-4}
if (keyboard_check(ord("A")) && !place_meeting(x-3,y,obj_wall)){
    x=x-4}
if (keyboard_check(ord("S")) && !place_meeting(x,y+3,obj_wall)){
    y=y+4}
if (keyboard_check(ord("D")) && !place_meeting(x+3,y,obj_wall)){
    x=x+4}
 

Lobos

Member
Thanks for the fast reply, but my problem with you're code is with it I'm back to square one. It works flawlessly (just as my original code), untill I introduce turning.
The player can go left/right/etc, but with a simple turning code (the player look for the direction of the mouse)

Code:
    //Look at the direction of the mouse
player_point_direction = point_direction(x,y,mouse_x,mouse_y);
image_angle += sin(degtorad(player_point_direction - image_angle)) * player_movement_rotationspeed;
can still get stuck (even with straight walls). I assume, the problem is, that with a square object, a player can't stuck, because she/he cannot go into the wall, but with turning, she/he can go near and turn, so the corner of the player object go inside.

Thats not a big deal, I just made a round mask for the player...but with this, still can stuck (not permanently, but sometimes tha player is stopped even when buttons are pressed and walls are flat, so it should go along).
 

Calvert

Member
Thanks for the fast reply, but my problem with you're code is with it I'm back to square one. It works flawlessly (just as my original code), untill I introduce turning.
The player can go left/right/etc, but with a simple turning code (the player look for the direction of the mouse)

Code:
    //Look at the direction of the mouse
player_point_direction = point_direction(x,y,mouse_x,mouse_y);
image_angle += sin(degtorad(player_point_direction - image_angle)) * player_movement_rotationspeed;
can still get stuck (even with straight walls). I assume, the problem is, that with a square object, a player can't stuck, because she/he cannot go into the wall, but with turning, she/he can go near and turn, so the corner of the player object go inside.

Thats not a big deal, I just made a round mask for the player...but with this, still can stuck (not permanently, but sometimes tha player is stopped even when buttons are pressed and walls are flat, so it should go along).
Use draw_sprite_ext instead of image_angle.

Step Event:
Code:
if (keyboard_check(ord("W")) && !place_meeting(x,y-3,obj_solid)){
    y=y-4}
if (keyboard_check(ord("A")) && !place_meeting(x-3,y,obj_solid)){
    x=x-4}
if (keyboard_check(ord("S")) && !place_meeting(x,y+3,obj_solid)){
    y=y+4}
if (keyboard_check(ord("D")) && !place_meeting(x+3,y,obj_solid)){
    x=x+4}

angle = point_direction(x,y,mouse_x,mouse_y);
Draw Event:
Code:
draw_sprite_ext(spr_player, 0, x, y, 1, 1, angle, c_white, 1);
 
Last edited:

Bentley

Member
In the script you copied, are you sure "exit" was used? Because (I'm pretty sure), exit will not only exit the script, it will also exit the event that is was called from. I would change it from exit to return. Return will stop the script without effecting the event.

Edit: I looked at the link really quick and exit is used, but it might be that you are calling the script from an event that has further code in it that is important. For example:

Step Event
move(); // Call the script
// Important code here, like setting your sprite or your image angle or w/e.

If the move script was able to find a place_free, then the exit ran, and that means the "Important code here" won't run.

You could have the script return Boolean. One idea would be something like this:
Code:
if (move()) // If the move script returned true
{
    // Run code that depends on being moved
}
Change exit to return true. You'd also have to return true where the first place_free check is done.
At the end of the script, code return false as a place to move to was not found.
 
Last edited:

Lobos

Member
I went on Calvert's suggestion - at first I got some error messages, because the weapons used the player's image_angle, but since I changed it, it works.

Bentley: thanks for the effort, but that script is way beyond my level and I'm not comfortable to change it (I only used scripts for particle effect before). But anyway, thanks you both for the time and effort, and maybe this post will help others it the future.
 
Top