Port 2 Controller Input (buttons always registered as "pressed")

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

User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

Hello! I'll get right into it. I'm working on making a Pong game for a tutorial (Nerdy Nights, of course), but I've run into a bit of a snag I don't understand. For some reason, the "down" button on controller 2 is always active, sending the second player's paddle sliding down the screen.

I thought maybe there was an issue in my code, but there wasn't anything major I could see that would tell the program that the second player's controller was sending "down" constantly. My ReadController2 subroutine is basically the exact same as my ReadController1 subroutine, except it's reading from $4017 instead of $4016. I checked the other buttons, and they seem to function correctly without any issues, I can try and send the paddle back up no problem (although, if I set the carry flag before doing the arithmetic for decreasing the paddle's y position, pushing up just stops it in place. Leaving the flag clear allows it to slowly inch back up the screen).

I then checked Mesen's debugger to see what my program was loading from my second controller, hoping it'd shed some light. As I recall, it always recieved the down button as "pressed". I checked it again after tinkering with it today just before making this post, and I see that it loads a value of $FF after reading from the second controller, meaning that every button is considered "pressed". This is perplexing, because 1) I'm not pressing any buttons for controller 2, and 2) Controller 1 receives a value of $00 when I'm not inputting anything on it. If I'm not inputting anything on controller 2, and the code for reading it is the same as the code for reading controller 1, which behaves correctly, then why is controller 2 constantly inputting all buttons?

By the by, if any code excerpts are required to give sound advice, just ask and I'll post up the relevant/requested sections.
What a horrible night to have a curse.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Pokun »

It sounds like a simple miss somewhere in the code but it's not something I can make good guesses without at at least seeing the code.
Could you share the input reading code (where you check what buttons are pressed and stores the states in RAM) as well as the input handling code (where the input states are actually used in the code)? In Nerdy Nights they might have both those merged together (I forgot), in that case just post the whole controller code.

You may also wanna check if you have setup some joystick or something on your computer to the second controller for the emulator that is misbehaving.
User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

Pokun wrote: Sun May 22, 2022 1:15 pm It sounds like a simple miss somewhere in the code but it's not something I can make good guesses without at at least seeing the code.
Could you share the input reading code (where you check what buttons are pressed and stores the states in RAM) as well as the input handling code (where the input states are actually used in the code)? In Nerdy Nights they might have both those merged together (I forgot), in that case just post the whole controller code.

You may also wanna check if you have setup some joystick or something on your computer to the second controller for the emulator that is misbehaving.
Sure thing! First up is the input reading:

Code: Select all

ReadController1:
    LDA #$01
    STA $4016 ; polling input for controller 1
    LDA #$00
    STA $4016

    LDX #$08 ; setting up loop counter

    :
        LDA $4016
        LSR A ; send controller status bit into carry
        ROL buttons1 ; send status bit from carry into buttons1 (a variable elsewhere in code)
        DEX
        BNE :- ; this loop runs 8 times, once for each button 
    RTS

ReadController2:
    LDA #$01
    STA $4017 ; polling input for controller 2
    LDA #$00
    STA $4017
    
    LDX #$08 ; loop counter

    :
        LDA $4017
        LSR A ; controller status bit into carry
        ROL buttons2 ; carry into buttons2 (also a variable)
        DEX 
        BNE :- ; this loop runs 8 times, once for each button 
    RTS
And now, the input handling. Here's the code for player 1:

Code: Select all

        MovePaddle1Up:
            JSR ReadController1
            LDA buttons1
            AND #%00001000 ; is "up" being pressed?
            BEQ MovePaddle1UpDone ; if not, skip everything

            LDA paddle1ytop
            CMP #TOPWALL ; is the top of the paddle colliding with the upper bound of the screen?
            BCC MovePaddle1UpDone ; only continue if it is not

            SBC paddlespeed ; 1, in this case
            STA paddle1ytop
            CLC
            ADC #$08 ; offset to render the bottom sprite of the paddle properly
            STA paddle1ybottom
        MovePaddle1UpDone:

        MovePaddle1Down: 
            JSR ReadController1
            LDA buttons1
            AND #%00000100 ; is "down" being pressed?
            BEQ MovePaddle1DownDone

            LDA paddle1ybottom
            CMP #BOTTOMWALL ; is the bottom of the paddle colliding with the lower bound of the screen?
            BCS MovePaddle1DownDone ; BCS here because paddle1ybottom would have to be greater than/equal to BOTTOMWALL to be colliding

            ADC paddlespeed
            STA paddle1ybottom
            SEC
            SBC #$08 ; offset to render top paddle sprite properly
            STA paddle1ytop
        MovePaddle1DownDone:
And here's the code for player 2's paddle:

Code: Select all

        MovePaddle2Up:
            JSR ReadController2
            LDA buttons2
            AND #%00001000 ; is "up" being pressed? 
            BEQ MovePaddle2UpDone

            LDA paddle2ytop
            CMP #TOPWALL ; is the top of the paddle colliding with the upper bound?
            BCC MovePaddle2UpDone

            SBC paddlespeed
            STA paddle2ytop
            CLC
            ADC #$08 ; sprite offset
            STA paddle2ybottom
        MovePaddle2UpDone:

        MovePaddle2Down: 
            JSR ReadController2
            LDA buttons2
            AND #%00000100 ; is "down" being pressed?
            BEQ MovePaddle2DownDone

            LDA paddle2ybottom
            CMP #BOTTOMWALL ; is the bottom sprite colliding with the lower bound?
            BCS MovePaddle2DownDone

            ADC paddlespeed
            STA paddle2ybottom
            SEC
            SBC #$08 ; sprite offset
            STA paddle2ytop
        MovePaddle2DownDone:
And to answer your question about joysticks, I didn't have any connected to my computer during the testing i described, I was using the keyboard for input, and my controllers are all wired (i.e. unplugged).
What a horrible night to have a curse.
User avatar
Movax12
Posts: 541
Joined: Sun Jan 02, 2011 11:50 am

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Movax12 »

mechadendrite wrote: Sun May 22, 2022 1:28 pm

Code: Select all

ReadController2:
    LDA #$01
    STA $4017 ; polling input for controller 2
    LDA #$00
    STA $4017
Change $4017 to $4016.
Suggested: One controller reading routine that reads both. No need to call it again when a button press doesn't match, just read the controllers once per frame.
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Fiskbit »

To explain further, while each joypad has its own register for reading, there is only a single shared write register for them. When you write 1 and then 0 to $4016, both controllers see that and latch their current button state. Writing to $4017 is actually an entirely unrelated register called the frame counter.

And indeed, a controller-reading function is normally only called once per frame; there's generally no reason to read more often than that, and it may lead to undesirable behavior to do so (allowing opposite directions to be pressed in a single frame of game logic, which causes problems in a lot of games). You might also want to learn about using the X register avoid repeating all this code; you can index with X equal to 0 for player 1, and then do it again with X equal to 1 for player 2, and your player variables would need to be placed one after the other (buttons1 followed immediately by buttons2) so you can use X as an index to get the appropriate variable per player. Maybe that's something coming up in the tutorial, though; I'm not sure.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Pokun »

Ah yes that is probably the problem. $4016 is the output register used to latch all controllers (including expansion port controllers) so it must be written to both times. Writing to $4017 does not latch any controllers.
I'm not sure what Fiskbit meant by using the X register. I think Mechadendrite is already doing that isn't he? But yes you would normally just read both controllers in the same routine, that way you only need to write to $4016 once. But that is just a small optimization and not important for making it work.

This is how my controller reading routine looks like (asm6 syntax):

Code: Select all

con_read:
;Reads current controller button states.
  lda #$01
  sta $4016            ;set $4016.0
  lsr a
  sta $4016            ;clear $4016.0, controllers latched
  ldx #$08             ;loop counter for 8 buttons
@loop:
  lda $4016            ;get button data from D0 (con I) and D1 (con III)
  lsr a                ;shift button data from D0 into carry
  rol con_state+0      ;rotate carry into a RAM register (con I buttons)
  lsr a                ;shift button data from D1 into carry
  rol temp+0           ;rotate carry into a RAM register (con III buttons)
  lda $4017            ;get button data from D0 (con II) and D1 (con IV)
  lsr a                ;shift button data from D0 into carry
  rol con_state+1      ;rotate carry into a RAM register (con II buttons)
  lsr a                ;shift button data from D1 into carry
  rol temp+1           ;rotate carry into a RAM register (con IV buttons)
  dex
  bne @loop
@merge:                ;merge controller I with III and II with IV using OR
  lda temp+0
  ora con_state+0
  sta con_state+0      ;OR con III with con I
  lda temp+1
  ora con_state+1
  sta con_state+1      ;OR con IV with con II
  rts
This code also reads the two Famicom expansion port controllers (con III and IV) by shifting both bit 0 and bit 1 of $4016/$4017 into RAM registers. It then merges them with con I and II using ORA. This is a good habit for any game that uses one or two controllers, and most commercial games do this too. It means that people with Famicoms (like me) can use any controller we want with your game instead of just the two hardwired ones. Nerdy Nights doesn't teach this so consider this a bonus lesson.

As Movax and Fiskbit both said you should only call this controller reading routine once per frame, typically the first thing in your main loop. This makes sure that all logic uses the same "snapshot" of your controllers' states, avoiding any inconsistencies which can be the cause of bugs.


Another bonus lesson you won't learn in Nerdy Nights is to use metasprites.
Instead of moving both the top and bottom parts of the paddle, just decide one of them to be the "parent" sprite and the other the "child" sprite, and only move the parent in your input handler. Then somewhere after that in your code you have a separate routine that updates the X- and Y-positions of all child sprites to a certain relative of their respective parent sprites. This way you only have to move one hardware sprite per "metasprite" (paddle). You are almost doing this, but you update the child sprite (bottom paddle part) inside the input handler which is wasteful since you need to do it both for when moving up and when moving down, and would also make the code longer if you decide to make the paddle longer by adding more child sprites to it.

About any other game more complex than Pong that uses more hardware sprites for each metasprite and also have more complex movement than just up and down will benefit from this much more.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by tokumaru »

Using a temporary variable for reading expansion port controllers is not necessary... Most games do it in a significantly faster way:

Code: Select all

lda $4016
and #%00000011 ;keeps only the relevant bits
cmp #$01 ;sets the carry if bits 0 or 1 are set
rol con_state+0
One reason to keep the amount of time spent reading controllers down is to reduce the chances of corrupted reads caused by DPCM samples that might be playing. The longer the controller-reading code takes, the higher the chances that it'll be interrupted by a DPCM fetch. If a high-frequency sample is playing and the controller-reading code is too slow, the chances of it getting interrupted could be 100%, meaning it'll be very hard for you to get an accurate read.
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Fiskbit »

I had meant using an index register (usually X) to avoid all of this code repetition. You don't need two duplicate copies of all of this code for each joypad or for each player; you can do $4016,index or buttons,index. Avoiding code duplication leads to code that is cleaner, easier to understand, and easier to maintain such that they don't begin to diverge and develop bugs.
User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

Pokun wrote: Sun May 22, 2022 3:43 pm Ah yes that is probably the problem. $4016 is the output register used to latch all controllers (including expansion port controllers) so it must be written to both times. Writing to $4017 does not latch any controllers.
I'm not sure what Fiskbit meant by using the X register. I think Mechadendrite is already doing that isn't he? But yes you would normally just read both controllers in the same routine, that way you only need to write to $4016 once. But that is just a small optimization and not important for making it work.

This is how my controller reading routine looks like (asm6 syntax):

Code: Select all

con_read:
;Reads current controller button states.
  lda #$01
  sta $4016            ;set $4016.0
  lsr a
  sta $4016            ;clear $4016.0, controllers latched
  ldx #$08             ;loop counter for 8 buttons
@loop:
  lda $4016            ;get button data from D0 (con I) and D1 (con III)
  lsr a                ;shift button data from D0 into carry
  rol con_state+0      ;rotate carry into a RAM register (con I buttons)
  lsr a                ;shift button data from D1 into carry
  rol temp+0           ;rotate carry into a RAM register (con III buttons)
  lda $4017            ;get button data from D0 (con II) and D1 (con IV)
  lsr a                ;shift button data from D0 into carry
  rol con_state+1      ;rotate carry into a RAM register (con II buttons)
  lsr a                ;shift button data from D1 into carry
  rol temp+1           ;rotate carry into a RAM register (con IV buttons)
  dex
  bne @loop
@merge:                ;merge controller I with III and II with IV using OR
  lda temp+0
  ora con_state+0
  sta con_state+0      ;OR con III with con I
  lda temp+1
  ora con_state+1
  sta con_state+1      ;OR con IV with con II
  rts
This code also reads the two Famicom expansion port controllers (con III and IV) by shifting both bit 0 and bit 1 of $4016/$4017 into RAM registers. It then merges them with con I and II using ORA. This is a good habit for any game that uses one or two controllers, and most commercial games do this too. It means that people with Famicoms (like me) can use any controller we want with your game instead of just the two hardwired ones. Nerdy Nights doesn't teach this so consider this a bonus lesson.

As Movax and Fiskbit both said you should only call this controller reading routine once per frame, typically the first thing in your main loop. This makes sure that all logic uses the same "snapshot" of your controllers' states, avoiding any inconsistencies which can be the cause of bugs.
Ahhhh, now that makes much more sense. I'll have to get in the habit of consulting the NesDev wiki pages for the more important memory locations more frequently! That controller routine is a hot tip, also. The Nerdy Nights tutorial seems to separate the controller input code, but this is a lot more convenient, I'll definitely be doing it this way in the future. Also very glad that I've made things more accessible for people with other controllers, that's pretty awesome!
Pokun wrote: Sun May 22, 2022 3:43 pm Another bonus lesson you won't learn in Nerdy Nights is to use metasprites.
Instead of moving both the top and bottom parts of the paddle, just decide one of them to be the "parent" sprite and the other the "child" sprite, and only move the parent in your input handler. Then somewhere after that in your code you have a separate routine that updates the X- and Y-positions of all child sprites to a certain relative of their respective parent sprites. This way you only have to move one hardware sprite per "metasprite" (paddle). You are almost doing this, but you update the child sprite (bottom paddle part) inside the input handler which is wasteful since you need to do it both for when moving up and when moving down, and would also make the code longer if you decide to make the paddle longer by adding more child sprites to it.

About any other game more complex than Pong that uses more hardware sprites for each metasprite and also have more complex movement than just up and down will benefit from this much more.
Ooh, now this also makes a lot of sense. I was starting to get a little annoyed with having to write the same lengthy code for sprite handling over and over again! :lol: One thing I'm immediately curious about is how one handles collision with metasprites, though. I can update my sprite-updating subroutine easily enough, changing it so it only uses the variable for the paddle top and simply offsetting the sprites for the paddle bottoms by 8 pixels, but the system I'm tinkering with for this Nerdy Nights example has collision code that looks like this:

Code: Select all

CheckPaddle1Collision: 
            LDA ballx
            CMP #PADDLE1X
            BCS CheckPaddle1CollisionDone ; is ballx < paddlex?

            LDA bally 
            CMP paddle1ytop
            BCC CheckPaddle1CollisionDone ; is bally > paddle1ytop?

            CMP paddle1ybottom
            BCS CheckPaddle1CollisionDone ; is bally < paddle1ybottom?

            LDA #$00
            STA ballleft ; ball is not moving left anymore
            LDA #$01
            STA ballright ; bounce

        CheckPaddle1CollisionDone:
With similar code for paddle 2. If I'm to change this to better resemble the metasprites system, all of a sudden the variable for paddle1ybottom and paddle2ybottom seem like dead weight, even though things like this code and the movement handling use it to a large extent. I feel like I'm missing something here, maybe I should put it out of my mind for now and focus on completely finishing the Pong example. Either way, it's something I'll be studying more soon, I'll warrant. Thanks for the tips!
What a horrible night to have a curse.
User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

tokumaru wrote: Sun May 22, 2022 4:10 pm Using a temporary variable for reading expansion port controllers is not necessary... Most games do it in a significantly faster way:

Code: Select all

lda $4016
and #%00000011 ;keeps only the relevant bits
cmp #$01 ;sets the carry if bits 0 or 1 are set
rol con_state+0
One reason to keep the amount of time spent reading controllers down is to reduce the chances of corrupted reads caused by DPCM samples that might be playing. The longer the controller-reading code takes, the higher the chances that it'll be interrupted by a DPCM fetch. If a high-frequency sample is playing and the controller-reading code is too slow, the chances of it getting interrupted could be 100%, meaning it'll be very hard for you to get an accurate read.
I'll have to keep that bit about conflict with the audio samples in mind for later. I think I understand what's happening here re: code, doing the CMP here sets the carry whether it's bit 0 or bit 1 that's set, so it effectively merges the input from both into one, which is then stored in the controller input variable. What happens if input comes in from both controller 1 and controller 3, though? Which is prioritized?
What a horrible night to have a curse.
User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

Fiskbit wrote: Sun May 22, 2022 4:18 pm I had meant using an index register (usually X) to avoid all of this code repetition. You don't need two duplicate copies of all of this code for each joypad or for each player; you can do $4016,index or buttons,index. Avoiding code duplication leads to code that is cleaner, easier to understand, and easier to maintain such that they don't begin to diverge and develop bugs.
I'm not sure I follow you here, but code duplication is bad, that much I understand all too well. If you'd like to elaborate further about how to use the X index register to track player input instead of 2 separate variables, I'd love to hear more.
What a horrible night to have a curse.
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Fiskbit »

Here's a conversion of your paddle moving code into a single generic function that can handle either player 1 or player 2, and code that calls it for each player. This is pretty quick-and-dirty, but hopefully illustrates what I mean.

Code: Select all

  buttons:  .res 2
   kButtonRight  = %00000001
   kButtonLeft   = %00000010
   kButtonDown   = %00000100
   kButtonUp     = %00001000
   kButtonStart  = %00010000
   kButtonSelect = %00100000
   kButtonB      = %01000000
   kButtonA      = %10000000

  paddle_y_top:     .res 2
  paddle_y_bottom:  .res 2

  paddle_speed:     .res 1

; ...

  JSR con_read

  ; Move paddles for players 1 and 2.
  LDX #$00
  JSR MovePaddle
  INX
  JSR MovePaddle

; ...

MovePaddle:
 MovePaddle_Down:
  LDA buttons,X
  AND #kButtonUp
  BEQ MovePaddle_UpDone ; if not, skip everything

  LDA paddle_y_top,X
  CMP #TOPWALL ; is the top of the paddle colliding with the upper bound of the screen?
  BCC MovePaddle_UpDone ; only continue if it is not

  ; SEC
  SBC paddle_speed ; 1, in this case
  STA paddle_y_top,X
  CLC
  ADC #$08 ; offset to render the bottom sprite of the paddle properly
  STA paddle_y_bottom,X
 MovePaddle_UpDone:

 MovePaddle_Down: 
  LDA buttons,X
  AND #kButtonDown
  BEQ MovePaddle_DownDone

  LDA paddle_y_bottom,X
  CMP #BOTTOMWALL ; is the bottom of the paddle colliding with the lower bound of the screen?
  BCS MovePaddle_DownDone ; BCS here because paddle1ybottom would have to be greater than/equal to BOTTOMWALL to be colliding

  ; CLC
  ADC paddle_speed
  STA paddle_y_bottom,X
  SEC
  SBC #$08 ; offset to render top paddle sprite properly
  STA paddle_y_top,X
 MovePaddle1DownDone:

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

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by tokumaru »

mechadendrite wrote: Sun May 22, 2022 5:01 pmI'll have to keep that bit about conflict with the audio samples in mind for later.
If you plan on using DPCM samples, fortifying your controller-reading code is a must. You can either keep reading until two consecutive reads match, or reuse last frame's input if the second read doesn't match.
I think I understand what's happening here re: code, doing the CMP here sets the carry whether it's bit 0 or bit 1 that's set, so it effectively merges the input from both into one, which is then stored in the controller input variable.
Yup.
What happens if input comes in from both controller 1 and controller 3, though? Which is prioritized?
Any value equal to or larger than 1 will cause the carry to be set after the CMP instruction (1 = button on controller 1, 2 = button on controller 3, 3 = button on both controllers), so the result is effectively the same as ORing the bits from both controllers, which means that pressed buttons always win over non-pressed buttons, no matter the controller.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by Pokun »

mechadendrite wrote: Sun May 22, 2022 4:57 pm Ooh, now this also makes a lot of sense. I was starting to get a little annoyed with having to write the same lengthy code for sprite handling over and over again! :lol: One thing I'm immediately curious about is how one handles collision with metasprites, though. I can update my sprite-updating subroutine easily enough, changing it so it only uses the variable for the paddle top and simply offsetting the sprites for the paddle bottoms by 8 pixels, but the system I'm tinkering with for this Nerdy Nights example has collision code that looks like this:

Code: Select all

CheckPaddle1Collision: 
            LDA ballx
            CMP #PADDLE1X
            BCS CheckPaddle1CollisionDone ; is ballx < paddlex?

            LDA bally 
            CMP paddle1ytop
            BCC CheckPaddle1CollisionDone ; is bally > paddle1ytop?

            CMP paddle1ybottom
            BCS CheckPaddle1CollisionDone ; is bally < paddle1ybottom?

            LDA #$00
            STA ballleft ; ball is not moving left anymore
            LDA #$01
            STA ballright ; bounce

        CheckPaddle1CollisionDone:
With similar code for paddle 2. If I'm to change this to better resemble the metasprites system, all of a sudden the variable for paddle1ybottom and paddle2ybottom seem like dead weight, even though things like this code and the movement handling use it to a large extent. I feel like I'm missing something here, maybe I should put it out of my mind for now and focus on completely finishing the Pong example. Either way, it's something I'll be studying more soon, I'll warrant. Thanks for the tips!
Treat the metasprite as a single object and do your collision box relative from a single hot-spot. Naturally that might be the position of the parent sprite (top-left pixel of top paddle), but many game designers make up an arbitrary hot-spot in the middle of the metasprite, and in platform games it is common to use the middle pixel of the bottom row of pixels in the metasprite to simplify platform jumping logic.

But the simplest is to just use the top left pixel which is already used for sprite position. You need to know the height and width of your metasprite in pixels, then when you test for bottom collision you just add the height of the metasprite and for collision on the right side of the paddle you add the width.
You can use constants for your height and width to avoid magic numbers, or use RAM registers to store them if they are variable.

You never need to test collisions with the child sprites. They are just there for looks.
User avatar
mechadendrite
Posts: 10
Joined: Tue May 17, 2022 9:00 pm
Location: North America

Re: Port 2 Controller Input (buttons always registered as "pressed")

Post by mechadendrite »

Pokun wrote: Mon May 23, 2022 11:30 am Treat the metasprite as a single object and do your collision box relative from a single hot-spot. Naturally that might be the position of the parent sprite (top-left pixel of top paddle), but many game designers make up an arbitrary hot-spot in the middle of the metasprite, and in platform games it is common to use the middle pixel of the bottom row of pixels in the metasprite to simplify platform jumping logic.

But the simplest is to just use the top left pixel which is already used for sprite position. You need to know the height and width of your metasprite in pixels, then when you test for bottom collision you just add the height of the metasprite and for collision on the right side of the paddle you add the width.
You can use constants for your height and width to avoid magic numbers, or use RAM registers to store them if they are variable.
Ahhh, I think I understand now. I've been too stuck on treating each individual sprite tile as if it were an object itself, I need to kind of "zoom out" and consider what I want the final metasprite object to look/act like and then code accordingly. Thanks for the clarification!
What a horrible night to have a curse.
Post Reply