Handling input properly in 2d brawler

Started by
3 comments, last by MattFF 11 years, 6 months ago
Hi guys!

I am currently in process of making a 2d side-scrolling fighting game. I'm using SFML for that purpose.

Problem i've ran into is handling input. ATM i'm passing states depending on key presses to the hero class, which then animates and moves main character properly. Display and move member functions are based on switch.

Problems i currently have:

1) It seems that sooner rather than later the switch will be overblown and hard to manage.
2) Animations are running only when key is pressed. I am not quite sure how to manage actions on key release. For example: When you run, then release, there should be a couple frames of animation of stopping, and returning to basic stance. I can't run the animation within the button press event with some kind of loop, because it executes instantly and jumps to last frame.
3)In general, i'm not sure where in the code should i place the input check. In the main loop? Or in some kind of interface manager? I imagine they shouldn't be in the hero class, it should recieve only states based on presses.

I would appreciate any help.
Advertisement
1, Yes and no.
If you don't do the logic in the switch, even there are a lot of cases, the code won't bloat.
Extract the logic in your cases to functions.

2, Not sure how other console game developers do, I only did some mobile games. We don't do any action/logic in system key events. Instead, we record the keyboard states and check them in the main loop.
You need more states than key up/down. You need,
Key is pressed -- the key has been pressed. So your hero can keep running.
Key is just pressed in current frame -- the key is just pressed. So if your hero is in idle state, change him to run.
The other two states for key released, similar as "pressed".

3, At the same time, only one object (or a manager) should care about key input. Just ask that object to process the key states. Don't mess your main loop up.

https://www.kbasm.com -- My personal website

https://github.com/wqking/eventpp  eventpp -- C++ library for event dispatcher and callback list

https://github.com/cpgf/cpgf  cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.

Make a state machine for "hero". In each state hero can play an animation, react to input or just "fade out" back to the idle state. You can have states which play loopable animations and stay in that state until some action (game event like enemy attack, user input, ...) throws them to another state, you can also have transition states, which wait until a finite animation ends and then jump to another state (run -> stoprun -> idle).

As for input, as mentioned above, use one place for all input detection. Record current input state (which keys/buttons are down, analog values, etc...) and compare with previous state(s). Based on that, your game logic can make a difference between "was pressed", "is pressed", "was released".
1) wqking is correct... you may end up with a lot of states, but that doesn't mean all your
logic should be inlined with the switch statement. Break each state into its own function, or
(if you want), create some high level states which each contain their own "sub-state" machines.


2) A button event (triggered down, held down, triggered up), should generally control state
transitions, not direct animations. I'll give you a quick example:

Lets say you're in an Idle state. When "PunchButton.Triggered" equals true, you'll call a
transition function, something like "BeginPunchState()", which will

  • change your state variable,
  • begin the punch animation, and
  • play any related sound effects.


Now that our state is changed, once per update the state machine will call UpdatePunchState(),
which will

  • check for hits against the enemy,
  • check for additional key triggers that could transition us to a combo state.
  • check for the end of the animation

When the punch animation is complete, UpdatePunchState will call ReturnToIdleState(), which sets
our state variable back to "Idle", and starts the looping Idle animation again.


3) Grab your physical inputs at the top of your main loop, then translate them to game-related
terminology and structures.

You want your state machine to handle things like "Punch" and "Kick", not "A" or "X". One
advantage of this approach (besides making user defined key mappings manageable), is that you
can have "Virtual" buttons that don't exist on the gamepad. Virtual buttons can be things like
interpreted accelerometer input (i.e. a quick shake means the "Leg Sweep" button has been pressed),
or you can do pattern matching on the input to generate combos without muddying up your other
states (i.e. "U+U+D+D+L+R+L+R + PUNCH + KICK" = KonamiFireBall.Triggered"

One mistake people sometimes make is to try and keep the input completely seperate from the
state machine. Collect and filter your input in one central location, but let your individual
states handle the abstract triggers they care about... Your state machine exists to translate
player input into character action, so don't get too meta with it.


Have fun!
Thanks a lot for those detailed answers!

After reading all this, i wanted to write it down in pseudocode/snippets to see if i understood it correctly.

In short, it would work like that:

0. Check the current state
1. Check for events that cause loss of control, or modify control ( knocback, stun, during a jump, combo etc. )
2. If there is loss of control/modification, change state
3. Check for input
4. Pass the state and the input to the hero class
5. Adjust the state accordingly - e.g. if character is being knocked back, ignore the input, otherwise take normal input and set state
6. Apply logic ( move, change hp )
7. Draw frames
8. Check for expiration of states - end of knockback, landing from a jump
9. If the state has not expired, keep the state, and adjust time remaining in that state, otherwise return to default state

Rinse and repeat.

Now i have question about code, specifically code/class separation. Just gonna paste small snippets.

main.cpp

int main()
{
while (window.isOpen())
{
while (window.pollEvent(event))
{
event_manager.handle_events;
input_manager.handle_input;
}
hero.apply_logic;
hero.draw;
}
}


input_manager.cpp

int handle_input
{
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Something))
input_state = x;

//repeat many times for all possible outcomes
return input_state;
}


hero.cpp


apply_logic(state)
{
switch (state)
case 1: //something that prohibits action - ignore input
move;
change_hp;
break;

case 2: // input allowed

switch(input_state)

case 1:
move;
break;

return final_state;
}

draw(final_state)
{
//draw proper frame, of proper animation, depending on state
}


Would that be the way to do it?


Edit: Buttons are hardcoded just for now, later buttons could be changed to virtual buttons, and base controls on .ini files, like sox suggested.

This topic is closed to new replies.

Advertisement