How to handle states in a fighting game?

Started by
22 comments, last by Daaark 11 years, 1 month ago

Hi everyone, as a newbie programmer, I am trying to develop a 2D side scrolling Streets of Rage like fighting game with a lot of characters, moves, and animations. I started to learn programming 1 year ago, and I managed to have a game where I have two player controllable characters hit each other, jump to higher elevations in a streets of rage style map, etc...

However, since I am new to programming, and rushed enthusiastically into programming the game without any plan, my code got really messy.

My problem is how to handle character states ( or moves ) , and their animation...

How I proceeded is: I have a huge switch function with states, another one with animations, another one with hitboxes for example:



//this is the base punch for the ninja character.
// this is the state switch function
case NINJA_BASEPUNCH:
		falsify(); // set everything to false (i.e. he cant jump while punch animation is on)
		bCanBasePunch = true; 
		enumAnimState = NINJA_AS_BASEPUNCH; //animation is set to base punch animation
		enumHitMessage = HIT_LIGHT_HI; //this will tell the enemy entity the damage type ( light ) and its position ( high )
		basePunch(); //punch function 
		break;

//once this is executed, the program will set the animation settings
//this is from animation switch function

case NINJA_AS_BASEPUNCH:
		sprite.w = 36;
		sprite.h = 36;
		iCurrentFrameCol = (bFaceRight ? 13:14);
		iCurrentFrameRow = 0;
		animation.maxFrames = 2;
		animation.setFrameRate(animation.getCurrentFrame() > 0 ? 100:60);
		break;
		}
//this is from the hitbox update function. 
case NINJA_AS_PUNCH:
		hitbox.w = groundPos.w;
		hitbox.h = 35;


All this is only for a simple basic punch. Now imagine how would it be if there was a big move list, big combos, stunts, etc... I need to get a better system for it, anyone who can think of a better implementation system?

Advertisement
This stuff should be data driven, and you really only need one attack state. The data for each attack will handle the rest. You should know which frame is the attacking frame, and have the hitbox for it on the part that is doing the attacking. Like a box around the fist on the frame where it would connect.

I don't know how to do that (I'll learn tho, I'm sure there is documentation about how its done), but each attack does not work the same way, especially input wise, some attack will combo with some, while they wont combo with others etc.. it sounds like a great solution tho..

I'll use Streets of Rage 3 logic and the main character Axel as an example for everything below.

First, you'll need an input array that can hold up to whatever the maximum amount of inputs for a move is. In streets of rage, this is 3.

Every time the user presses a button, you check against previous inputs to see if matches any specific patterns. So Up,Up or Down,Down cause Axel to roll up or down. Forward, forward or back, back, cause him to dash. Forward,Forward,Attack causes the Bare Knuckle Uppercut.

So you need an array of inputs 3 entries long, and a SetState(STATE s, STATEINFO si) function.

STATE_IDLE
STATE_ROLLING
STATE_DASHING
STATE_JUMPING
STATE_ATTACK
STATE_HIT
STATE_KNOCKBACK
STATE_DAZED

Each state will have it's own input code and logic. The logic and input for STATE_DAZED will be very different than STATE_IDLE.

Any keypress in STATE_DAZED just goes towards decreasing the dazed timer.

STATE_HIT doesn't need to concern itself with input, because the player is just reacting to getting hit. The only important information would come from STATEINFO_HITHIGH, STATEINFO_HITMED, STATE_INFOHITLOW, STATEINFO_HITKNOCKBACK, etc to know which animation to play, and then when it's down, you go back to STATE_IDLE. I would probably actually make STATEINFO a struct and include a ton of information in there for any state.

Press the attack button while in STATE_IDLE, STATE_DASHING, or STATE_JUMPING will change state to STATE_ATTACK, and then you will play the relevant attack animation. Unless you have special situational rules, you shouldn't need to concern yourself with STATE_DASHINGATTACK, or STATE_JUMPINGATTACK.

You will also have an array of animation frame data which will be specified in a data file somewhere.

All your attack data will come from here while in STATE_ATTACK. You will need to know
*-the number of frames, and their coordinates on the sprite sheet
*-which frame(s) are the contact fames, and their hitboxes
*-which animation is next in the chain (0 for no combo ability (used on jump kick, dashing attack, and the last move in any combo)
*-frame delays, so each frame is played with the proper time delay

With this system, if the user presses attack button during the attack, or is idle but pressed attack within the allowable grace period, you automatically know if you can combo based on the current or last animation.

In Streets of Rage 3, attack 3 times causes something like left punch, right punch, uppercut.

The left punch is the basic attack. It's frame data would indicate that it combos into the right punch.
The right punch would frame data would indicate that it combos into the uppercut.
The uppercut frame data would indicate that it combos into nothing. So you return to STATE_IDLE.

It's all in the data set. Your code for the attack will read in the data and be able to handle every possible attack with it's general purpose code. Even if you want to want to have people tossing projectiles, you will have data that specifies this, and the contact frame is just the frame where you spawn a projectile (which should just need type, direction, owner data).

That's just an example off the top of my head. Your rules will be different, and it will get a bit more complex, but hopefully that helps you understand a way to get it done. Also, why not check out this thread I'm in that analyzes these types of games? http://www.gamedev.net/topic/637975-what-makes-a-good-beatem-up-game/

Hey, that was a great reply and will be very helpful!! I think I grasped the logic, now I got to apply it to my system. thanks!

I'm also gonna look at that thread

EDIT: Wow that was a great post!! the game I'm working on is actually directly influenced by River City Ransom (with unique characters and moves, the RPG elements, free world, etc). I was thinking of a mario style map like Scott Pilgrim, which looks also great.

Well I am a noob programmer, and I have a LOT to learn... But its so fun ^^

I forgot STATE_WALKING in the list above. That's a big oversight! smile.png

I put SetState() as a function because you shouldn't be setting all those random variables all over the place like that. Everything should be single responsibility, and as clean and simple as possible.
STATE StateCurrent;
STATE StatePrevious;


void SetState (STATE s, STATEINFO si)
{
	//buffer the current state into a previous one, just incase
	//you ever want to know what the character was doing right before now
	//you have that information easily available
	StatePrevious = StateCurrent;

	//now use a switch statement to setup the state s
	switch(s)
	{
		//character got hit
		case STATE_HIT:
		{
			StateCurrent = STATE_HIT;

			//use the info in si to figure out what to do
			//if the attack was high, medium, or low, set
			//the proper reaction animation to current
			//IMAGINARY CODE HERE


			//use other info in si about how strong the attack
			//was or whatever to determine knockback distance
			//and animation speed to simulate weight and impact
			//IMAGINARY CODE HERE			
				

			break;
		}

	}

}

I already have a function like this but I assigned a state to each hit type ^^

but again there will be a lot of hit styles ( different animation for strong or light hits, a lot of death animations given the weapon / move)... Should I do it also with data?

Oh and by the way is it better to open and close the data file each time, or to open it once and close it when the program ends?

Open the data file, put it into an array of animation frame data structures, and close it.

Use as few states as possible. It doesn't matter what type of attack is it. Figure out what an attack is, describe a general purpose attack with a set of variables, and then use the data you have to supply the information.

Your characters should have an update function. The game will loop through all the characters and run their update function every frame. The update should just be a switch statement and it will play out whatever the current animation is in the current state, and then take whatever action needs to be taken on special frames.

You only need a new state when the code will be entirely different, and cause different things to happen. Otherwise, you will just have duplicate in all your states with 1 thing that is different. And every time you make a change, you will have to change it everywhere, or risk introducing subtle bugs, or unintended behaviour.

This is why I suggested that data structure called StateInfo. You can use it as a catch all data structure to describe specifics when you change your states. You make a new one to pass to the function, and it just contains a bunch of switches you can toggle.

And what about inputs?

Right now, my input function has control over states.

For example, when I press punch, it verifies the state I'm in, and if its allowed to do so, it transitions into a new state.

With your system I can think of a solution like this:

When I press an attack button (there are a punch, a kick, a grab, and an attack modifier button, and I plan on adding shoryuuken style special inputs), I change state to attack, And then logic in the attack case in the state function will deal with its type.

Am I getting it right?

This topic is closed to new replies.

Advertisement