Handling of Control Pad input too sensitive?

Ask anything your want about the 32X Mushroom programming.

Moderator: BigEvilCorporation

Post Reply
ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Handling of Control Pad input too sensitive?

Post by ammianus » Sun Aug 12, 2012 6:49 pm

So after I worked on the different performance enhancements, one other thing I ran into was that the "pause" feature I had in my game when you press start is way over sensitive to the point it is hard to pause with out many attempts. If you just press the key normally, it almost immediately pauses then quickly un-pauses. For normal character movement its not a problem since you usually just continue in one direction, but I could imagine other functions, like attacks might need to be more precise. I've borrowed this code from ChillyWilly's examples I think :)

What is the best way to control this generically?

Here is a sanitized version of my handleInput() function that is called every iteration of the main game loop. (do you not call each time if the game loop is very fast?)

Code: Select all

void handle_input(GameCharacter* player)
{
	unsigned short new_buttons, curr_buttons;
	unsigned short buttons = 0;
	
	// MARS_SYS_COMM10 holds the current button values: - - - - M X Y Z S A C B R L D U
	curr_buttons = MARS_SYS_COMM8;
	if ((curr_buttons & SEGA_CTRL_TYPE) == SEGA_CTRL_NONE)
		curr_buttons = MARS_SYS_COMM10; // if no pad 1, try using pad 2
		
	// set if button changed
	new_buttons = curr_buttons ^ buttons; 
	buttons = curr_buttons;
	
	//pause
	if (new_buttons & SEGA_CTRL_START )
	{
		if(paused != 1){
			paused = 1;
		}else{
			paused = 0;
			//clear pause and debug messages
			HwMdClearScreen ();
		}
	}
	
	
	if(paused == 0)
	{
		if (new_buttons & SEGA_CTRL_B && (player->action != CHARACTER_ATTACK1))
		{

		}
		
		if (buttons & SEGA_CTRL_LEFT && (player->action != CHARACTER_ATTACK1))
		{

		}
		
		if (buttons & SEGA_CTRL_RIGHT && (player->action != CHARACTER_ATTACK1))
		{

		}
	}
	
}

sega16
Very interested
Posts: 251
Joined: Sat Jan 29, 2011 3:16 pm
Location: U.S.A.

Post by sega16 » Mon Aug 13, 2012 3:12 am

What I do for my program is wait for the user to press AND RELEASE the start button then I wait for them to press start again after they release start then unpause the game
So check start
if user pressed start paused the game then wait for start to be NOT pressed
after that wait for the user to press start again
then wait for user to release the start button then unpause game

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Mon Aug 13, 2012 10:26 am

When you call controller reads more than one per frame you'll get problems like that.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Mon Aug 13, 2012 8:35 pm

Why just don't use the Joystick event handler present in sgdk ?
This way you can reply to specific even as press or START release.

Example :

Code: Select all

JOY_setEventHandler(joyEvent);

...

static void joyEvent(u16 joy, u16 changed, u16 state)
{
    // START button state changed
    if (changed & BUTTON_START)
    {
        // START button pressed ?
        if (state & BUTTON_START)
        {
            // toggle pause
            if (paused) paused = 0;
            else paused = 1;
        }
    }

    if (changed & state & BUTTON_A)
    {
        // A button pressed ?
        if (state & BUTTON_A)
            ....
    }
}

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sun Aug 26, 2012 12:45 pm

Stef wrote:Why just don't use the Joystick event handler present in sgdk ?
This way you can reply to specific even as press or START release.

Example :

Code: Select all

JOY_setEventHandler(joyEvent);

...

static void joyEvent(u16 joy, u16 changed, u16 state)
{
    // START button state changed
    if (changed & BUTTON_START)
    {
        // START button pressed ?
        if (state & BUTTON_START)
        {
            // toggle pause
            if (paused) paused = 0;
            else paused = 1;
        }
    }

    if (changed & state & BUTTON_A)
    {
        // A button pressed ?
        if (state & BUTTON_A)
            ....
    }
}
I am not using SGDK since this is for 32x development. Is it possible to use this part in a 32x program?

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Aug 26, 2012 6:32 pm

A couple things about 32X projects - most important: keep the 68000 running in Work RAM, not the ROM!! It's vital to speed... the more often the 68000 accesses the cart, the slower the SH2 will go since they have to wait on the 68000 before they can access the rom.

If you look at any of my 32X example code, I copy code to work ram for the 68000. That code looks for values stored in the 32X communication registers for things the 32X wants the MD side to do for it. So the 68000 is mainly looping over code in the ram that checks the 32X comm registers - none of which slows the SH2s.

Second, the 68000 code reads the controllers once each vertical blank (in the vblank int), and stores the values to certain communication registers. So if the 32X wants to see what the current controller value is, it merely looks at the comm register. This means the controllers get read at a nice set rate that isn't too fast, and the 32X side has an easy way to see the results.

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sat Sep 01, 2012 11:49 am

Thanks for the explanation, I see now that the MD is driving reading the input and stores the buttons in MARS_SYS_COMM10.
Chilly Willy wrote:A couple things about 32X projects - most important: keep the 68000 running in Work RAM, not the ROM!! It's vital to speed... the more often the 68000 accesses the cart, the slower the SH2 will go since they have to wait on the 68000 before they can access the rom.

If you look at any of my 32X example code, I copy code to work ram for the 68000. That code looks for values stored in the 32X communication registers for things the 32X wants the MD side to do for it. So the 68000 is mainly looping over code in the ram that checks the 32X comm registers - none of which slows the SH2s.
Right. I am using your 68k code in my demo :)

Code: Select all

| Copy 68000 main loop to Work RAM to keep contention for the ROM with
| SH2s to a minimum.
        lea     __m68k_start(pc),a0
        lea     0x00FF1000,a1
        move.w  #__m68k_end-__m68k_start-1,d0
Second, the 68000 code reads the controllers once each vertical blank (in the vblank int), and stores the values to certain communication registers. So if the 32X wants to see what the current controller value is, it merely looks at the comm register. This means the controllers get read at a nice set rate that isn't too fast, and the 32X side has an easy way to see the results.
I think you may be misunderstanding my question. It's not that the 32x code is slowing down, its that the apparent reading of the input is happening way too fast. So the slightest press of start button key is read as multiple presses of that button.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Sep 01, 2012 8:09 pm

ammianus wrote:I think you may be misunderstanding my question. It's not that the 32x code is slowing down, its that the apparent reading of the input is happening way too fast. So the slightest press of start button key is read as multiple presses of that button.
Ah - that comes back to Stef's suggestions... and general usage of controller values regardless of the platform. Let's look at some basics.

Most platforms give you joypad inputs as a field of positive values - each bit is SET when a button is pressed. In the case of my sample code for the 32X, each word looks like this:

type (upper 4 bits):buttons (lower 12 bits)

The type is either 0xF if no controller is present, 1 if a six button pad is present, or 0 if a three button pad is present. The buttons are SET to 1 if the corresponding button is pressed, and consist of:

(0 0 0 1 M X Y Z S A C B R L D U) or (0 0 0 0 0 0 0 0 S A C B R L D U)

Notice how the return value for a six button pad has 0 0 0 1 for the top four bits? That's that type field I mentioned.

Now that we know what's stored in the comm register, how do we use it? The simplest way is probably what you are doing - just looking if a bit is set. All that tells us is if the button is pressed or not. Good for some things, but "too fast" for others.

To see which buttons have changed, you need to exclusive-or the buttons with the last value you read... keep one variable called something like prev_buttons, and a new variable called buttons, like this

Code: Select all

    unsigned short prev_buttons = 0, buttons, changed;
Notice how I preset the previous value to 0. Now when you read the buttons, you find the ones that changed this way:

Code: Select all

    buttons = COMM8; // pad(0)
    changed = (buttons & 0x0FFF) ^ prev_buttons;
    prev_buttons = buttons & 0x0FFF;
Notice how we strip the type field - we don't want the pad type messing with our consideration of the buttons. At the end of the code, prev_buttons hold just the buttons currently read, and changed is just the buttons that have changed (either just pressed or just released). So now if we want to do something if a button was just pressed, we do this:

Code: Select all

    if ((changed & SEGA_CTRL_A) && (buttons & SEGA_CTRL_A))
The first condition see if A changed, and the second sees if it changed by being pressed. Only if BOTH are true do we pass the if. To look for a button just released, do this:

Code: Select all

    if ((changed & SEGA_CTRL_A) && !(buttons & SEGA_CTRL_A))
That looks to see if A changed, but the second condition now checks if it changed by NOT being set, i.e., just released.

If you do things when a button is pressed, or released, you do it ONCE for each time the button is pressed or released instead of every time through the loop. Sometimes you want to do something every time, but many times you just want to do things once.

One caution - notice how above I don't strip buttons just to look at it. Let's say we wish to wait until ANY button is pressed - you might try this:

Code: Select all

    while (!buttons) buttons = COMM8;
and you would be wrong! There's that type field... if you have a six button pad, buttons will ALWAYS be 0x1000 when not pressing buttons, so the wait will NOT wait. That only works right for 3 button pads. To wait for any button, do this instead:

Code: Select all

    while (!(buttons & 0x0FFF)) buttons = COMM8;
Strip that type field!

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sun Sep 02, 2012 12:26 pm

Great, thanks for the explanation. You should write a 32X tutorial with all of these tips.

So I defined a prev_buttons as a global variable, since my input handling is all done in a function, and I need to preserve that state somewhere outside of the function.

Code: Select all

	// MARS_SYS_COMM10 holds the current button values: - - - - M X Y Z S A C B R L D U
	curr_buttons = MARS_SYS_COMM8;
	if ((curr_buttons & SEGA_CTRL_TYPE) == SEGA_CTRL_NONE)
		curr_buttons = MARS_SYS_COMM10; // if no pad 1, try using pad 2
		
	// set if button changed
    new_buttons = (curr_buttons & 0x0FFF) ^ prev_buttons;
    prev_buttons = curr_buttons & 0x0FFF;
So for my start button problem I solved using the technique to check if it had just been pressed

Code: Select all

	//pause when start is pressed
	if ((new_buttons & SEGA_CTRL_START) && (curr_buttons & SEGA_CTRL_START) )
	{
The only other scenario, for my other button events, like Move left or right, or attacking using the B button, I need to know if the button is pressed and held, so I just checked against the current buttons

Code: Select all

		//if B is pressed and held
		if (curr_buttons & SEGA_CTRL_B && (player->action != CHARACTER_ATTACK1))
		{

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Sep 02, 2012 4:55 pm

Yes, that looks like you have things straight now. :D

A lot of this stuff are things we picked up over time in a number of places and never thought about since. We "just know" them, so we often don't explain them unless asked specifically.

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sat Sep 15, 2012 4:09 pm

Chilly Willy wrote:Yes, that looks like you have things straight now. :D

A lot of this stuff are things we picked up over time in a number of places and never thought about since. We "just know" them, so we often don't explain them unless asked specifically.
You know it would great for someone new if we had an overall tutorial with these various tips and tricks, just the sort of "here's how you do X" type tutorials in some basic way, like a wikibook. Maybe an enhanced sample program.

For example, my next questions will be around how to do sound effects and music?

I'd like to contribute my experience learning this stuff somehow.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Sep 15, 2012 10:59 pm

At the moment, most of the 32X "tutorials" are basically "look at the stuff I've written and do something similar." :lol:

My Yeti3D demo shows how to do "low-res" graphics on the 32X, MOD music playing, and sound effects. I'm just about ready to release an XM player for the 32X. I've been working on it for Wolf3D and Doom. In trying to make it easier to play music, I've also made it easier to play any sound. I'll probably post the library and example player code some time this week.

Except for Wolf32X, which uses a polled mode of setting the audio, I tend to use a DMA double buffer for audio. It makes it MUCH easier to play audio when you have (for example) 441 sample periods to update your sound rather than 2 or 3 (the FIFO depth in polled mode). Again, if you look at my Yeti3D demo, you'll see how I use the slave sh2 to dma the audio buffer, handle the music, and mix all the sounds.

EDIT: Here's a few binaries of music to tide you over until I release all the code. ;) :D

XMPlayer-1.0-binaries.7z

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sun Sep 16, 2012 3:22 pm

Those were very cool. D_E1M1 was always one of the great game tracks. :D

I would be willing to release the source for my graphics functions for drawing 2d sprites. They are very generic, and decently robust, plus they work thanks to yours and TapamN contributions to the assembly functions that made it actually perform.

Would it make sense to start a developers kit, or just a 32x utils library as an open source project? I don't know how others feel about it sharing their hard written code :)

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Sep 16, 2012 5:12 pm

Some folks are more willing than others. I always contribute everything I can. I don't think we have enough to make a 32X SDK like Stef's SGDK for the MD, but maybe organizing what we DO have to be easier for folks to find and use would be helpful. Maybe have a thread or wiki dedicated to each thing we have contributed with code and an explanation... controllers, sound, video. That sort of thing.

Post Reply