4.5.9 Grid Based Walking (Adventure Module)

SciNEStist

Well-known member
So this has been asked a few times, so here is a simple and effective way to have grid-based walking in your adventure game. There may be extra steps you will need to do, like removing the knockback code in playerhurt, but this should get you started at least. If someone can make this more efficient, please post with your edits.

CLICK HERE FOR AN UPDATED VERSION THAT ALLOWS FOR PLAYER SPEED CONTROL


HOW TO USE:


Step 1: remove all input controls for up left down and right, including hold, press, and release
Step 2: add in a user variable in Project settings> User variable called "walkcount" with a default of 0
step 3: in around line 15 of doHandlePhysics_AdventureBase you will see

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;; FOR ADVENTURE PHYSICS
;;;;;;;;;;;;;;;;;;;;;;;;;;; DON'T UPDATE POSITIONING IF ATTACKING
    CPX player1_object
    BEQ +
        JMP +notPlayer
    +
        ;; it was the player.
        ;; if this object is in attack action, currently set to 2,
        ;; he should not move, and should skip physics check
        TXA
        STA temp
        GetActionStep temp
        CMP #$02
        BNE +notPlayer ;; skip, because was not 2.
            JMP skipPhysics
 
    +notPlayer

replace it with this:

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;; FOR ADVENTURE PHYSICS
;;;;;;;;;;;;;;;;;;;;;;;;;;; DON'T UPDATE POSITIONING IF ATTACKING
    CPX player1_object
    BEQ +
        JMP +notPlayer
    +
;START OF CUSTOM STUFF

            LDY Object_type,x
            LDA ObjectBboxLeft,y
            STA self_left
            CLC
            ADC ObjectWidth,y
            STA self_right
            CLC
            LDA ObjectBboxTop,y
            STA self_top
            CLC
            ADC ObjectHeight,y
            STA self_bottom
        
        
        LDA walkcount
        CMP #$00
        BEQ +notwalking
            DEC walkcount
            LDA Object_direction,x
            AND #%00000111
            CMP #$02; right
            BNE +
                INC Object_x_hi,x
                LDA Object_x_hi,x
                ADC self_right
                CMP #BOUNDS_RIGHT
                BCC ++
                    LDA #$01
                    STA screenUpdateByte
                    JSR doHandleBounds
                    JMP skipPhysics
                ++
                JMP +donewalk
            +
            CMP #$06; left
            BNE +
                DEC Object_x_hi,x
                LDA Object_x_hi,x
                CMP #BOUNDS_LEFT
                BCS ++
                    LDA #$03
                    STA screenUpdateByte
                    JSR doHandleBounds
                    JMP skipPhysics
                ++
                JMP +donewalk
            +
            CMP #$04; up
            BNE +
                DEC Object_y_hi,x
                JMP +donewalk
            +
            CMP #$00; down
            BNE +
                INC Object_y_hi,x
                LDA Object_y_hi,x
                ADC self_bottom
                CMP #BOUNDS_BOTTOM
                BCC ++
                    LDA #$00
                    STA screenUpdateByte
                    JSR doHandleBounds
                    JMP skipPhysics
                ++
                JMP +donewalk
            +
            JMP +donewalk
        +notwalking
            STX temp ;; assumes the object we want to move is in x.
            LDA currentNametable ; there are 2 collision tables, one for odd screens, and one for even.
            AND #%00000001 ; this gets the right one for the current screen
            STA tempA ; and saves it to tempA
            LDA gamepad ; read the gamepad
            AND #%10000000 ; is it right?
            BEQ +
                ChangeFacingDirection temp, #FACE_RIGHT ; face right nop matter what
                LDA Object_x_hi,x ; all this is for checking out the new coords to test
                ADC ObjectWidth,y
                ADC #$08
                STA temp
                LDA Object_y_hi,x
                ADC self_top
                STA temp1
                ;DrawSprite temp, temp1, #$0A, #%00000000, #$00
                CheckCollisionPoint temp, temp1, #$00, tempA ; is the next tile walkable?
                BEQ +walk
                    ChangeActionStep player1_object, #$00
                    JMP +donewalk ; if not, dont walk. we could also add a sound before this line if you want
                +walk
                ChangeActionStep player1_object, #$01 ; set player to walking action step
                LDA #$10 ; set the countdown to 16 (#$10 is 16), change this if you want a bigger grid
                STA walkcount
                JMP +donewalk
            +
            LDA gamepad
            AND #%01000000 ; left
            BEQ +
                ChangeFacingDirection temp, #FACE_LEFT
                LDA Object_x_hi,x
                ADC self_left
                SBC #$08
                STA temp
                LDA Object_y_hi,x
                ADC self_top
                STA temp1
                ;DrawSprite temp, temp1, #$0A, #%00000000, #$00
                CheckCollisionPoint temp, temp1, #$00, tempA
                BEQ +walk
                    ChangeActionStep player1_object, #$00
                    JMP +donewalk
                +walk
                    ChangeActionStep player1_object, #$01
                    LDA #$10
                    STA walkcount
                    JMP +donewalk
            +
            LDA gamepad
            AND #%00010000 ; up
            BEQ +
                ChangeFacingDirection temp, #FACE_UP
                LDA Object_x_hi,x
                ADC self_left
                STA temp
                LDA Object_y_hi,x
                ADC self_top
                SBC #$08
                STA temp1
                ;DrawSprite temp, temp1, #$0A, #%00000000, #$00
                CheckCollisionPoint temp, temp1, #$00, tempA
                BEQ +walk
                    ChangeActionStep player1_object, #$00
                    JMP +donewalk
                +walk
                    ChangeActionStep player1_object, #$01
                    LDA #$10
                    STA walkcount
                    JMP +donewalk
            +
            LDA gamepad
            AND #%00100000  ;down
            BEQ +
                ChangeFacingDirection temp, #FACE_DOWN
                LDA Object_x_hi,x
                ADC self_left
                STA temp
                LDA Object_y_hi,x
                ADC self_bottom
                ADC #$08
                STA temp1
                ;DrawSprite temp, temp1, #$0A, #%00000000, #$00
                CheckCollisionPoint temp, temp1, #$00, tempA
                BEQ +walk
                    ChangeActionStep player1_object, #$00
                    JMP +donewalk
                +walk
                    ChangeActionStep player1_object, #$01
                    LDA #$10
                    STA walkcount
                    JMP +donewalk
            +
            ChangeActionStep player1_object, #$00
        +donewalk
;;END OF CUSTOM STUFF
        ;; it was the player.
        ;; if this object is in attack action, currently set to 2,
        ;; he should not move, and should skip physics check
        TXA
        STA temp
        GetActionStep temp
        CMP #$02
        BNE +notPlayer ;; skip, because was not 2.
            JMP skipPhysics
 
    +notPlayer

EXTRAS:

change what you can walk over:


the script above is set to check if a tile is walkable before allowing you to move, so anything other than a walkable type 0 tile will block you. If you want to change this so you can walk over any tile type other than solids, change all 4 instances of theses lines:
Rich (BB code):
                CheckCollisionPoint temp, temp1, #$00, tempA
                BEQ +walk
into these:
Code:
                CheckCollisionPoint temp, temp1, #$01, tempA
                BNE +walk

offset player spawn:

Now, if you are one of those people who like an odd sized height on your player object (3 tiles tall for example), you will notice that you can only spawn a player on a 16X16 grid, so your feet will start half way between 2 tiles. Since we can't have that, a good option is to either

A: put an extra blank row of tiles above your players head to make it an even count. if you do this, i strongly recommend you read this thread: https://www.nesmakers.com/index.php...ng-blank-sprites-to-not-draw.5938/#post-29744

B: add a bit of code that pushes your player down a bit to get it on the tile properly

To do option B, you can add this bit to the end of your post screen load script:

Code:
    SwitchBank #$1C   
    LDX player1_object
    LDY Object_type,x
    LDA ObjectSize,y
    LSR
    BCC +
        LDA Object_y_hi,x
        ADC #$07
        STA Object_y_hi,x
    +
    ReturnBank

that will leave the even tiled player objects alone (2, 4, 6, 8) and move the odd tiled player objects down 8 pixels to put them where they should be (1,3,5,7)
 
Last edited:

SciNEStist

Well-known member
cant sleep, so updating it. now it checks the adjacent tile first before moving to avoid partial movements, and also changes the action steps to trigger walking animations.
 

Jonny

Well-known member
Thanks @SciNEStist I was trying to figure out how to do this. I got grid movement working from a previous tutorial, sorry no credit I forgot where it came from. This method only does jump movements but I was trying to make a Frogger like game which animates into the next block. I'll give your method a try.

This is what I had for input scripts, example down...

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; STORE COLLISION POINTS AS TEMPS ;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    LDX player1_object
    LDA Object_x_hi,x
    CLC
    ADC self_center_x
    STA tempA   

    LDA Object_y_hi,x
    CLC
    ADC self_center_y
    SEC
    ADC #$08   
    STA tempB
    
    ChangeFacingDirection player1_object, #FACE_DOWN
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; CHECKS BEFORE NEW POSITION SET ;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    GetCollisionPoint tempA, tempB, #$00
    CMP #$01
    BEQ +end
    
    LDA Object_y_hi,x
    CLC
    CMP #208
    BEQ +end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; SET NEW POSITION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    LDA tempB
    STA Object_y_hi,x
    PlaySound #$06
    +end
    RTS
 

Jonny

Well-known member
I presume your method, the player walks into the block rather than just snapping to it?
 

SciNEStist

Well-known member
yes indeed, player just strolls over and animates.

Be warned, one issue Ive just found is that the object edge stuff seems to be acting up, so walking off the edge isnt warping to the next screen like it should. can't fix that tonight though.
 

Jonny

Well-known member
That's amazing. Personally I just use warp tiles for all warping as I find that easier to understand/manage. Thanks again
 

SciNEStist

Well-known member
UPDATE:
  • fixed tile collision detection to work off the player collision box instead of x/y coords.
  • fixed it so it works with screen boundaries
  • added note to help with odd tile sized player objects
 

SciNEStist

Well-known member
sorry, this script only allows for a speed of 1 pixel per frame, since it more or less ignores physics stuff. you could double the speed by doubling certain lines in the script if you like.
 

tbizzle

Well-known member
sorry, this script only allows for a speed of 1 pixel per frame, since it more or less ignores physics stuff. you could double the speed by doubling certain lines in the script if you like.
Is it possible to decrease the speed?
 

SciNEStist

Well-known member
it could be halved with some fun code to skip the movement every other frame.

in the handle physics code right between lines 41 and 42 "BEQ +notwalking" and "DEC walkcount" add the following lines:
Code:
        LDA vBlankTimer
        AND #%00000001
        BEQ +
            JMP +donewalk
        +
 

SciNEStist

Well-known member
V2: Brought to you by tbizzle!

After writing this for tbizzle, he was gracious enough to let me share it with everyone. In this version, you can set the player speed using the "Normal Max Speed" slider bar on the player object details window. movement is still in a 16X16 grid, but how fast the player gets there is up to you.


You will need to modify 2 scripts to get this working, both are included in the gridwalk.zip

STEP1: Replace doHandlePhysics_AdventureBase.asm with doHandlePhysics_GridAdventureBase.asm (included in the zip file on this post)

STEP2: add the user variable "walktoggle" and "walkstop" in project settings, user variables tab.

STEP3: replace updateScreenAtEdge.asm with "updateScreenAtEdgeGrid.asm"(included in the zip file on this post)

STEP4: if your player is not a 16X16 size, you might need to adjust some of the offsets in this script. They are set up to keep the player exactly on the grid when warping between screens at the edge. they are marked with the comment ";;YOU MAY NEED TO ALTER THIS VALUE"

STEP5: in your player object details screen, set the "normal max speed" anywhere between 1 and 100. (setting higher than 100 can result in erratic behaviour) Also set the "Edge object reaction" to "Go To Next Screen" You should also give at least 1 pixel space at the edges of the bounding box.

Now you should be able to walk around at your own pace!
 

Attachments

  • gridwalk.zip
    5.4 KB · Views: 30
V2: Brought to you by tbizzle!

After writing this for tbizzle, he was gracious enough to let me share it with everyone. In this version, you can set the player speed using the "Normal Max Speed" slider bar on the player object details window. movement is still in a 16X16 grid, but how fast the player gets there is up to you.


You will need to modify 2 scripts to get this working, both are included in the gridwalk.zip

STEP1: Replace doHandlePhysics_AdventureBase.asm with doHandlePhysics_GridAdventureBase.asm (included in the zip file on this post)

STEP2: add the user variable "walktoggle" and "walkstop" in project settings, user variables tab.

STEP3: replace updateScreenAtEdge.asm with "updateScreenAtEdgeGrid.asm"(included in the zip file on this post)

STEP4: if your player is not a 16X16 size, you might need to adjust some of the offsets in this script. They are set up to keep the player exactly on the grid when warping between screens at the edge. they are marked with the comment ";;YOU MAY NEED TO ALTER THIS VALUE"

STEP5: in your player object details screen, set the "normal max speed" anywhere between 1 and 100. (setting higher than 100 can result in erratic behaviour) Also set the "Edge object reaction" to "Go To Next Screen" You should also give at least 1 pixel space at the edges of the bounding box.

Now you should be able to walk around at your own pace!
This is probably better for my strategy game's cursor... thanks!
 
okay sooooo funny request what if hypothetically you want some screens with regular movement for other objects & for the overworld you have the grid movement?
asking for a future Idea I have.
 
Top Bottom