Key Combinations for fighting game?

I’m trying to see if i can create a fighting game à la Street Fighter, Mortal Kombat, King of Fighters etc.
How do you trigger actions to perform special moves like fireball (down-downforward-forward + punch), dragon punch (forward-down-downforward + punch) or sonic boom (hold back for 2 seconds then forward + punch) etc.
I’d like it to work for both keyboard and joystick controller. I think python is the (only) way to go but don’t know where to start.
A tutorial and example .blend file would be really appreciated. Thanks in advance:D!

well one limitation - most keyboards only register so many keypresses at a time,

joysticks usually register them all,

so what you will need,

define sensors for each thing you will be observing

Keyboard A------------python

then in python

ComboList=own['ComboList']
A= cont.sensors['A']

if A.positive and own['ComboList'][0]="whatever" and own['ComboList'][1]="whatever2":
    attack="Attack x"
    own['ComboList']+=["Attack x"]

but much much much more complicated

@@BluePrintRandom: could you give me a concrete example, say fireball (down-downforward-forward + punch)?

import bge

cont= bge.logic.getCurrentcontroller()

own= cont.owner

Down= cont.sensors['Down']
Right= cont.sensors['Right']




if 'ComboList' not in own:
    own['ComboList']=[]
    listS = [Down,Right],etc]
else:
      
       for sensor1 in listS:
             if sensor.positive:
                   move =[sensor]
                   for sensor in listS:
                       if sensor.positive:
                            if sensor!=sensor1:     
                            move+[sensor]
              return
           
          
             
          

edit - thought of a easier way to do it

move will = [first detected sensor +any others]

however to filter you will need to have

if ‘Down’ in move and ‘Right’ in move and ‘A’ in move:

if move[0] ==Down:

making a demo, it may be a minute,

Well, actually this is a hard one!
I’ve done recently a combo system like that in my game,it took me 1 day developing and 3 days speaking with pro programmers about the structure.
It is not easy, do not delude your self.

What you need to consider before starting to think about doing it (I am assuming you will use keys, that are used for walking,running, hitting…etc, if no combo is in progress):

  1. What should be the max delay between actions(key pressed for combo execution)
  2. What if I hold down a key(by chance) and then press another key in the combo key list.
  3. What if I press a key and the hold down the second one(by chance) and then press the third one.
  4. What if I want to do “left-left” combo and “left-left-click” combo, how to distinguish that I want to do the second one and not the first.
  5. How to measure the time between key pressing.
    This is a small fragment of what problems you will encounter.

If you do it right, at the end you’ll just need to add something like this:

g.playerProps['Combos']['Fury Of The Gods']=['run','jump','hit']

No additional code required and the combo is up and running.No need to define key sensors or anything.

In two words, my combo structure is this:

  1. Initializing combo dicitionary:
g.playerProps['Combos']={}
g.playerProps['Combos']['Fury Of The Gods']={}
...
...

  1. Getting every key that is pressed and putting it in ComboCheckList with two elements: (key sensor,TimeStamp), then putting it in another list - CheckListIfKeyHold, then manage those lists, so there is only one key of this kind present in the first list, even if you are holding a key down.
def getKeys(sensor):
....
...
getKeys('walk')
getKeys('run')
...
...

  1. This is where the magic happens - on each frame you check the ComboCheckList for combo key sequence by comparing fragments of key sequence and their time stamps in ComboCheckList.
    If there is a mach, then you clear the ComboCheckList and execute the combo, if there is no mach and ComboCheckList length becomes higher than some number(for me it is 5 elements), then clear the ComboCheckList again, because you don’t need a list with thousands of elements in a 10 minutes gameplay.

  2. You have to manage combos and normal character controlling, by delaying on purpose normal controlling, because on each key press you need to know if there is a combo in progress.

I know it is hard to do/understand, I’ll make an example, when I have time.

here is a example :smiley:

right now it only prints the “move”

a list of “moves” can be used to trigger a behavior etc.

this is missing the list generator, and the move filter,

Attachments

SensorGetter.blend (417 KB)

@@haidme: Wow, that’s a lot to take in, I’m really looking forward to your example! In the meantime I’ve just found a tutorial by Goran Milovanovic on youtube called “Blender Game Engine Tutorial: Key Combo” => https://www.youtube.com/watch?v=NlpuYLmH_Qg
I’ll study that tutorial and look for other tutorials and resources, hopefully this thread will result in some kind of template because key combos aren’t just apllicable to fighting games but all sorts of games.:stuck_out_tongue:

this is getting there, but still needs the list implementation redone (it needs to add the move to the front of the list)

Ok, this is all working except my filter, which we can fix probably, I just need to think for a second…

ok - it’s working :smiley:

still needs a little work

I <3 coding

Down - Down Right - Right = FireBall

S = Down

D = Right

Attachments

SensorGetter.blend (419 KB)SensorGetterBetter.blend (420 KB)Working.blend (419 KB)

@@BluePrintRandom: I’m having a problem with both “SensorGetterBetter.blend” and “Working.blend”. firstly if I open any of those blender files it opens a second instance of Blender (I have never seen this behaviour in Blender before).
Secondly “SensorGetterBetter.blend” gives error when i press “P”:

IndexError: list index out of range
Python script error - object 'Cube', controller 'Python':
Traceback (most recent call last):
  File "gamelogic_simple.py", line 48, in &lt;module&gt;
  File "gamelogic_simple.py", line 40, in main

Thirdly “Working.blend” gives a constant stream of prints in the system console, I don’t know if that is how it’s supposed to work.

press

S , SD, D - launches fireball

much easier to do on a controller I bet though



import bge




def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner
    if own['MoveList']=="Empty":
        own['MoveList']=["blarg"]
    space = cont.sensors['space']
    Down = cont.sensors['Down']
    Right = cont.sensors['Right']
    ListS = [Down,Right,space]
    move=["Empty"]
    
    if own['timer']==0:
        for sensors in ListS:
            
            if sensors.positive:
                if move[0] == "Empty":
                    
                    move=[sensors.name]
                    
                else:
                    move+=[sensors.name]
                        
       
        if move[0]!="Empty":
            own['timer']=10
            print(move)
            own['MoveList']=[move]+own['MoveList']
            
                   
    else:
        if own['timer']&gt;=1:
            own['timer']-=1
            print(str(own['timer']))
    
    if own['MoveList'] == [['Right'],['Down','Right'],['Down']]:
        print("FireBall")
        own['MoveList']=["FireBall"]+own['MoveList']    
        added = bge.logic.getCurrentScene().addObject('FireBall',own,120)
        
    print(str(own['MoveList']))
    x=0
    for items in own['MoveList']:
        x+=1
            
    if x&gt;3:
        own['MoveList'].pop(3)
    own['Status']=str(own['MoveList'])                                          
main()




If you go to open the debug console and you accidentally click new window… It adds another window. This is nice because you can change the config of each window so one is like a file explorer or animation station etc,

Attachments

Working (withFireBall).blend (429 KB)

Requirement:

If you analyze your requirement you should notice these things:

A) sense several events
B) sense them in a sequential way

This means the timing is important.

Idea:
From my experience I think a state machine is the way to go (How to: use build-in states - a simple FSM tutorial).

It will really help you to manage the timing. It will be a pretty simple state machine as there is a really low number of transitions.
You can adjust it for any number of input events (keyboard, joystick, mouse) even mixed.

Example
Lets see how a state graph with the mentioned example would look like:


Basically you have three (semantic) types of states:
A) the start state “Waiting”. it just waits that the user starts a combo
B) the target state “Punching” that performs or triggers the result of the combo
C) a few intermediate states that describe the way how to come from A) to B)

The transitions are three semantic types too:
A) the “forward” transitions, describing how to successfully go to the next state
B) the “cancelling” transitions, when “breaking the chain”
C) the “completion” transition when the “success was celebrated” :wink:
I think you can discover by yourself what transition means what. If not let me know.

Where to build it in?
Now we have a plan, but what to do with it?
Think about following. If you have other combos you will quickly get an awful large number of states. If you put everything into a single state graph this graph is nearly unreadable. The BGE supports 30 states “only”.

So we need a way to keep it small and simple and still the option to add 1001 different combos.

How about that:

The Independent Combo Detector (TICD)
You have one object that deals with one combo. It knows how to detect input and it knows how to tell the combo is completed. This requires just some basic operations and can be implemented without Python.

First we create an empty and call it “TICD.punch” [= The Incredible Combo Detector to detect “punch”]. Others might be named TICD.fireball or TICD.super-duper-punch.

Then we need the states … you can read them from the above graph:


It does not really matter what state you use, as long as they are unique. I suggest you use the actuator name as state name. This way you know the target state of this actuator.
Important: Make sure the initial state is correctly set to “Waiting”.

Now you can add the (forward) transitions to each state. Again, you can take it from the above graph:



The upper part shows how it looks like when you look at a single state. The lower part shows how it looks like when you look at several states. Please note the different state numbers at the controllers.

You might have noticed that I just added the “good transition” = forward and completion transitions. Lets add the “cancelling” transitions with the next post …

… where we look at the timeout.
You did not mention it in your example but I guess you do not want a combo that last over the complete game. So we assume you want a 0.5 second timeout after that a step gets cancelled. The above state graph already contains this event.

So lets look how we can detect

Timeouts
A simple method is to use a timer property. You set it to zero to start the timer. Then you check if it exceeds your limit. As it counts with decimals you should check an interval.

So we need:

  • an always sensor sensing when entering the state (enable [Level]) -> set timer to zero
  • a property sensor sensing the timeout -> change state to “Waiting”

if you add this you might notice that the state machine immediately returns to “waiting”. Why is that?. It might be not obvious but the timer counts always … therefore when we entry this state, the timer mostlikely exceeded the timeout already before we had a chance to reset it.

How to ensure the timer gets reset before we start the evaluation? There are several approaches. A simple one is to use a delay sensor AND with the timeout sensor.
It would look like this:


So every time the next event is later than 0.5 seconds the combo can’t be completed and starts again.

Tip: When the timeout is always the same you can separate the timeout logic into a separate state and combine this state with all intermediate states (see the dirty demo).

But we have another “cancelling” transition:
pressing the wrong button.

This one is tricky. You can’t simply invert the sensor output. Because this means “the input you are looking for is not present”. I’m pretty sure you want to be able to release the button and press the next one without breaking the combo.
I guess you mean: “There is another input that is not what I’m looking for”. (e.g. down-down-up should break down-down-forward)

Unfortunately there is no sensor that senses “any input except this one”. So we dig through our bag of tricks. !Danger: Extremely dirty solution!

We sense for all inputs and hope the right input is handled as the last one overwriting out cancelling.

Luckily this works pretty straight forward. You need to make sure the state actuator for “Waiting” is above all other state actuators.

Then we can do this:


A clean solution is possible too. We can use the expression controller. But we need to rename some of our sensors as they do not run with spaces and special characters like < or >.



The expression should be good to understand: “anyInput and not down”
this solution does not rely on the order of actuators as it should be.

Now you need to establish these two “cancelling” transitions to all intermediate states.

Notice:

  • The “or” in the “cancelling” transition is not performed via OR controller but by connecting the same actuator to different controller. This is acts as an implicit OR.
  • You can set up different timeout intervals for each state. E.g. you make it harder with each step by reducing the timeout after each step (1 sec, 0.5 sec, 0.25 sec, 0.2 sec …)

With that you have nearly completed the state graph we wanted to have. But one is missing - How to perform the punch. Read about in the next post …

With the above steps we are able to measure a successful combo input. Now we want to get the “PUNCH”.

I guess you would go and add some actions, motions or whatever to state “Punching”. Yes this is possible, but look at the “mess” you already got. Do you really want this into your “character” or whatever logic? [now you are unsure, aren’t you?]

You know we already applied the combo to the TICD object (which is not part of the character at all).

All we need to do no is to let the TCID communicate with the object that performs the real “PUNCH”. The simplest way is … to send a message (not via post office, but via message actuator).

The implementation is extremely simple:


Your character can now (rather than listen to the keyboard) listen to the message “do punsh” and do whatever it needs to “PUNCH” the brain out of the enemy.

The cool thing is … you can add another TICD for other combos. They do a similar thing … transforming input to messages.

You had another requirement:
Several input devices
like keyboard and joystick.

No problem:
create a TICD for each of them, so you can do a combo on one device and the same combo at the other device at the same time.
Or
create on TICD that can deal with keyboard and joystick this way you can continue the combo with the other device (what a multiplayer concept ;)).

You can even decouple the TCID from input device by feeding it with messages. But I guess this is a little too advanced :wink:

Attachments

TheIncredibleComboDetector_Demo.blend (87.4 KB)TheIncredibleComboDetector_dirty_Demo.blend (93.2 KB)

@@Monster: Wow, thank you for that detailed explanation! I’m gonna do my homework on this subject all weekend and see how far it will get me. Also thanks for the example .blend files, they are really helpfull for reference of how key combos work.

edit: in your example file “TheIncredibleComboDetector_dirty_Demo.blend” my joystick doesn’t register the down hat key. I don’t know what the cause is but I created a new file and set some logic bricks up to move a cube and tested up/down hat keys of my controller and it worked so it’s probably not caused by my controller (btw an old chillstream xbox 360 controller). the last combo “flinch” which uses left/right hat keys does work with the controller.

this should do what you want, but with about 90% less complications…

check out the status read from your inputs now,

try rolling s,sd,d

I think you can make this work easier then states,

what it do,

if timer > 0 and keys are pressed, add all positive sensors as a entry, since you set the order of the list, you know what order they will read,

like

[‘Down’,‘Right’,‘Space’] - could be a single entry,

to filter for combos you simply do


if own['MoveList']=[['Down'],['Down'].['Right']]:
    do stuff

You should be able to add any sensor you want, and then add it ot the list it checks for positive sensors, (make note of the order of the list as the first positive sensor will be the first value of the move)

PopTimer = length of time before clearing the list if no key is pressed.

there is a
if x>5:
own[‘MoveList’].pop(4) at the bottom

change the value of (x>5) to anything, and then change the pop to that -1 (this sets max list length)

Attachments

FightingSystem_workingWell (withFireBall).blend (431 KB)

@@BluePrintRandom: Thanks but your examples are very… Unforgiving for now. I first tried “FightingSystem_workingWell (withFireBall).blend” without editing it (Down-DownForward-Forward) and I could perform “Fireball” approximatly 75% of the time performing with various speed in game mode. after I added “space” (Down-DownForward-Forward-Space) in the combo by editing line 56 to

   if own['MoveList'] == [['space'],['Right'],['Down','Right'],['Down']]:

I could perform the move only 10% of the time. Also why do i have to write the Movelist backward?
@@Monster: Could you post example of an instance where you need to hold 2 keys at the same time like “dragon punch” (down-DOWNFORWARD-forward-punch) and also “Sonic Boom” where you need to “hold Back for x number of seconds, then Forward, then Punch)”.

the list is backwards, so the most recent item is item[0]

this way as the list increases in length, you just remove the end,

try messing with the timer, as well as try setting up the gamepad (keyboards are not the easiest thing to perform street-fighter moves on)

also, see the value Poptimer?

you can make it longer, and it will be more forgiving,

these same issues you will face in any system

(timing - how long to wait before taking the next input)

(timing how fast the combo needs to be to be executed)

about holding a key,

make a property holdDown , or holdBack

keypressDown----------and-----------add 1 to holdDown

if keypress down inverted—python
if holdDown != 0---------------/

this way releasing the key will trigger setting [‘MoveList’] in another manner

you will need a method to trigger one script on tap, (holdDown<x) that is different then
(holdDown>=x)

also,

[[‘Right’,‘space’],[‘Down’,'Right],[‘Down’]]

I think is how it went in the game

@@BluePrintRandom: Should I change “PopTimer” value in line 20 and/or line 39 in “list2” in Text Editor? Also I don’t see “PopTimer” property in Logic Editor, does this property get created in the game at runtime?