How to handle states in a fighting game?

Started by
22 comments, last by Daaark 11 years, 2 months ago
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.
Advertisement

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

No problem. smile.png I love beat em ups. If you have any questions go ahead and ask, and add some insight into the other beat em up thread. smile.png

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!

a WIP 2d game engine: https://code.google.com/p/modulusengine/

English is not my first language, so do feel free to correct me :)

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.

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!

a WIP 2d game engine: https://code.google.com/p/modulusengine/

English is not my first language, so do feel free to correct me :)

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.

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?

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

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.

This topic is closed to new replies.

Advertisement