[4.5.9] Grid-based top-down movement system (like Pokemon or Chip's Challenge)

puppydrum64

Active member
I think this has been asked before but I couldn't find working code from what I could tell. It seems much more difficult than the Zelda-style movement that comes with the NESMaker software. Has anyone attempted or created this?

EDIT: This is what I attempted to use but it didn't work. I imagine the AND statement doesn't do what I think it does.

Code:
    TXA
    STA temp ;; assumes the object we want to move is in x. 
   GetActionStep temp
    CMP #$07
    BNE +notHurt
        RTS
    +notHurt
    
    LDX player1_object
    LDA Object_x_hi,x
    AND #%00001000
    BNE +divisibleby16
        RTS
    +divisibleby16
    StopMoving temp, #$FF, #$00
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    RTS
 

Logana

Well-known member
Yeah, I really wanna use this style of movement too in a game that’s basically all I ever wanted in an rpg packaged together, but I’m not a code so I can’t really help although I will post this in a status update so more people can see :p
 

PasseGaming

Active member
I'm trying to remember what's different between Pokemon and Legend of Zelda's movement. As far as I recall it's more or less the same isn't it?
 

Logana

Well-known member
Nah, it’s tile based, basically you can move a tile at a time no more and no less
 

puppydrum64

Active member
I'm trying to remember what's different between Pokemon and Legend of Zelda's movement. As far as I recall it's more or less the same isn't it?
Zelda's movement lets you go between two or more tiles, you aren't confined to moving along discrete 16 x 16 tiles. In Pokemon once you press the direction button you automatically move 16 pixels. In Zelda you come to a stop immediately when you let go of the directional pad.
 

AllDarnDavey

Active member
There was a couple threads during byteoff2020 for grid movement. Maybe you'll find something useful.

If I remember correctly the basics were don't use the normal physic engine for movement, instead in the input scripts for movement, test to see if the tile you're trying to move to (one right of the player if moving right, one up from the player if moving up, etc) isn't a solid tile, then just add or subtract the players position exactly one tiles worth.
 

pigeonaut

Member
I have a workaround to get this to work that isn't very clean but it's been working for me. You will need an input script and an additional tile script.

Here is what I want to accomplish for Pokémon like movement:

1. I press UP on my D-pad and my input script will start moving my player upwards.
2. When the player is on the center of a tile, the player should be stopped or keep going if I am still holding my up arrow.

Number 1 is easy, I just use this script and assign it to your "hold" "up" input:

Code:
    TXA
    STA temp
       
    StartMoving temp, #UP
    TXA
    STA temp ;; assumes the object we want to move is in x.
    ChangeFacingDirection temp, #FACE_UP
RTS

It will move my player up and even if I let go of my input, the player still moves up.

But (for number 2) we need it to stop whenever the player hits the center of any tile in front of them. The issue is that we cannot use the input script as the script only fires off when there is user input. So if you put the logic, to check if your player is on the center of tile, in the moving script, the script might terminate before that check can even happen because your player might let go of the UP arrow early!

So to combat this we need another script that is constantly checking for if a player in on the center of a tile. The easiest I found was to put that logic into the tile's themselves. The tiles will fire off their scripts whenever they experience a collision with an object. All we have to do is check for only the player object and see if their position is divisible by 16 (which will fire off if player is at center of any tile). Here is the script:


Code:
;;
    CPX player1_object ;; see if player is colliding with me
    BNE +skip
        LDX player1_object

    LDA Object_y_hi,x
    AND #15
    BNE +notDivBy16       ;this filters out any y position that isn't divisible by 16
       
       
        ;PlaySound #sfx_powerup
        TXA
        STA temp
        StopMoving temp, #$FF, #$00
       
        RTS
    +notDivBy16
       
    RTS
   
    +skip

If you let go of the input and encounter a collision with a tile, the script will stop you at the nearest tile that is in front of you.

Now I don't know for sure why, but if you HOLD up arrow, you will keep moving up, but as soon as you let go of the input, your player will stop at the center of the nearest tile in front of them! Not sure how THAT part is working but I won't argue with that extra functionality!

You will need to change all of your screen tiles to this new tiletype with this script, but I hope this will help you figure out a better solution. I will leave the other directions for you. Let me know if this works or you find something better!
 
Last edited:

puppydrum64

Active member
I have a workaround to get this to work that isn't very clean but it's been working for me. You will need an input script and an additional tile script.

Here is what I want to accomplish for Pokémon like movement:

1. I press UP on my D-pad and my input script will start moving my player upwards.
2. When the player is on the center of a tile, the player should be stopped or keep going if I am still holding my up arrow.

Number 1 is easy, I just use this script and assign it to your "hold" "up" input:

Code:
    TXA
    STA temp
      
    StartMoving temp, #UP
    TXA
    STA temp ;; assumes the object we want to move is in x.
    ChangeFacingDirection temp, #FACE_UP
RTS

It will move my player up and even if I let go of my input, the player still moves up.

But (for number 2) we need it to stop whenever the player hits the center of any tile in front of them. The issue is that we cannot use the input script as the script only fires off when there is user input. So if you put the logic, to check if your player is on the center of tile, in the moving script, the script might terminate before that check can even happen because your player might let go of the UP arrow early!

So to combat this we need another script that is constantly checking for if a player in on the center of a tile. The easiest I found was to put that logic into the tile's themselves. The tiles will fire off their scripts whenever they experience a collision with an object. All we have to do is check for only the player object and see if their position is divisible by 16 (which will fire off if player is at center of any tile). Here is the script:


Code:
;;
    CPX player1_object ;; see if player is colliding with me
    BNE +skip
        LDX player1_object

    LDA Object_y_hi,x
    AND #15
    BNE +notDivBy16       ;this filters out any y position that isn't divisible by 16
      
      
        ;PlaySound #sfx_powerup
        TXA
        STA temp
        StopMoving temp, #$FF, #$00
      
        RTS
    +notDivBy16
      
    RTS
  
    +skip

If you let go of the input and encounter a collision with a tile, the script will stop you at the nearest tile that is in front of you.

Now I don't know for sure why, but if you HOLD up arrow, you will keep moving up, but as soon as you let go of the input, your player will stop at the center of the nearest tile in front of them! Not sure how THAT part is working but I won't argue with that extra functionality!

You will need to change all of your screen tiles to this new tiletype with this script, but I hope this will help you figure out a better solution. I will leave the other directions for you. Let me know if this works or you find something better!
I think this is very clever. But I don't understand why this works. Specifically the first line of your tile code: CPX player1_object. What is player1_object being compared to?
 

pigeonaut

Member
I think this is very clever. But I don't understand why this works. Specifically the first line of your tile code: CPX player1_object. What is player1_object being compared to?
Thanks! I think it's being compared to the current tile collision, not sure but it does look weird. Did it work for you?
 

puppydrum64

Active member
Thanks! I think it's being compared to the current tile collision, not sure but it does look weird. Did it work for you?
I set Tile Type 0 (Null/Walkable) to use this script and sadly it did not work. The movement seems to be completely as it was if the game had the normal blank tile code there.
 

CutterCross

Active member
I set Tile Type 0 (Null/Walkable) to use this script and sadly it did not work. The movement seems to be completely as it was if the game had the normal blank tile code there.
Tile types 0 and 1 [Null_Walkable and Solid] are essentially hardcoded and aren't easily replaceable by switching out the scripts for each tile type slot. If you're going to test a new tile type, use a different slot.
 

pigeonaut

Member
I set Tile Type 0 (Null/Walkable) to use this script and sadly it did not work. The movement seems to be completely as it was if the game had the normal blank tile code there.
Ah I'm sorry. You removed the "stopMoving" script off your 'release' d-pad inputs? Also my player speed was set to 16 and whenever I tried to increase it the tiles would not stop my player.
 

puppydrum64

Active member
There was a couple threads during byteoff2020 for grid movement. Maybe you'll find something useful.

If I remember correctly the basics were don't use the normal physic engine for movement, instead in the input scripts for movement, test to see if the tile you're trying to move to (one right of the player if moving right, one up from the player if moving up, etc) isn't a solid tile, then just add or subtract the players position exactly one tiles worth.
Your version almost works but for some reason my character moves more than one tile upward. I'm not sure why that is. I think it has to do with the subtraction of #$10 but I'm not sure
 

puppydrum64

Active member
Ah I'm sorry. You removed the "stopMoving" script off your 'release' d-pad inputs? Also my player speed was set to 16 and whenever I tried to increase it the tiles would not stop my player.
It works for up and down now. I tried making a branch for left and right. Here is my code. I had to swap Object_x_hi and Object_y_hi in the code to get it to work (not sure why, unless I did this it acted like normal movement)
Code:
CPX player1_object ;; see if player is colliding with me
    BNE +skip
        LDX player1_object
   
    LDA Object_direction,x
    AND #%00000010 ;this bit equals 1 only when facing left or right
    BNE +isFacingUD
        JMP +isFacingLR
    +isFacingUD
    JMP RESET
    LDA Object_x_hi,x
    AND #15
    BNE +notDivBy16       ;this filters out any y position that isn't divisible by 16
      
      
        ;PlaySound #sfx_powerup
        TXA
        STA temp
        StopMoving temp, #$FF, #$00
      
        RTS
    +notDivBy16
      
    RTS
   
    +isFacingLR
    LDA Object_y_hi,x
    AND #15
    BNE +notDivBy16       ;this filters out any y position that isn't divisible by 16
      
      
        ;PlaySound #sfx_powerup
        TXA
        STA temp
        StopMoving temp, #$FF, #$00
      
        RTS
   
    +notDivBy16
    RTS
    +skip
   
    RTS
 
Last edited:

pigeonaut

Member
Now it works, at least for moving up. Now I just need to figure out how to make it work for other directions too. Is there a way to load into the accumulator the direction your player is facing? I want to do something like this:
Code:
CMP #FACE_UP
BNE +notUp
; put the code for moving up here

+notUp
CMP #FACE_LEFT
BNE +notLeft
etc.
Glad its working! I think this might be the variable you are looking for: LDA Object_direction,x .

Let me know if your version works. I tried making a separate move down script (essentially a copy of the move_up one I posted) which works except for there is now a 2 pixel offset when moving down... not sure why. Moving up still works as intended.
 

puppydrum64

Active member
Glad its working! I think this might be the variable you are looking for: LDA Object_direction,x .

Let me know if your version works. I tried making a separate move down script (essentially a copy of the move_up one I posted) which works except for there is now a 2 pixel offset when moving down... not sure why. Moving up still works as intended.
It did work. But not very well, there's still a few bugs. Collision isn't perfect and moving between solid objects is a no-go. I need to know the NES gamepad binary codes to try something out but all I know is that down equals #%00100000. There's hundreds of scripts to look through and I can't find the gamepad buttons defined anywhere among them.
 

CutterCross

Active member
It did work. But not very well, there's still a few bugs. Collision isn't perfect and moving between solid objects is a no-go. I need to know the NES gamepad binary codes to try something out but all I know is that down equals #%00100000. There's hundreds of scripts to look through and I can't find the gamepad buttons defined anywhere among them.
Documentation for NESmaker's gamepad variable:

NESmaker stores the data bits read from $4016 and $4017 backwards from what NESdev wiki does. It's really just a matter of NESmaker using ROR instead of ROL to cycle in each data bit, I guess NESmaker just has to be weird like that for no reason.
 

puppydrum64

Active member
I got the movement to work for the most part, problem is that my character is not centered perfectly on the tiles so there is some error that occurs causing them to become de-synced with the grid over time.
 

pigeonaut

Member
I got the movement to work for the most part, problem is that my character is not centered perfectly on the tiles so there is some error that occurs causing them to become de-synced with the grid over time.
That's the issue I'm having as well. There must be something different with the 'down' direction than with the 'up'. Let me know if you figure it out, I'll work on it some more from my end.
 

puppydrum64

Active member
That's the issue I'm having as well. There must be something different with the 'down' direction than with the 'up'. Let me know if you figure it out, I'll work on it some more from my end.
The character seems to start off that way once the game begins, even though in the overworld editor he is perfectly centered. I think this is related to the way NESMaker starts the game since I've noticed that if you put the player one tile above a solid floor in the Metroidvania module she partially falls through the floor
 
Top Bottom