#17383 - darknet - Sun Mar 07, 2004 3:37 am
I remember seeing this question quite a while ago on the forums, but am unable to find it now..
Basically, my game obviously includes stuff like "press A' to continue and when the user presses 'A', the GBA + emulator are both very very sensitive and actually skip ahead to other portions of the game.
I use the more or less standard code for checking for button input like so:
Code: |
#define RIGHT_BUTTON 16
[etc...]
#define pressed(n) (!((*BUTTONS) & n))
|
and for checking if the next screen should be displayed because the user pressed RIGHT, i do the following:
Code: |
while(!pressed(RIGHT_BUTTON));
//load new graphic
|
Which works, but is entirely too too sensitive.
IN short, how does one detect once the button has been pressed AND released? Only then would I want to act, not just when its been pressed.
Any help is greatly appreciated,
-Mike
#17384 - tepples - Sun Mar 07, 2004 3:49 am
To detect a press or release, you need to store the last state of the buttons.
Code: |
#define JOY (*(volatile unsigned short)0x04000130)
int last_joy;
while(game_loop)
{
int j = JOY ^ 0x03ff; /* keys being held down */
int j_down = j & ~last_joy; /* keys pressed since last game loop */
int j_up = last_joy & ~j; /* keys released since last game loop */
/* act on j_down and j_up */
last_joy = j;
} |
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#17386 - dagamer34 - Sun Mar 07, 2004 4:24 am
Just so you know, most games don't check to see if a certain button is pressed at the time of checking, but if it just got pressed compared to last frame.
One of those things you don't learn in a tutorial.
_________________
Little kids and Playstation 2's don't mix. :(
#17392 - darknet - Sun Mar 07, 2004 6:52 am
Thanks a lot to both of you.
In response to daGamer, I understand what you mean, but exactly would your words be implemented?
If i have a decision that is based upon what button was just pressed, then the button pressing/releasing should be acted upon after a vBlank? I am a little confused.
In any case, thank you both very much,
-Mike
#17400 - darknet - Sun Mar 07, 2004 10:36 am
Tepples,
I read the code you posted and think I understand it...however I am unsure as to how to apply it to my problem at hand:
I have a picture + text based adventure (i.e. a game where you're shown a picture and some text and have to make a decision based on the question at hand---think those old 'choose your own adventure' books).
Basically, while I realize it may not be the most efficient way to go about things, I have each screen be a bitmap and I basically want to check button presses at each screen and move to the next screen based upon their decision. For example, each screen gives the user 3 choices, and to get to each choice one has to press a different button.
The way i am checking for it now does not work, here's a sample of the code, which shows one screen and 3 decisions...
Code: |
one = true;
drawMode3Background(videoBuffer, stageOne_Bitmap);
if(one) {
if(pressed(A_BUTTON)) {
while(!pressed(A_BUTTON) && one);
one = false;
twoA = true;
drawMode3Background(videoBuffer, stageTwoA_Bitmap);
}
if(pressed(B_BUTTON)) {
while(!pressed(B_BUTTON) && one);
one = false;
twoB = true;
drawMode3Background(videoBuffer, stageTwoB_Bitmap);
}
}
|
'one' above means they are AT screen one, and so on. The code above does not work at all, and at best will quickly shift through all the screens when a button is pressed.
So finally, in reference to your suggestion, how would or could that be incorporated?
Hope this wasn't too long winded,
Thanks,
-Mike[/i]
#17404 - yaustar - Sun Mar 07, 2004 2:22 pm
side question: what does the ~ operator do? The articles I have read skipped over it showing it in XOR.
On the part of the button presses...I do have a extremely poor method that works but will disrupt game flow and tepples method is much better anyway :)
Code: |
if(!BUTTON_B)
{
while(!BUTTON_B)
{
//do nothing
}
}
|
_________________
[Blog] [Portfolio]
#17411 - poslundc - Sun Mar 07, 2004 3:54 pm
yaustar wrote: |
side question: what does the ~ operator do? The articles I have read skipped over it showing it in XOR.
|
~ is the bit-complement operator in C; it flips the bits of any value/variable.
Oddly enough, one of the least-documented C operators, considering its importance.
Dan.
#17412 - poslundc - Sun Mar 07, 2004 4:04 pm
darknet wrote: |
The way i am checking for it now does not work, here's a sample of the code, which shows one screen and 3 decisions...
Code: |
one = true;
drawMode3Background(videoBuffer, stageOne_Bitmap);
if(one) {
if(pressed(A_BUTTON)) {
while(!pressed(A_BUTTON) && one);
one = false;
twoA = true;
drawMode3Background(videoBuffer, stageTwoA_Bitmap);
}
if(pressed(B_BUTTON)) {
while(!pressed(B_BUTTON) && one);
one = false;
twoB = true;
drawMode3Background(videoBuffer, stageTwoB_Bitmap);
}
}
|
|
Well, the control system you have set up here is kind of wacky. You have a while loop that will never execute inside a strange choice of control structure.
Instead of trying to find a way to fix it, I strongly suggest you read up a bit on finite state machines. They are an extremely useful concept for setting up game controls for something like the GBA.
Once you've got that concept down, create the following two states:
A. Viewing a picture
B. Viewing a picture with a button pushed down; waiting for release
Depending on what state your machine is in, you will want to respond differently to user controls. If you're in state A and the user presses a button, you go to state B. If you're in state B and the user releases the button they were pressing, you swap the picture and go back to state A.
Finite state machines are probably the most organized and reliable way to design a system like this. Even if you get your current system working it is likely to become unwieldy as you develop further if you don't find a more logical way to organize it.
Dan.
#17415 - Paul Shirley - Sun Mar 07, 2004 5:09 pm
removed
Last edited by Paul Shirley on Sun Mar 28, 2004 8:59 pm; edited 1 time in total
#17446 - Cearn - Mon Mar 08, 2004 9:54 am
Maybe these functions will help
Code: |
u16 key_curr= KEY_ANY, key_prev= KEY_ANY;
void key_poll()
{
key_prev= key_curr;
key_curr= REG_P1;
}
// <EDIT>
u16 key_is_down(u16 key)
{ return ~key_curr & key; }
u16 key_is_up(u16 key)
{ return key_curr & key; }
// </EDIT>
u16 key_transit(u16 key)
{ return (key_curr^key_prev) & key; }
u16 key_held(u16 key)
{ return ~(key_curr|key_prev) & key; }
u16 key_pressed(u16 key)
{ return (~key_curr&key_prev) & key; }
u16 key_released(u16 key)
{ return (key_curr&~key_prev) & key; }
|
If you call key_poll once a frame, then these functions will tell you
EDIT: - if the button is currently down (key_is_down)
EDIT: - if the button is currently up (key_is _up)
- if the button's state has changed (key_transit)
- if it's held down (key_held)
- if it's pressed (EDIT: i.e., going down!) this frame (key_pressed)
- if it's released (EDIT: i.e., going up!) this frame (key_released)
Last edited by Cearn on Wed Mar 10, 2004 1:27 pm; edited 1 time in total
#17460 - Miked0801 - Mon Mar 08, 2004 8:12 pm
I could see why ~ would be mentioned as xor as it is equivilent to xor'ing a value by 0xFFFFFFFF.
#17468 - darknet - Mon Mar 08, 2004 11:54 pm
The FSM structure does sound like the most logical and proper way to structure this portion of my project. I have studied them before, but never in the useful gaming context =) Thanks for that advice..
Thanks too to Cearn for the button pressing/releasing code.
My only question so far with it (there may be more unfortunately !) is what key_poll() is setting key_curr to.? What exactly is REG_P1? I am assuming its a register I have defined under a different identifier in my project, but am nonetheless curious.
Any help is appreciated,
Thanks again,
-Mike
#17469 - dagamer34 - Tue Mar 09, 2004 12:11 am
REG_P1 is the key register at 0x04000130. key_poll () is updating the current state of the keys directly from the register.
_________________
Little kids and Playstation 2's don't mix. :(
#17470 - darknet - Tue Mar 09, 2004 12:34 am
Thats what I thought, just wanted to make sure (i named it something differently).
Thanks,
-Mike
#17473 - darknet - Tue Mar 09, 2004 1:36 am
First of all, I am very sorry for continually asking questions about things I am sure are obvious to most. Hopefully that will soon be the case for me as well =)
Back to Cearn's code...
so every VBlank I would call key_poll()....ok
then when I want to check if a button has been pressed and released would i do the following?
if(key_pressed(A_KEY) && key_released(A_KEY) ) { do something... }
i notice the functions return unsigned shorts, so i dunno if an 'if' statement can simply check for those two?
if I am somehow right in that usage, when would the transition functio be used? Why would I ever need to check if the the state has changed?
OORRR...would I use the code the same way tepples did by basically setting an int to each function AND THEN checking.
The way I have played around with it has not worked thus far, but I am positive it is my usage of your functions and not the functions themselves.
Thanks for any feedback,
-Mike
Finally, is the initialization important (the KEY_ANY at the top)?
#17477 - DekuTree64 - Tue Mar 09, 2004 3:43 am
My input system works off frame counters for each key. Normally each counter is set to 0xff or any value you want to mean un-pressed. Then if a key is pressed, you set its bit in a var called keyDown, and set its counter to a delay, like 30, which would be half a second. Then each frame, unset its bit in keyPress, and if the key is still down, decrement its counter. If it's not pressed, set it back to 0xff. Then if the counter gets to 0, set its bit in another var called keyHold.
Then you can use keyPress to tell if a key has just been pressed, and keyHold to tell if you've held it down for as long as your delay time. That's good for things like menus where you want to be able to move one space at a time, or move fast if you hold the key down.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#17560 - darknet - Wed Mar 10, 2004 6:10 am
Hopefully you guys aren't frustrated with me yet =)...
For those that are not, I am still having the damndest time trying to implement all of the methods mentioned to me. The closest I've come is with tepples' method, like so:
Code: |
bool onIntros = true;
while(onIntros) {
int button = *BUTTONS ^ 0x03ff;
int down = button & ~previousButton; //pressed since last loop
int up = previousButton & ~button; //released since last loop
if(up == A_BUTTON) {
drawMode3Background(videoBuffer, textIntro2_Bitmap);
onIntros = false;
}
}
|
While that doesn't really work, i am assuming there is something wrong with that methodology.
Alot of people have been saying "each frame do this...," does that specifically mean that you're to check for a button press outside of a method I have defined above? am i to incorporate the waitforVBL function somehow?
Once again, I apologize to those who do this stuff in their sleep =)
Thanks again,
-Mike
#17563 - tepples - Wed Mar 10, 2004 7:58 am
darknet wrote: |
Alot of people have been saying "each frame do this...," does that specifically mean that you're to check for a button press outside of a method I have defined above? am i to incorporate the waitforVBL function somehow? |
Yes. You need a wait4vbl() somewhere in that loop. If you poll too much faster than three times a frame, you'll fall victim to the GBA's bouncy buttons.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#17570 - Cearn - Wed Mar 10, 2004 1:23 pm
darknet wrote: |
then when I want to check if a button has been pressed and released would i do the following?
if(key_pressed(A_KEY) && key_released(A_KEY) ) { do something... }
|
Uhm, no this is exactly what you shouldn't do.
key_pressed() gives a non-zero result if the key is going down in that frame, key_released() gives a non-zero result if the key is going up in that frame. Since the two are mutually exclusive it's no surprise nothing happens. Also, for a key to be released it has to be pressed at some point in the past in the first place. There's no need to check for that first. Just using key_released() should work just fine. That's basically what your last code snippet does too, only in a round-about way. If you change the "==" into an "&" that is.
Quote: |
Finally, is the initialization important (the KEY_ANY at the top)?
|
Only for the first frame. By the way, KEY_ANY is defined as 0x03ff. Basically if you want to see if any key is pressed you'd use
Code: |
if(key_pressed(KEY_ANY) != 0)
blah
|
Hmmm, now that I think of is, maybe the problem was that key_pressed()doesn't do what you expected it to do. So once again: it does not check whether the key is down at that time, only whether it's going down. Big difference. To check the current state I use
Code: |
u16 key_is_down(u16 key)
{ return ~key_curr & key; }
u16 key_is_up(u16 key)
{ return key_curr & key; }
|
#17576 - poslundc - Wed Mar 10, 2004 3:23 pm
tepples wrote: |
darknet wrote: | Alot of people have been saying "each frame do this...," does that specifically mean that you're to check for a button press outside of a method I have defined above? am i to incorporate the waitforVBL function somehow? |
Yes. You need a wait4vbl() somewhere in that loop. If you poll too much faster than three times a frame, you'll fall victim to the GBA's bouncy buttons. |
More on this subject:
You know those finite state machines we were talking about? Well this is now how you get around to implementing them.
Use a global variable to represent the state of your machine. Your machine only ever changes states once per frame. This means you should only ever check the status of the keys once per frame, and you only ever respond to user input once per frame. If you are looking for a sequence of button presses (eg. keydown followed by keyup) then you do it with a sequence of state changes, over as many frames as it takes.
So your program's main loop will typically look something like:
Code: |
while (1)
{
HandleInput(); // update state of the FSM ***once only***
WaitForVBlank();
DoVBlankStuff();
} |
As tepples mentioned, this is also how you debounce the keypad.
Dan.
#17580 - Paul Shirley - Wed Mar 10, 2004 3:56 pm
removed
Last edited by Paul Shirley on Sun Mar 28, 2004 8:59 pm; edited 1 time in total
#17581 - abilyk - Wed Mar 10, 2004 4:31 pm
Paul Shirley wrote: |
Its generally wrong to trigger actions on key releases in most situations, it feels wrong to users. We expect things to happen instantly on the press. |
In some cases, triggering on a key release is standard and expected. In most platformers, jump height is dependent on how long the button is held in. If the button is released before the max. height of the jump, the character will stop moving upward prematurely.
But in most cases, you're right, don't get me wrong. ;)
#17606 - Paul Shirley - Thu Mar 11, 2004 1:12 am
removed
Last edited by Paul Shirley on Sun Mar 28, 2004 8:59 pm; edited 1 time in total
#17633 - jma - Thu Mar 11, 2004 3:58 pm
The easiest way to do what you want is the way Dragon BASIC does it. A simple function to test if a button was just released.
For example:
Code: |
#define BUTTON_STATE ~(*REG_P1) // 0=released
u16 KeyUp(u16 *prev,u16 mask)
{
u16 now = BUTTON_STATE; // 0=released
u16 released = (*prev) ^ now; // 1=released
// update the current state
*prev = now;
return (released & mask);
} |
What this does is keep polling the player control (assuming you keep calling the function) to check for which buttons have recently been released. The mask is which buttons you care about, OR'd together. Imagine it in a routine:
Code: |
void DoTitleMenu()
{
u16 key_state = BUTTON_STATE; // initialize
u16 released = 0;
while((released = KeyUp(&key_state,D_PAD | KEY_A)) != KEY_A)
{
if (released == KEY_UP) // TODO:
if (released == KEY_DOWN) // TODO:
}
// TODO: 'A' was released, menu exited, make choice
} |
Bear in mind, I haven't been keeping up with this thread, so if this has already been said, kudos to whoever mentioned it first =). Likewise, as others have probably mentioned, it is best to wait for v-blanks between calls, etc.
P.S. The above code is untested. I always code in DB or ARM for the GBA, so the above may need some tweaking, but the concept is what is important here...
Jeff
_________________
massung@gmail.com
http://www.retrobyte.org
#17747 - delbogun - Sat Mar 13, 2004 8:58 pm
Code: |
int button;
void ButtonPress( void )
{
static int toggle = 0;
button = 0;
if( toggle && !( REG_INPUT&KEY ) )
{
button = 1;
toggle = 0;
}
if( REG_INPUT&KEY )
toggle = 1;
}
int main( void )
{
while( 1 )
{
ButtonPress();
if( button )
// do-stuff
}
}
|
Writing from my head, I think this is what I did. Probably clumsy, but it worked.
#17857 - darknet - Tue Mar 16, 2004 2:08 am
Well, after tons of trial and error I finally got what I wanted thanks to a mixture of techniques posted in this thread. Thanks for your input everyone (no pun intended!).
-Mike