Controller Input Subroutines: How many?
Moderator: Moderators
- MetalSlime
- Posts: 186
- Joined: Tue Aug 19, 2008 11:01 pm
- Location: Japan
Controller Input Subroutines: How many?
Hello, I'm still in the design phase of my little one-screen game and I'm making a list of all subroutines I will have to write. I have a simple question about how to handle Controller Input.
First a little info. My game has a few different gamestates (TitleScreen, Movie, Gameplay, Paused, GameOver). Some gamestates have very few Controller Input possibilities (Paused gamestate only has one: START to unpause). Other gamestates have many Controller Input possibilities (Gameplay).
I will have one ReadController subroutine that will be called outside of gamestates and store button status in RAM. Then within the gamestates I will want to handle that input, so...
My question is:
Should I have just one HandleInput subroutine that will handle all possible gamestate (i.e. all gamestates will call the same subroutine)? or have a separate subroutine for each gamestate (HandleInputTitleScreen, HandleInputMovie, HandInputGameplay, etc)?
or both? or neither?
What's the best way to organize this?
First a little info. My game has a few different gamestates (TitleScreen, Movie, Gameplay, Paused, GameOver). Some gamestates have very few Controller Input possibilities (Paused gamestate only has one: START to unpause). Other gamestates have many Controller Input possibilities (Gameplay).
I will have one ReadController subroutine that will be called outside of gamestates and store button status in RAM. Then within the gamestates I will want to handle that input, so...
My question is:
Should I have just one HandleInput subroutine that will handle all possible gamestate (i.e. all gamestates will call the same subroutine)? or have a separate subroutine for each gamestate (HandleInputTitleScreen, HandleInputMovie, HandInputGameplay, etc)?
or both? or neither?
What's the best way to organize this?
-
Celius
- Posts: 2159
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
I would definitely have different segments of code to handle button presses for different things. But you should have a segment of code that reads button presses, and puts all the button press information into a single byte (1 bit per button), then read from this value in each section. Oh, and also have a "ButtonPressOld" byte which holds the values of the buttons pressed last frame.
-
UncleSporky
- Posts: 388
- Joined: Sat Nov 17, 2007 8:44 pm
If I had the time I would do it both ways and weigh the resulting data/cycle cost.
I don't see it being that difficult to go
But it takes a few more cycles and I also don't see it being that difficult to call different routines.
I don't see it being that difficult to go
Code: Select all
if (start is pressed)
if (gamestatus = titlescreen)
start game
else if (gamestatus = paused)
unpause game
else
pause game
endif
endif- MetalSlime
- Posts: 186
- Joined: Tue Aug 19, 2008 11:01 pm
- Location: Japan
Thanks for the replies guys! I have a lot of good ideas to think about. But I think I didn't communicate my question as clearly as I wanted to, because the answers didn't hit the point that was bothering me. I hope it will be a little clearer what I'm asking with this reply.
because button priorities might differ by gamestate and because I want each gamestate to be as separate from the other ones as possible.
The idea behind having one master subroutine would be for readability/organization. It's easier for me if all gamestates are interfacing the Controller Input stuff through the same subroutine:
But it comes at the cost of checking the gamestate twice (once to get to the gamestate's main codeblock, which is where the HandleInput call will be, and then once again within HandleInput to take the correct branch).
Having separate subroutines for each gamestate (HandleInputTitleScreen, HandleInputPause, etc) would avoid the double check at the cost of losing a common interface/readability.
Hope I'm explaining my dilemma alright. It's hard to talk about this stuff in words! Basically I want to have the master subroutine, but I'm worried that it's wasteful with such limited resources.
i.e. different code in each gamestate's main codeblock?
or would I have a master subroutine (HandleInput, see above) that sets the correct pointers and performs the jump?
i.e. each gamestate's main codeblock will have the same call to HandleInput?
Again, thanks for your comments so far!
In the case of one master subroutine, I was originally thinking of nesting it like this:UncleSporky wrote: I don't see it being that difficult to go
But it takes a few more cycles and I also don't see it being that difficult to call different routines.Code: Select all
if (start is pressed) if (gamestatus = titlescreen) start game else if (gamestatus = paused) unpause game else pause game endif endif
Code: Select all
if (gamestate=titlescreen)
if (start is pressed)
start game
if (down is pressed)
move menu cursor down
....
if (gamestate=movie)
if (start is pressed)
skip movie
...
etcThe idea behind having one master subroutine would be for readability/organization. It's easier for me if all gamestates are interfacing the Controller Input stuff through the same subroutine:
Code: Select all
check gamestate, jump to correct label
TitleScreen:
Do some stuff
JSR HandleInput
JMP GamestateStuffDone
Movie:
prepare next frame for drawing
JSR HandleInput
JMP GamestateStuffDone
Gameplay:
Check game timer to see if you're dead
JSR HandleInput
UpdateScore
....
....
If it's been 60 frames, decrement GameTimer
JMP GamestateStuffDone
...
etc.Having separate subroutines for each gamestate (HandleInputTitleScreen, HandleInputPause, etc) would avoid the double check at the cost of losing a common interface/readability.
Hope I'm explaining my dilemma alright. It's hard to talk about this stuff in words! Basically I want to have the master subroutine, but I'm worried that it's wasteful with such limited resources.
so within each gamestate, somewhere I'd have:Celius wrote: Here's a good method. Store the address of the controller routine at say $40/$41 in ZP. Then when you want to go to the controller routine, do JMP ($40). So if you want to point to a different controller routine, change $40/$41 to the address of the desired routine.
Code: Select all
set pointer in $40/$41
jmp ($40)or would I have a master subroutine (HandleInput, see above) that sets the correct pointers and performs the jump?
i.e. each gamestate's main codeblock will have the same call to HandleInput?
Again, thanks for your comments so far!
- MetalSlime
- Posts: 186
- Joined: Tue Aug 19, 2008 11:01 pm
- Location: Japan
Good idea. So everytime you change a gamestate, you'd set a series of pointers for every set of gamestate-specific routines. Makes a lot of sense. How many different pointers do you usually use for this kind of thing, and for what kinds of functionality (other than controller input handling)?Celius wrote:Anywhere where you make a decision to change game modes, you set the pointer to $40/$41. Probably not right before you jump to it.
And a newbie question. If I jump to my Controller Input Routines via a pointer in ZP, how do I get back? Since I'm not using JSR, I can't return with RTS, right? Once I'm done handling the input, how would I get back to where I started?
MetalSlime runs away.
-
Celius
- Posts: 2159
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
If you know where you're jumping from, all you have to do is something like:
jmp ($40)
Label:
...
;Address jumped to with jmp($40)
;blah code
jmp Label
So you don't really do a variable "Return", but just a direct jump back to a label ahead of "jmp ($40)". If you don't know where you're jumping from, that's a long story. You'd want to do something like "jmp ($42)" where you return in the end of the routine gone to with jmp ($40). $42/$43 hold the address of where you want to return. Actually, all you'd have to do then is something like this:
lda #High(Label)
sta $43
lda #Low(Label)
sta $42
jmp ($40)
Label:
...
;Code gone to with jmp ($40)
;Blah code
jmp ($42)
This is okay to do, however you could eat up a lot of bytes having this sort of set up for lots of different routines.
jmp ($40)
Label:
...
;Address jumped to with jmp($40)
;blah code
jmp Label
So you don't really do a variable "Return", but just a direct jump back to a label ahead of "jmp ($40)". If you don't know where you're jumping from, that's a long story. You'd want to do something like "jmp ($42)" where you return in the end of the routine gone to with jmp ($40). $42/$43 hold the address of where you want to return. Actually, all you'd have to do then is something like this:
lda #High(Label)
sta $43
lda #Low(Label)
sta $42
jmp ($40)
Label:
...
;Code gone to with jmp ($40)
;Blah code
jmp ($42)
This is okay to do, however you could eat up a lot of bytes having this sort of set up for lots of different routines.
Sorry I didn't read the whole thread (yet), but reading 8 bits of the controller, and storing the result in a byte is really the standard way to do it. I've always do that since my very first demo and all commercial games seems to do that. Then you can take the older controller status eor-ed with #$ff and anded with that byte to detect all '0' to '1' transitions (which means the button has just been pressed), which is often as usefull as just detecting a '1' (wich means the button is hold down).
Do do the unpause thing you'd want to do something like that
Where "JoyLocked" is the variable that detects '0' to '1' transitions (you don't want to use JoyData, which is when the button is pressed, I just made those names up and use them in all my programms, but you can use your owns).
Do do the unpause thing you'd want to do something like that
Code: Select all
_pause
jsr WaitVBl
jsr ReadController
lda JoyLocked
and #$10
bne _pause
ReadController
lda JoyData
eor #$ff$
pha
jsr ReadJoypad1 ;This overwrite JoyData with new value
pla
and JoyData
sta JoyLocked
rts
Useless, lumbering half-wits don't scare us.
-
UncleSporky
- Posts: 388
- Joined: Sat Nov 17, 2007 8:44 pm
For organizing something like this, I've thought about a lookup table system. For example:
Where GameStatus $01 is the title screen, $02 is the main game etc. You read VideoRoutine modified by (GameStatus * 2) to get to the starting bit. You could use this to organize a lot of other things too such as level music.
What do you more experienced people think of this? Is it wasteful? I like it on the surface but I'm not experienced yet in optimization.
Code: Select all
VideoRoutine:
.dw TitleVideo, GameVideo, StatusVideo
ControlRoutine:
.dw TitleControl, GameControl, StatusControlWhat do you more experienced people think of this? Is it wasteful? I like it on the surface but I'm not experienced yet in optimization.
Oh, I used to use this "gamestate" technique when I started out. The main loop would be nothing but an instruction that jumps over itself, and the NMI would do a few common things (like sprite DMA) before jumping to a different adress in function of game state. However, this was extremely limited and tedious as your code will always jump to the same adress no matter what, so you neeed a lot of variables and check them to know where to jump, and not only it is tedious, but wastes CPU ressources. I even got a system that get rid of those limitations for the AI of my enemies.
However, Nintendo used that system for some of their earlier games, and Konami seems to use it all the time, so if you know what you do and if that works for you I'd say go for it.
After a quick automated seach in my source files, there is 26 times I call a "WaitVbl" routine. Doing an equivalent programm with the game state mode would correspond to 26 game states and you'd jump to one of those 26 places depending on it, which should be some trouble to handle.
However, Nintendo used that system for some of their earlier games, and Konami seems to use it all the time, so if you know what you do and if that works for you I'd say go for it.
After a quick automated seach in my source files, there is 26 times I call a "WaitVbl" routine. Doing an equivalent programm with the game state mode would correspond to 26 game states and you'd jump to one of those 26 places depending on it, which should be some trouble to handle.
Useless, lumbering half-wits don't scare us.
- MetalSlime
- Posts: 186
- Joined: Tue Aug 19, 2008 11:01 pm
- Location: Japan
I thought of another question. Is there an advantage to using this pointer system over just JSRing to subroutines? It looks like it would be harder to keep track of my code with all of this manual jumping. Why not just JSR to a HandleInputTitleScreen subroutine (if I were in the TitleScreen gamestate, for example) instead?Celius wrote:If you know where you're jumping from, all you have to do is something like:
jmp ($40)
Label:
...
;Address jumped to with jmp($40)
;blah code
jmp Label
.....(cut)......
This is okay to do, however you could eat up a lot of bytes having this sort of set up for lots of different routines.
You're awesome! Figuring out a way to detect 0 to 1 transitions was next on my list of things to figure out. It will be quite necessary for my game to know this. Thank you!Bregalad wrote:Then you can take the older controller status eor-ed with #$ff and anded with that byte to detect all '0' to '1' transitions (which means the button has just been pressed), which is often as usefull as just detecting a '1' (wich means the button is hold down).
To clarify, would these data words hold the addresses where the routines are located, i.e. the addresses you want to jump to?UncleSporky wrote:For organizing something like this, I've thought about a lookup table system.
Code: Select all
VideoRoutine: .dw TitleVideo, GameVideo, StatusVideo ControlRoutine: .dw TitleControl, GameControl, StatusControl
Well, I am just starting outBregalad wrote:Oh, I used to use this "gamestate" technique when I started out.
....(cut)....
However, Nintendo used that system for some of their earlier games, and Konami seems to use it all the time, so if you know what you do and if that works for you I'd say go for it.
And I don't have anywhere near 26 gamestates. I have 6.
Thanks for all the help guys. I'm really learning a lot (more than I expected!)
-
UncleSporky
- Posts: 388
- Joined: Sat Nov 17, 2007 8:44 pm
Yes, those are just labels that get processed as addresses by the assembler. You use them as normal before the routines:MetalSlime wrote:To clarify, would these data words hold the addresses where the routines are located, i.e. the addresses you want to jump to?UncleSporky wrote:For organizing something like this, I've thought about a lookup table system.
Code: Select all
VideoRoutine: .dw TitleVideo, GameVideo, StatusVideo ControlRoutine: .dw TitleControl, GameControl, StatusControl
Code: Select all
TitleVideo:
blah blah
rts
GameControl:
blah blah
rtsMy game is not really complex either. I just have a title screen, a stage introduction screen and gameplay. I haven't coded any end movie or credits yet. It's just that I don't use gamestates, but I do a "jsr WaitVbl" whenever I have a frame completed, and it returns to the next of the programm. This is not really hard to understand even for newbies.And I don't have anywhere near 26 gamestates. I have 6. Smile And one of them (EndingMovie/Credits) accepts no input and won't be reached very often, so really it's more like I have 5. But I see your point. With a more complex game, it would be extremely tedious jumping between 26 different gamestates!
It's just that when you want for example to draw a window texbox on screen, you so something like draw rows 1&2, wait a VBlank, draw rows 3&4, wait a VBlank, etc... in a loop.
If you don't have a wait VBlank method, but a game state method I have no idea how you're supposed to do that. You should probably have one "sub game state" by possible row, which sounds terribly tedious.
Even more complex Konami games like Castlevania III, Lagrange Point, Bucky o'Hare, and so on are all coded that way. I really have no idea how Konami programmers were able to do it that way, but maybe it's just not as a headache as I can remember, if you use a lot of indirect jumps cleverly.
Useless, lumbering half-wits don't scare us.