Sprite movement without continuous user input.

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
lazerbeat
Posts: 64
Joined: Tue Jul 09, 2013 7:13 am

Sprite movement without continuous user input.

Post by lazerbeat » Mon Jan 05, 2015 8:12 am

I am making a very very simple sprite movement program based on the Nerdy Night tutorial. and I am a bit stuck. I have 4 little bubble moving around the screen at different speeds when the user pressed up / down / left / right. I have 2 questions

1 - how do I make sprite movement continuous without the user constantly holding down a direction. I am guessing at BNE rather than BEQ while reading controllers but I cant work out how to implement it.

2 - How do I make the sprites start moving from the moment the rom is started rather than waiting for user input? I was assuming something like

Code: Select all

  LDA $0200       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$01        ; A = A - 1
  STA $0200       ; save sprite X position
Before reading controllers would do the trick but I couldn't work out where to put it!

Here is the code I have for movement so far. I attached the .nes as well is anyone wanted to have a look at it.

thanks everyone!


Code: Select all

LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons


ReadA: 
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
ReadADone:        ; handling this button is done
  

ReadB: 
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
ReadBDone:        ; handling this button is done


Readselect: 
  LDA $4016       ; player 1 - select
  AND #%00000001  ; only look at bit 0
  BEQ ReadselectDone   ; branch to Readselect Done if button is NOT pressed (0)
ReadselectDone:        ; handling this button is done

Readstart: 
  LDA $4016       ; player 1 - select
  AND #%00000001  ; only look at bit 0
  BEQ ReadstartDone   ; branch to Readstart Done if button is NOT pressed (0)
ReadstartDone:        ; handling this button is done


Readup: 
  LDA $4016       ; player 1 - up
  AND #%00000001  ; only look at bit 0
  BEQ ReadupDone  ; branch to Readup Done if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0200       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$01        ; A = A - 1
  STA $0200       ; save sprite X position

  LDA $0204       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$02        ; A = A - 1
  STA $0204       ; save sprite X position

  LDA $0208       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$03        ; A = A - 1
  STA $0208       ; save sprite X position

  LDA $020C       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$04        ; A = A - 1
  STA $020C       ; save sprite X position
ReadupDone:        ; handling this button is done


Readdown: 
  LDA $4016       ; player 1 + down
  AND #%00000001  ; only look at bit 0
  BEQ ReaddownDone   ; branch to Readdown Done if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0200       ; load sprite Y position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0200       ; save sprite Y position

  LDA $0204       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  ADC #$02        ; A = A + 1
  STA $0204       ; save sprite X position

  LDA $0208       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  ADC #$03        ; A = A + 1
  STA $0208       ; save sprite X position

  LDA $020C       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  ADC #$04        ; A = A + 1
  STA $020C       ; save sprite X position
ReaddownDone:        ; handling this button is done



Readleft: 
  LDA $4016       ; player 1 - select
  AND #%00000001  ; only look at bit 0
  BEQ ReadleftDone   ; branch to Readleft Done if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position

  LDA $0207       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$02        ; A = A - 1
  STA $0207       ; save sprite X position

  LDA $020B       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$03        ; A = A - 1
  STA $020B       ; save sprite X position

  LDA $020F       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$04        ; A = A - 1
  STA $020F       ; save sprite X position
ReadleftDone:        ; handling this button is done


Readright: 
  LDA $4016       ; player 1 - select
  AND #%00000001  ; only look at bit 0
  BEQ ReadrightDone   ; branch to Readright Done if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position

  LDA $0207       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$02        ; A = A + 1
  STA $0207       ; save sprite X position

  LDA $020b       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$03        ; A = A + 1
  STA $020b       ; save sprite X position

  LDA $020f       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$04        ; A = A + 1
  STA $020f       ; save sprite X position
ReadrightDone:        ; handling this button is done
Attachments
stars.nes
(48.02 KiB) Downloaded 76 times

User avatar
tokumaru
Posts: 11744
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Sprite movement without continuous user input.

Post by tokumaru » Mon Jan 05, 2015 11:35 am

lazerbeat wrote:1 - how do I make sprite movement continuous without the user constantly holding down a direction. I am guessing at BNE rather than BEQ while reading controllers but I cant work out how to implement it.
It's a bit more complex than that. The basic idea behind an action game (as opposed to a puzzle game or a turn-based game) is that the game world is constantly updating, regardless of user input. To accomplish this, each object or group of objects has to have their own AI routine, which dictates their behavior. AI routines can take various factors into consideration when deciding what to do to the objects, including input from the controller, in the case of the player object.

The program in the tutorial you're following is an oversimplification of what a game actually is, where certain sprites are hardcoded to controller input.
2 - How do I make the sprites start moving from the moment the rom is started rather than waiting for user input?
By implementing AI. You have to give your objects some rules they can follow. It doesn't have to be anything complex, but if you want your game objects to act on their own you have to make them capable of taking simple decisions, even if it's just "keep moving right no matter what".

To keep things simple for now, you could make a different AI routine (which you can JSR to and RTS from, to keep the code organized) for each object you have on screen, and call all the routines every frame. Inside the routines, just write whatever behavior you expect each object follow. If the object is supposed to move right forever, just add 1 to its X coordinate and you're done. If you want it to bounce up and down the screen, use a "direction" variable to control whether it's going up or down, move it in the current direction (add or subtract 1 from the Y coordinate based on the value "direction" variable) and switch directions whenever the sprite touches the top or the bottom of the screen (Y coordinate equals 0 or 232).

You can keep one or more objects reacting to input, but you can take other factors of the game world into consideration if you want. In a platformer for example, characters are supposed to fall when there's no ground below them because of gravity. That is one movement that happens regardless of what buttons are pressed on the controller. The important thing is to think in terms of rules. What rules does each object follow? How does the environment affect its decisions? How do other objects affect its decisions? Once you have that figured out, you have to translate those rules into code. You will surely need to use many variables to define the state of the objects, because then you'll have to keep track of a lot more than just their coordinates, like direction, speed, health... all of that can affect how an object moves and what it does each frame.
I was assuming something like

Code: Select all

  LDA $0200       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  SBC #$01        ; A = A - 1
  STA $0200       ; save sprite X position
Before reading controllers would do the trick but I couldn't work out where to put it!
This is just the actual hardware sprite being moved, and could be the simplest form of AI where the object only does 1 thing (move left) no matter what.

As for where to place it, I can see that the tutorial you're using is doing something that's not recommended, which is taking action in the middle of reading the controller. This is bad for a number of reason, the main one being it mixes game logic with hardware interfacing, which in the long run will make your code hell to maintain. Another bad reason is that may actually have actions that depend on more than one button being pressed (such as holding a button to run faster in SMB), and if you only have a big block of code for each button you can't do that.

The correct thing to do would be to read all the controller bits and store them in a variable (coincidentally or not, the NES controller has 8 buttons, the same number of bits in a byte), which can be consulted later by any object that needs to know the state of the controller.

So, the ideal thing to do would be to modify the controller reading to just save the status of all the buttons but doesn't make any decisions right away. After that, call all the object AI routines (where the sprite movement code should be) one by one. If they do need to peek at the controller status, great, the variable will be sitting there for them to read.

I know that this will cause a lot of changes in your code, but I guess that's what happens when you try to turn tutorial code into something closer to a game. If you think this is too much, maybe it would be better to stick with the tutorial for now, and maybe it will come to a point where these changes feel more natural. Just keep in mind that AI is the answer to what you're looking for.

Pokun
Posts: 1485
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Sprite movement without continuous user input.

Post by Pokun » Mon Jan 05, 2015 7:19 pm

tokumaru wrote:I can see that the tutorial you're using is doing something that's not recommended, which is taking action in the middle of reading the controller.
It gets better later in the tutorial. Early on it just teaches to do things manually without looping and stuff which is a great way to teach how it works. But of course there things it never teaches how to do properly.
lazerbeat wrote:

Code: Select all

LDA $4016       ; player 1 - A
AND #%00000001  ; only look at bit 0
This is another bad code in the Nerdy Nights tutorial. I don't see why Nerdy Nights don't teach you to read both bit 0 and bit 1 in $4016 (and $4017 if two player). We don't need homebrewers that learn to make games that doesn't work with Famicom external controllers.

tepples
Posts: 22014
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Sprite movement without continuous user input.

Post by tepples » Mon Jan 05, 2015 8:12 pm

I would imagine that the majority of people who read tutorials in the English language have no easy access to a Famicom with which to test.

mikaelmoizt
Posts: 120
Joined: Sat Apr 12, 2014 12:11 pm
Location: Gothenburg, Sweden

Re: Sprite movement without continuous user input.

Post by mikaelmoizt » Tue Jan 06, 2015 3:26 am

If you want just the sprites moving like you have done in your program without any form of interactivity, for whatever reason (why?) you could do something like moving parts like

Code: Select all

  LDA $0200       ; load sprite Y position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0200       ; save sprite Y position

  LDA $0204       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  ADC #$02        ; A = A + 1
  STA $0204       ; save sprite X position

(...) 
into your nmi and cut everything related to controller reading. Are you by any chance using NESASM3 to assemble with? You have a 32k rom with first 16k block empty.

EDIT: This method is not endorsed by me, but it is pretty much how to do what the OP is asking if I am not mistaken.
I´ve got %01100011 problems but the BITs aint one.

Celius
Posts: 2157
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: Sprite movement without continuous user input.

Post by Celius » Tue Jan 06, 2015 11:26 am

mikaelmoizt wrote:

Code: Select all

 
  LDA $0204       ; load sprite X position
  SEC             ; make sure the carry flag is clear
  ADC #$02        ; A = A + 1
  STA $0204       ; save sprite X position

(...) 
I think the SEC is supposed to be CLC in this code snippet.

I also recommend not going the route of hard coding sprite movement; the only sprite you should directly update is sprite #0 if you're doing any effects with it. Like tokumaru explained, developing an abstract "game world" where you have objects moving within it, and then updating the entire sprite page based on object positions is ideal in most cases.

User avatar
Tsutarja
Posts: 123
Joined: Sun Oct 12, 2014 11:06 am
Location: Finland

Re: Sprite movement without continuous user input.

Post by Tsutarja » Tue Jan 06, 2015 12:27 pm

lazerbeat wrote:1 - how do I make sprite movement continuous without the user constantly holding down a direction. I am guessing at BNE rather than BEQ while reading controllers but I cant work out how to implement it.
I would set a flag when the button is pressed. The object checks if the flag is set. If it is, the object moves as you want. The flag gets cleared when you want the object to stop, not when the button is released.
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi

Pokun
Posts: 1485
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Sprite movement without continuous user input.

Post by Pokun » Tue Jan 06, 2015 7:27 pm

tepples wrote:I would imagine that the majority of people who read tutorials in the English language have no easy access to a Famicom with which to test.
It doesn't need to be tested since it's already well known to work (I can confirm it too). It's standard practice for commercial games to read both bits and recommended in the wiki, so there's really no reason to not include it when teaching this.

User avatar
Tsutarja
Posts: 123
Joined: Sun Oct 12, 2014 11:06 am
Location: Finland

Re: Sprite movement without continuous user input.

Post by Tsutarja » Tue Jan 06, 2015 10:56 pm

Also, I noticed that you are reading each button individually and doing all the checks related to that button right after it. Better way is to update all buttons once per frame (all buttons at once) and then see if the desired buttons are pressed somewhere else.

Code: Select all

Joy1Read:
 LDA #$01              ; Latch controllers
 STA Joy1
 LDA #$00
 STA Joy1
 LDX #$08
Joy1Loop:
 PHA
 LDA Joy1
 AND #$03            ; AND off anything unnecessary
 CMP #$01            ; See if button was pressed (sets carry flag if it was)
 PLA
 ROR A                 ; Rotate right (puts carry flag to the leftmost bit whether it was set or not)
 DEX
 BNE Joy1Loop
 RTS
NOTE: Joy1 refers to the $4016 register. You can set one by writing Joy1 = $4016 (or PPUMask = $2001 as another example) after the ines header but before the first .bank (to the same area you define your variables in with .rsset).

NOTE 2: Notice how I'm not storing the controller readings anywhere? That's because I store them outside this subroutine. It allows me to use this same subroutine to read the buttons again and compare the two readings to verify that the inputs were not corrupted by DMC sample (if used).

Now, if you held down A and B you would have %00000011. If you want to see if A button is pressed, you can do something like this:

Code: Select all

CheckA:
 LDA joy1_reading
 AND #%00000001
 CMP #%00000001
 BNE ANotHeld

; What you want to do if A is held

ANotHeld:

; Whatever happens in your code after this
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi

Post Reply