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

pigeonaut

Member
Alright I got it to work for real this time haha:

I just needed grid movement for the up and down direction but was able to figure out left and right and wanted to share this solution with you. Yes it's messy but it can be shortened and cleaned up.

How does it work? The tile will check for player collision. If the player collides, the tile checks to see if the player is at the center of the tile. If at center of tile, then stop the player. Since the collision system is complex, the direction you collide with the tile will give different collision values. We need to account for this offset by using the player direction as a guide.

Here is the tile script, you will need to paint your whole floor with this tile type:
Code:
;; TODO: clean the code!
;; first check if player collided with this tile:
    CPX player1_object
    BEQ +checkDirection
        ;else not a player
        JMP +skip
   
    +checkDirection
    ;; next check which way we are facing:
       
        LDA Object_direction
        AND #%00000111
        CMP #%00000000 ;Down
        BNE +notDown
            ; is down direction, do this:
           
            ;;convert tile coord to collision coords:
            LDA tileX
            AND #%11110000
            STA tempx
            ;; fix the offset:
            LDA tempx
            SEC
            SBC #$02
            STA tempx
            ;; now do same for Y
            LDA tileY
            AND #%11110000
            STA tempy

            LDA tempy
            SEC
            SBC #$03
            STA tempy
            JMP +continue
        +notDown
        AND #%00000110 ; up
        CMP #%00000100
        BNE +notUp
           
            LDA tileX
            AND #%11110000
            STA tempx

            LDA tempx
            SEC
            SBC #$02
            STA tempx

            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notUp
        AND #%00000110
        CMP #%00000010 ;;right
        BNE +notRight
            LDA tileX
            AND #%11110000
            STA tempx

            LDA tempx
            SEC
            SBC #$02
            STA tempx
            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notRight
        AND #%00000110
        CMP #%00000110
        BNE +notLeft ;; left
            ;PlaySound #sfx_powerup
            LDA tileX
            AND #%11110000
            STA tempx
            LDA tempx
            SEC
            ADC #$00
            STA tempx
            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notLeft
            JMP +skip
           
; stop the player at center of tile
        +continue
       
        LDA Object_y_hi,x
        CMP tempy
        BNE +checkX
            JMP stop_player_1
        +checkX
        LDA Object_x_hi,x
        CMP tempx
        BNE +skip
       
        stop_player_1
       
           
            TXA
            STA temp
            StopMoving temp, #$FF, #$00
           
        +skip
       
        RTS

Next your player move scripts NEED to have the facing direction in them. You can still assign the d-pad "hold" input to these input scripts and it will still work. Here is my move UP script for example:
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

Lastly, and this is important, your player bounding box needs to be 2 pixels less on the top and bottom sides. And 1 pixel less on the left and right sides. This keeps the player from "skipping" tiles when they really should be stopping.

Expected output:
When you hold the UP input, your player should keep going up. As soon as the player lets go of UP input, the player should stop at the next tile. Similar to Pokemon.

Also this has the nice side-effect of "lining up" the player at the start of the game run. Newsmaker always starts their sprite 1 pixel off on the Y-axis for some reason. If the player starts on one of these tiles, they will be perfectly centered on the tile!

Let me know if you have any questions!

Here are my sources for figuring out this solution:
 
Alright I got it to work for real this time haha:

I just needed grid movement for the up and down direction but was able to figure out left and right and wanted to share this solution with you. Yes it's messy but it can be shortened and cleaned up.

How does it work? The tile will check for player collision. If the player collides, the tile checks to see if the player is at the center of the tile. If at center of tile, then stop the player. Since the collision system is complex, the direction you collide with the tile will give different collision values. We need to account for this offset by using the player direction as a guide.

Here is the tile script, you will need to paint your whole floor with this tile type:
Code:
;; TODO: clean the code!
;; first check if player collided with this tile:
    CPX player1_object
    BEQ +checkDirection
        ;else not a player
        JMP +skip
 
    +checkDirection
    ;; next check which way we are facing:
     
        LDA Object_direction
        AND #%00000111
        CMP #%00000000 ;Down
        BNE +notDown
            ; is down direction, do this:
         
            ;;convert tile coord to collision coords:
            LDA tileX
            AND #%11110000
            STA tempx
            ;; fix the offset:
            LDA tempx
            SEC
            SBC #$02
            STA tempx
            ;; now do same for Y
            LDA tileY
            AND #%11110000
            STA tempy

            LDA tempy
            SEC
            SBC #$03
            STA tempy
            JMP +continue
        +notDown
        AND #%00000110 ; up
        CMP #%00000100
        BNE +notUp
         
            LDA tileX
            AND #%11110000
            STA tempx

            LDA tempx
            SEC
            SBC #$02
            STA tempx

            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notUp
        AND #%00000110
        CMP #%00000010 ;;right
        BNE +notRight
            LDA tileX
            AND #%11110000
            STA tempx

            LDA tempx
            SEC
            SBC #$02
            STA tempx
            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notRight
        AND #%00000110
        CMP #%00000110
        BNE +notLeft ;; left
            ;PlaySound #sfx_powerup
            LDA tileX
            AND #%11110000
            STA tempx
            LDA tempx
            SEC
            ADC #$00
            STA tempx
            LDA tileY
            AND #%11110000
            STA tempy
            JMP +continue
        +notLeft
            JMP +skip
         
; stop the player at center of tile
        +continue
     
        LDA Object_y_hi,x
        CMP tempy
        BNE +checkX
            JMP stop_player_1
        +checkX
        LDA Object_x_hi,x
        CMP tempx
        BNE +skip
     
        stop_player_1
     
         
            TXA
            STA temp
            StopMoving temp, #$FF, #$00
         
        +skip
     
        RTS

Next your player move scripts NEED to have the facing direction in them. You can still assign the d-pad "hold" input to these input scripts and it will still work. Here is my move UP script for example:
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

Lastly, and this is important, your player bounding box needs to be 2 pixels less on the top and bottom sides. And 1 pixel less on the left and right sides. This keeps the player from "skipping" tiles when they really should be stopping.

Expected output:
When you hold the UP input, your player should keep going up. As soon as the player lets go of UP input, the player should stop at the next tile. Similar to Pokemon.

Also this has the nice side-effect of "lining up" the player at the start of the game run. Newsmaker always starts their sprite 1 pixel off on the Y-axis for some reason. If the player starts on one of these tiles, they will be perfectly centered on the tile!

Let me know if you have any questions!

Here are my sources for figuring out this solution:
You've done an excellent job with this one! It works almost perfectly, but I still see a slight 1-pixel offset when the game begins, and a little bit of weirdness near solid edges where you can slightly move into them but this fixes itself for the most part in the middle of a wide open space. I haven't tested moving through narrow passages yet but I'll let you know what happens.

EDIT: It works for passing through narrow areas. But I've also noticed the code does not prevent you from turning a corner before the movement to the next square is complete.
 

pigeonaut

Member
You've done an excellent job with this one! It works almost perfectly, but I still see a slight 1-pixel offset when the game begins, and a little bit of weirdness near solid edges where you can slightly move into them but this fixes itself for the most part in the middle of a wide open space. I haven't tested moving through narrow passages yet but I'll let you know what happens.
Thanks! I feel like we are at least getting closer. You are right there is still a 1-pixel offset when the game begins, despite the player being on one of these tiles. My eyes def missed that.
 
Still can't figure this out haha. I've tried a whole bunch of things but there's always that one pixel offset I can't seem to fix.
 

pigeonaut

Member
Still can't figure this out haha. I've tried a whole bunch of things but there's always that one pixel offset I can't seem to fix.
What I did to fix the offset is I manually moved the player up by one pixel at the start screen. (When the player presses start, run a script that moves the player up before warping to the play screen). Then from there, the player is fine for the rest of the game.

In the start screen add one of those snap-to-grid tiles we made and if the player walks up, there should be no more offset as the player has been realigned thanks to the tile. Now when the the player warps to a new screen, they should keep the corrected position!
 
What I did to fix the offset is I manually moved the player up by one pixel at the start screen. (When the player presses start, run a script that moves the player up before warping to the play screen). Then from there, the player is fine for the rest of the game.

In the start screen add one of those snap-to-grid tiles we made and if the player walks up, there should be no more offset as the player has been realigned thanks to the tile. Now when the the player warps to a new screen, they should keep the corrected position!
So just subtract 1 from Object_y_hi?
 

pigeonaut

Member
So just subtract 1 from Object_y_hi?
That might work and would probably be a cleaner solution, but I just used one of our snap-to-grid tiles on the start screen and had the player move forward inside of that tile. Then the tile should automatically stop the player once it's aligned with the tile.
 
Top Bottom