Not fully satisfied with these diagonal directions

dale_coop

Moderator
Staff member
I added diagonal directions to my game, but I am not satisfied with the result...
I made scripts for diagonals. Here, for example, the Press_upright.asm script:
Code:
MoveUpRight_Direct:
    LDX player1_object
    LDA #%11100011
    STA Object_movement,x
    ChangeObjectState #$01
    RTS

Then, I inspired myself from a post by MistSonata, and made a unique Press_AnyDirection.asm script, that calls all the direction movements subroutines of the Press_xxx.asm scripts:
Code:
MoveAny_Direct: 
    ;;; Object_movement
    ;; 0 0 0 0 0 0 0 0
    ;;           x x x
    ;            0 0 0 = down
    ;            0 0 1 = down right
    ;            0 1 0 = right
    ;            0 1 1 = up right
    ;            1 0 0 = up
    ;            1 0 1 = up left
    ;            1 1 0 = left
    ;            1 1 1 = down left

    LDA gamepad
    AND #%11110000
    BNE moveDirection
    RTS
    
moveDirection:
    ;LDA gamepad
         ;+---------- is it moving horizontal? 0 = no, 1 = yes
		 ; + left or right? 0= left, 1= right
		 ;  +-------- is it moving vertical? 0 = no, 1 = yes
		 ;   + up or down? 0= up, 1= down	
    CMP #%10010000 ;; check if upright is pressed
    BEQ moveUpRightDirect
    CMP #%10100000 ;;check if downright is pressed
    BEQ moveDownRightDirect
    CMP #%01010000 ;;check if upleft is pressed
    BEQ moveUpLeftDirect
    CMP #%01100000 ;;check if downleft is pressed
    BEQ moveDownLeftDirect  
    CMP #%00010000 ;; check if up is pressed
    BEQ moveUpDirect
    CMP #%00100000 ;;check if down is pressed
    BEQ moveDownDirect
    CMP #%01000000 ;;check if left is pressed
    BEQ moveLeftDirect
    CMP #%10000000 ;;check if right is pressed
    BEQ moveRightDirect
    RTS
	
moveUpRightDirect:
	JMP MoveUpRight_Direct
	RTS

moveDownRightDirect:
	JMP MoveDownRight_Direct
	RTS

moveUpLeftDirect:
    JMP MoveUpLeft_Direct
	RTS

moveDownLeftDirect:
    JMP MoveDownLeft_Direct  
	
moveUpDirect:
    JMP MoveUp_Direct
	RTS
	
moveDownDirect:
    JMP MoveDown_Direct
	RTS

moveLeftDirect:
    JMP MoveLeft_Direct
	RTS

moveRightDirect:
    JMP MoveRight_Direct
	RTS

Then in the "Input manager", I assigned my "Press_AnyDirection.asm" script to the Top, Right, Bottom and Left Press inputs.

It works, but the control is quite sloppy. For example, when I keep pressing UpRight, my player is going UpRight... but I need to not move my thumb.
Else if I move my thumb to the Right (without release anything) the player goes more to the right.
It's not strictly 8 way directions, in fact it's like playing with an analog stick.

Does someone know how to fix my code to have 8 ways?

If you try my game, you will understand what I try to explain :
https://drive.google.com/open?id=1VNXbPdlqQxPf0-STbzGAcJFDhUB-otgp

Maybe it's not possible, and finally, the controls of my game are quite good (not perfect) but I have a lot of fun with it... as it, it's the more important.
 

MistSonata

Moderator
I'm not sure what exactly you mean, and playing the game myself I didn't really notice any wonkiness, but I do see that the movement is very fluid, and maybe it's that you're looking for something more rigid. I'm not sure exactly how you might make it more rigid, but you might try adding code to stop moving after "BEQ moveRightDirect" and call the script in the input manager every time a direction is released.
 

dale_coop

Moderator
Staff member
Yes exactly that, MistSonata.
I wanted something more rigid... because it’s a minimalist old (Atariesque) style NES game.
But I played tonight with a proper controller, and « fluid » as it is now, is not too bad ;)

I think Inwill let it like that for now.

Thanks for your comment and your help, MistSonata
<3
 

RadJunk

Administrator
Staff member
There are LOTS of ways of handling this. Mystic Origins has very pleasant diagonal controls, and even offsets the speed a bit when moving diagonal (to about 75%) so that it feels like the same speed...

It's just a LOT of conditional checks and whatnot. These instructions were more to get people familiar with the concepts (why I keep calling them vanilla solutions). In fact, you COULD bypass the input controls altogether and write your own input->movement controls yourself.

Here's a method that might work, though.

Only use four directions HELD...left, right, up and down, in the input editor. But in the scripts for left and right movement, check directly to see if the gamepad is pressed...first, check if up is pressed, if it is, set up right diagonal. Then check if down is pressed, if it is, set down right diagonal. If neither, it's just set move moving right.

You can check the gamepad directly like this:
Code:
LDA gamepad
      ;; AND #%xxxxxxxx 
      ;; RIGHT | LEFT | DOWN | UP | START | SELECT | B | A
      ;; put a 1 in the bit you want to check for
      ;; and the rest 0.
      ;; so to check up it would be...
AND #%00010000
BEQ upWasNotPressed ;; that bit = 0, so it was not pressed
      ;;; here, it means up WAS pressed
      ;;;; set diag motion
      JMP doneSettingRightMovement
 upWasNotPressed:
      ;;; check down....
      ;;; do the things
      JMP doneSettingRightMovement
 downWasNotPressed
      ;;; right was pressed, but not up or down.
      ;;; so set just right here.
      
 doneSettingRightMovement:


There are probably a hundred ways to do this. That's just one example. Hope it gives you some ideas! :)
 

dale_coop

Moderator
Staff member
Very interesting way of handling that.
I will try (for the learning purpose) when I'll have some hours of freetime for myself (next weekend maybe).

Thank you, Joe.
 

Mihoshi20

Member
Handling input on the NES is one of the last things about the hardware I can't seem to grasp or wrap my brain around simply by looking at example code and pouring through the technical documents aside from the initial latch to prep for reading. For Mermay's Den I simply used some wonderful code MistSonata made and then just modified it to work when pressing a direction instead of having to hold a button to move and also fixed it to work with the animation state changes.

But I can't rely on someone else's code forever so I've been knuckling down lately studying how the input works and ways to read the inputs and use the data. I have to admit the NES' quirks are something I was completely unprepared for when I started down the path of working with the NES. NESmaker makes it far easier especially for rapid prototyping but you're still at the whim of an engine you yourself didn't design which adds a customization hurdle for complete noobz.

I have still learned far more in rapid time then going it alone and will be happy once figuring out the input clicks into place.
 

RadJunk

Administrator
Staff member
Probably the reason you can't latch your head around it is because of ALL the things the physics engine is doing for you. It's thick. It's actually really easy to make something move.

Code:
LDA gamepad
And #%10000000 ;; is the right button down?
BEQ rightButtonNotPressed ;; if that bit is equal to zero, it's not pressed
;;;;; here would be what happens if it IS pressed
LDA Object_x_hi,x ;; assuming x is loaded with the object you want to move
CLC
ADC #$04 ;; a speed of 4
STA Object_x_hi,x ;; store it into the new x

That's how to make something move. However, it doesn't handle acceleration, deceleration, collision detection, edge checking, etc. That's why we created what equates to a "turn on movement" set of simple macros...flipping the movement bit to "start moving right" does ALL of that. :)
 

Mihoshi20

Member
TheNew8bitHeroes said:
Probably the reason you can't latch your head around it is because of ALL the things the physics engine is doing for you. It's thick. It's actually really easy to make something move.

Code:
LDA gamepad
And #%10000000 ;; is the right button down?
BEQ rightButtonNotPressed ;; if that bit is equal to zero, it's not pressed
;;;;; here would be what happens if it IS pressed
LDA Object_x_hi,x ;; assuming x is loaded with the object you want to move
CLC
ADC #$04 ;; a speed of 4
STA Object_x_hi,x ;; store it into the new x

That's how to make something move. However, it doesn't handle acceleration, deceleration, collision detection, edge checking, etc. That's why we created what equates to a "turn on movement" set of simple macros...flipping the movement bit to "start moving right" does ALL of that. :)

Okay, that does seem far clearer though I do have one lingering question and that's with 'gamepad' in the code it's loaded with #$80 and I'm not sure exactly what it's doing as the value is usually either rotated to the right (ROR'd) or loaded before a comparison is made between two button presses.

Exert from GamePadCheck.asm
Code:
LDA #$01
	STA $4016
	LDA #$00
	STA $4016
	LDA #$80
	STA gamepad
 

Kasumi

New member
The carry flag is a single bit.

When you ROtate Right (ROR) a value, the following things happen (simplified for understanding.)
1. The current state of the carry flag (1 or 0) is stored safely in a temporary place in the CPU. (That you'll never have access to.)
2. The rightmost bit of the byte you used the ROR on (0 or 1) is copied to the actual carry flag.
3. All bits in the byte are shifted right. (The bit that is second from the right, replaces the rightmost bit. The bit that is thirdmost from the right, replaces the bit that is second from the right. Etc.)
4. The old state of the carry is retrieved from that safe temporary location to replace the leftmost bit.
Let's look at what happens for a ROR of $80 that starts with the carry set. ($80 is %10000000)
Code:
Carry set (1):
%10000000
;^      ^;This bit is copied into the carry.
;|The old carry replaces this bit.
;Result after ROR:
%11000000
;^This bit was the old carry
;And now the carry is clear, because the rightmost bit of %10000000 was clear.
Code:
Carry Clear (0)
%10000000
;^      ^;This bit is copied into the carry.
;|The old carry replaces this bit.
;Result after ROR:
%01000000
;^This bit was the old carry
;And now the carry is clear, because the rightmost bit of %10000000 was clear.
Now without explantion for each step, let's look at what happens when we ROR $80 eight times.
Code:
clc;Not explicitly needed, but for the example, it should be a known state.
lda #$80
;in A is binary 10000000; The carry flag is clear. (0)
ROR;ROR #1.
;In A is binary 01000000; The carry flag is clear. (0)
ROR;ROR #2
;In A is binary 00100000; The carry flag is clear. (0)
ROR;ROR #3
;In A is binary 00010000; The carry flag is clear. (0)
ROR;ROR #4
;In A is binary 00001000; The carry flag is clear. (0)
ROR;ROR #5
;In A is binary 00000100; The carry flag is clear. (0)
ROR;ROR #6
;In A is binary 00000010; The carry flag is clear. (0)
ROR;ROR #7
;In A is binary 00000001; The carry flag is clear. (0)
ROR;ROR #8
;In A is binary 00000000;The carry flag is SET! (1)
There are eight button on NES. Up, Down, Left, Right, A, B, Start and select.

If you start a loop with #$80, the carry will be set after the eighth time ROR is done. Since you can branch based on the state of the carry directly, you avoid doing a compare eight times.

Code:
lda #$80
loop:
;This will be run exactly 8 times
ROR A
bcc loop;branch if the carry flag is clear (0) to the loop label

So the store of #$80 makes reading each of the 8 buttons slightly faster. This is done once per frame. Then, any time you want to know the state of the buttons for the rest of the frame, you load the gamepad variable in RAM. Then AND. If you and with a value that has only a single bit set, the result will be either zero or non zero based on that state of the "matching" bit.

Since each bit is a button, you will get either 0 or 1, for pressed or unpressed.

In short:
1. #$80 makes a faster loop if you want to do something exactly 8 times. (You can also use #$01 and ROL, but gamepad will have the buttons in the opposite order, then.)
2. Then lda gamepad, and AND #%(some value with one set bit) will let you decide if a button is pressed or not by branching on the zero flag. (BNE or BEQ)

Edit: As far as the lda #1, sta $4016, lda #0, sta $4016. That tells the hardware to start reading from the first button again. It's not automatically going back to the first button after 8 reads is what makes reading the extra two controllers with four score possible. (Also reading the extra button on an SNES controller as discussed in that other topic.)
 

dale_coop

Moderator
Staff member
Agree with you Mihoshi, it’s not easy, because we need to learn how the engine is made, to understand how modify.
And the Joe’s coding tutorial videos are a great help for that.

Katsumi, you blew my mind! Your knowledge is impressive and I feel so small with my small problems of LDA BNE BQE AND CMP ;)

Thank you guys it’s a pleasure to read you
 

Mihoshi20

Member
Kasumi said:
The carry flag is a single bit.

When you ROtate Right (ROR) a value, the following things happen (simplified for understanding.)
1. The current state of the carry flag (1 or 0) is stored safely in a temporary place in the CPU. (That you'll never have access to.)
2. The rightmost bit of the byte you used the ROR on (0 or 1) is copied to the actual carry flag.
3. All bits in the byte are shifted right. (The bit that is second from the right, replaces the rightmost bit. The bit that is thirdmost from the right, replaces the bit that is second from the right. Etc.)
4. The old state of the carry is retrieved from that safe temporary location to replace the leftmost bit.
Let's look at what happens for a ROR of $80 that starts with the carry set. ($80 is %10000000)

Ah okay, so it literally is just the same equivalent of the binary. Many thanks on shedding light on that portion and a bit of a routine walk-through. It's been a massive help on clearing that portion on the code up and what's going on with it. It's good having someone skilled in the NES hardware and ASM with us, especially in the case of the clueless such as myself. LOL.
 
Top Bottom