GML Getting my obj_player to stop properly after calling move_towards_point()

MrSlippery

Member
Hello,

If you are familiar with old-school Dragon Warrior/Quest games for the NES, this is the type of movement I am after.

I would like my obj_player to move exactly 16 pixels for every movement input that is recognized by the game. It seems that move_towards_point() will help me accomplish this (perhaps there is a more appropriate function?).

The manual indicates that the instance will continue moving past the 'point' and that it is the programmer's job to tell it when to stop. I am not exactly sure how to go about doing this.

The code I have is quite simple:

Code:
if(right_key)
    {
        move_towards_point(x + 16, y, 1);
    }
When I press the right_key to move right, the instance moves at a speed of 1, but keeps moving (as expected). I am not sure how to get my obj_player to stop after moving exactly 16 pixels.

Any help or advice would be appreciated.
 

MrSlippery

Member
Hello Nidoking,

Yes, I understand that. But after moving 16 pixels, I am not sure how to then set the speed to 0.

If I do something like this:

GML:
if(keyboard_check_pressed(ord("G")))
    {
        move_towards_point(x + 16, y, 1);
    }
else speed = 0;
The obj_player only moves 1 pixel before having its speed set to 0.

If I do:

GML:
if(keyboard_check_pressed(ord("G")))
    {
        move_towards_point(x + 16, y, 1);
        speed = 0;
    }
The obj_player doesn't move at all.

The problem, and I should have been more specific, isn't getting the obj_player to stop, but rather to stop after it has moved exactly 16 pixels from where it began.
 

Slyddar

Member
There are plenty of ways to do it, but I find everything is easier when you use a state machine. Something like this would allow you to set the direction for different key presses. You just add the end position and direction for left (as shown), up and down. (direction would be 180, 90 and 270 respectively)
Also you could use move_towards_point instead of setting speed and direction directly, as all it does is set direction and speed for you. Up to you really.
GML:
//Create event
enum states {
  IDLE,
  MOVE,
}
state = states.IDLE;

//step event
switch state {
    case states.IDLE:
        var _moved = false;
        if(keyboard_check_pressed(ord("G"))) {
            direction = 0;
            end_x = x + 16;
            end_y = y;
            _moved = true;
        }
        if(keyboard_check_pressed(ord("F"))) {
            direction = 180;
            end_x = x - 16;
            end_y = y;
            _moved = true;
        }
        if _moved {
          speed = 1;
          state = states.MOVE;
          start_x = x;
          start_y = y;
        }
    break;
    case states.MOVE:
      if point_distance(x, y, start_x, start_y) >= 16 {
         x = end_x;
         y = end_y;
         speed = 0;
         state = states.IDLE;
    }
    break;
}
 
Last edited:

MrSlippery

Member
Hey Slyddar,

Thanks a lot! I got it working perfectly, now.

I had tried to incorporate the use of point_distance(), but was not able to get it to work properly. I cannot say I understand the use of 'state machines,' like in your example, but I am going to do a little bit of research to try and learn more.

Also, I just purchased your Tile Based Platformer Course. I hope I can learn a lot from that, too!
 

woods

Member
it took me a bit to wrap my head around how the "state machine" works too.. but once it the gel stuck together.. WoW .. very powerful tool ;o)

dumbing it down for basic explanation:
we are in the IDLE state and we hit "G"
// we face right and target a spot 16 away from where we are
// then we move there due to _moved = true;
// with _moved being true it changes the state to MOVE

while we are in the MOVE state and we hit "G" nothing happens because the keypress check is in the IDLE state.
this allows us ALOT more freedoms to control how and when things happen..

ex: say we want to add in a short invulnerable state so we dont take continual damage while being attacked ..and cant shoot because we are stunned (stun lock is no fun on the receiving end)
we would add another state to the list and things like canMove or canShoot being controlled separately from the default state.


it may sound complicated and confusing at first, but just like everything else the more you get into it, the easier it becomes
 

MrSlippery

Member
Hi Woods,

Yes, that seems like a great way to go. I am sure many people handle their movement events in the Step event with no problem, but a state machine seems like a better way to go (and perhaps less of a strain on memory).

The issue I am running into now is that when my obj_player is moving through 'random_encounter_generator' tiles, the random encounter can trigger before the character has completed its 16 pixel trek. After the battle is over, the character returns to its original Room, but will be positioned where it would have been if it had completed the 16 pixel move (as if it had not triggered a random encounter).

What I am working on now is only getting the possibility of a random encounter triggering when the player has finished its 16 pixel movement and before starting another one. I think using the IDLE state can help me solve this, but then the problem I ran into was my character could get into random encounters while standing still on 'random_encounter_generator' tiles.

Anyway, I will keep working on it.

Thanks for your two-cents!
 

Slyddar

Member
I cannot say I understand the use of 'state machines,' like in your example, but I am going to do a little bit of research to try and learn more.

Also, I just purchased your Tile Based Platformer Course. I hope I can learn a lot from that, too!
Woods has explained it nicely above. State machines are incredibly powerful since you can cut down on other irrelevant pieces of code affecting what you are trying to do at certain times. You place the instance in a particular state, and you then know exactly what code it will run. When you combine it with custom functions, it allows you to pick and choose what the instance will do while in that state. You've alluded to this above when you realised you can check if the instance is in the IDLE state, and if so, only then run what you need on it. That kind of segregation is extremely helpful.

I appreciate you purchasing my course. Your experience using states will grow substantially as I show how to use them effectively, and by the end you will be able to use them for so many things. It should hopefully unlock the potential to allow any new ideas to be solved simply by using state machines. Best of luck!
 

TheouAegis

Member
You could always just do it the way a lot of us did back in the old, old days of GM.
GML:
///Step Event
if !alarm[0] {
    hspeed = keyboard_check(vk_right) - keyboard_check(vk_left);
    if hspeed == 0 {
        vspeed = keyboard_check(vk_down) - keyboard_check(vk_up);
        if vspeed != 0 {
            alarm[0] = 16;
            if vspeed > 0
                sprite_index = spr_walk_down;
            else
                sprite_index = spr_walk_up;
        }
        else
        if sprite_index == spr_walk_right
            sprite_index = spr_stand_right;
        else
        if sprite_index == spr_walk_down
            sprite_index = spr_stand_down;
        else
        if sprite_index == spr_walk_up
            sprite_index = spr_stand_up;
    }
    else {
        alarm[0] = 16;
        sprite_index = spr_walk_right;
        image_xscale = sign(hspeed);
    }
}
Code:
///Alarm 0 Event
//blank comment
 

woods

Member
using @Slyddar 's move state as an example..
Code:
case states.MOVE:
      if point_distance(x, y, start_x, start_y) >= 16 {
         x = end_x;
         y = end_y;
         speed = 0;
         state = states.IDLE;
    }
    break;

i would maybe add that check in here.. looks like a good spot ;o)
tho i donno how you are checking for random encounters.. maybe a variable toggle for can_random = true/false

Code:
case states.MOVE:
      if point_distance(x, y, start_x, start_y) >= 16 {
         x = end_x;
         y = end_y;
         speed = 0;
         //check once for random encounter when the move completes but before the idle state
         state = states.IDLE;
    }
    break;
 
Top