Sign in to follow this  
tashaklikedi

How to handle states in a fighting game?

Recommended Posts

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?

 

Share this post


Link to post
Share on other sites

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..

Share this post


Link to post
Share on other sites

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 ^^

Edited by tashaklikedi

Share this post


Link to post
Share on other sites
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;
		}

	}

}

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
Inputs is a matter of opinion.

In my main update loop, I would only first check global inputs, like for pause or anything that affects the game as a whole, then I would do specific checks on a per state basis.

STATE_DAZED:
Check for ANY possible input, and use it to reduce the daze timer.
STATE_IDLE or STATE_WALKING
Check for anything and respond. Like movement keys, attack, jump, whatever.
STATE_DASHING:
Check for anything and respond, but the responses are different than above.
STATE_ATTACKING:
Player is busy is attacking
STATE_HIT:
No valid input in this state, player is reacting to being hit.
etc...

I find that cleaner and straight forward than other methods. But that is a matter of opinion and organization. I find checking input and then later trying to match it up to a correct state to be backwards thinking. Why do the logic up front only do find out later down the line that you aren't even in the correct context?

Question 2:

As for the attack stuff. I kept my description very high level. I'll try to answer it better

You have a character object, and he references a sprite sheet, such as: http://soronline.net/sprites/sor3/Axel(sor3).png

So now you have a list of animations for this character. An animation is a class that has some data that describes it, plus a list of frame structures...

PSEUDO CODE
class animation
{
	string Name;

	ANIMATION_TYPE Type;

	//with enums
	ANIMATION_TYPE_WALKING
	ANIMATION_TYPE_RUNNING
	ANIMATION_TYPE_ATTACKING
	etc...

	//a list of frames
	vector <Frame> Frames;

}


class Frame
{

	bool isContact;	//contact frame?
        someType delay; //delay until the next frame, in whatever measurement form you are using

	//coords of the frame on the sprite sheet
	int x,y;
	int height,width;

	//coords of the hitbox
	int hitbox_x,hitbox_y;
	int hitbox_width,hitbox_height;

	//coords of the contact hitbox, if applicable, on contact frame only
	int contact_x,contact_y;
	int contact_width,contact_height;
	
}
The logic in your update function will deal with the attack. The SetState function example was just code to handle state switching cleanly.

Share this post


Link to post
Share on other sites

I like the frame class idea^^ 

Thanks for the long replies and for your effort! I'll try to implement all these things (gonna take me a lot of time since I think I'm gonna reprogram the game from scratch) and let  you know if I succeed^^

Edited by tashaklikedi

Share this post


Link to post
Share on other sites

Actually, I have a question :)

 

What about the whole "state machines are bad and lead to huge switch cases" argument? This kind of structure seems prone to the exact same argument dosen't it? if you want to add more abilities / combos, won't they just become branches in your switch case?

 

So, is there a better way to structure it that won't evolve into a thousand lines of code? (this happened to me, not kidding) :D

 

Thanks! 

Share this post


Link to post
Share on other sites
Bollµ, state machines are not bad, especially when they map perfectly to the problem you are solving. Saying state machines are bad makes about as much sense as saying that comments slow down code. smile.png

So, is there a better way to structure it that won't evolve into a thousand lines of code? (this happened to me, not kidding)

If you have a giant mess of a switch statement, it looks like you started the programming before the design was finalized. Design your program first (possibly on a big white board), and then figure out what code is needed to make that design work.

I've mentioned a few times in this topic not to introduce new states needlessly, and to let the data handle everything. Comboing isn't a state. An attack is an attack. Only one state is needed. The data in the animation / attack table will tell the inform the code what to do. The one section of code should be as general purpose as possible. It doesn't know or care anything about the attack. It just does whatever the data tells it.

Once you know what an attack is (informed by your design and ruleset), you can make a general purpose state that handles every possible attack cleanly.

If you press the attack button, and you are in a state where pressing the attack button will initialize an attack then you go through a checklist.

What is current state? Determine which animation to play.

AM I CURRENTLY IN THE AIR? -> SetActiveAnimation(ATTACK_JUMPATTACK);
AM I CURRENTLY DASHING? -> SetActiveAnimation(ATTACK_DASHINGATTACK);
AM I HOLDING A WEAPON? -> WHICH? -> SetActiveAnimation(ATTACK_SPECIFICWEAPONBATTACK);
AM I IDLE? ->
------------>DID I JUST ATTACK, AND, AM I WITHIN THE GRACE PERIOD FOR COMBOING?
--------------> AND DOES MY CURRENT ATTACK CHAIN INTO ANYTHING?
----------------->SetActiveAnimation(ATTACK_WHATEVER);

SetState(STATE_ATTACKING, whatever else);

So now you are in STATE_ATTACKING which has it's own update code path. It doesn't matter what attack is happening, it's all handled by the data.

WHAT FRAME AM I ON NOW? -> DID ENOUGH TIME PASS TO ADVANCE TO THE NEXT FRAME?->SetAnimationFrame(x);
---->FRAME ADVANCED? -> WAS THAT THE LAST FRAME? -> SetState(STATE_IDLE);
IS THIS A CONTACT FRAME? -> YES? -> (Whichever part of your program that does attack collision checks will be notified).

It gets a bit more complicated, but that's the gist of it. Your code is only concerned with the fact that it is playing an attack animation, and running the proper logic on contact frames. It doesn't know or care what attack is happening. It just takes in data, and processes it per the rules of the game.

Share this post


Link to post
Share on other sites

Well, that does make sense :D

 

And yes, you're right. I'd considered each ability to be a state and then went on adding new states to it. I refactored it afterwards by storing the data in XML (what buffs, what frames etc) in quite a similar fashion as you described :)

 

Thanks!

Share this post


Link to post
Share on other sites
Also, don't forget, those states are also very useful when doing your AI.
if (Target == null)
{
    Character *PotentialTarget = GetNextTarget();
    CharacterState = PotentialTarget->GetState();

    switch(CharacterState)
    {
        //..
    }

}
Then you switch through CharacterState and decide the course of action to take. When you do acquire a target you will have another switch based on he state that will decide the proper course of action.

Is the target idle? Move in for the attack.

Is the target attacking? Create distance, if no distance, possibly block, or use whatever attack can override theirs. Or do nothing if the attack is the opposite direction.

Is the target reacting to a hit? Don't attack just yet. Possibly play animation to taunt or cheer on the attacker.

Is target dazed? TAUNT or use big finishing attack.

You can mix this with personality type variables to make complex gameplay from just that set of simple states.

Share this post


Link to post
Share on other sites

Hello again, after all this discussion, I thought about how I can implement a combo system that could be unique for each character. I came up with something like this:

 

//PSEUDOCODE:


class attack:
{
	enum type   
	enum stance //the game will have a lot of stances, this includes running while grabbing a guy, or different weapon styles, figting styles etc..

	std::string name //each attack should have a name for the sake of clarity
	int ID           //to be inserted in a vector

	unsigned int animationID  //which animation will be played?
	
	std::vector<std::string> vCanComboTo //to which moves it can be comboed to?
	
	gameInput inputList[5]  /*what input will be querried to the move to be executed? 
this will be read from a buffer of enums, which will go like below.*/
}

//the gameInput struct covers all kinds of commands that can be input to the game.

struct gameInput:
{
	enum button
	enum direction
	bool bModifier
}

/*the button enum reads from a secondary buffer, that only records the 3 attack buttons
and combinations like "HCF" which means half circle forward 
( a street fighter style input)*/

enum button
{
	punch
	kick
	grab
	HCF
	HCB
	QCF
	QCB
}
 
 

what do you think?

Edited by tashaklikedi

Share this post


Link to post
Share on other sites

I forgot direction and type enums:

/* the direction enum reads from another secondary buffer which is relative to the direction the user's facing.
i.e. left = forward if the user is facing left*/
enum direction
{
    up
    down
    forward
    back
}
//this is simply to know what kind of attack it is enum type: { hit grab projectile etc... }


enum type:
{
	hit
	grab
	projectile
	etc...
}

Share this post


Link to post
Share on other sites

what do you think?

Looks like you got organized. Just as long as you knew what all the possible combinations were before you invented all those data structures, you should be on the right path.

IMO, the next step is probably to go borrow some streets of rage, or final fight sprite sheets, and to test it out. Just try to get someone walking around a level and responding to input.

Share this post


Link to post
Share on other sites

IMO, the next step is probably to go borrow some streets of rage, or final fight sprite sheets, and to test it out. Just try to get someone walking around a level and responding to input.

 

I have my own sprite sheets ^^ I do the art myself for now. I'll transform the ideas above to code and tell you guys if it works^^

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this