0 votes

Hello people! I'm working on a fighting game just for learning purpose.
I'm trying to make a melee combat combo system, just like old Mortal Kombat games.

Been searching around and in generally, it's all about saving the last pressed keys, clear the buffer after some time and check if match any combo. It works well for combos performed after a specific input sequence, scorpion spear as an example:

Back, Back, Low Punch -> do_spear()

The problem is when it comes to more complex combos. I'll use scorpion as an example again. In addition to the special moves (like spear), we also have basic combos that are basically a sequence of attacks that will perform a different animation, for example:

High Punch, High Punch, High Kick, Back + High Kick

The difference here is that we have to press the last key, High Kick, while Back is being hold down or at same time, it doesn't matter. Also if you hold Back since the first or any other key it doesn't break the combo as it would do in "specific input" combos like the spear one.

There are many other "ramifications" so to say, just playing the game to know exactly what I mean. But anyway I hope you guys understand and have some ideas and suggestions to share. Thanks in advance

asked Dec 5, 2019 in Engine by Crazy (14 points)

1 Answer

+2 votes

Assuming you are using an array to store the combo sequence, you could use, in addition to the array, a var to hold the key being pressed at the time, maybe create a condition to only get the pressed key if it's been pressed for more than a second. Then validate the next animation/attack based on the array's elements and the var.

EDIT: maybe a finite state machine would be better for this.

answered Dec 5, 2019 by null_digital (162 points)

Hey, thanks for the answer. I did a workaround with bits, might not be the best code. If you've suggestions to improve it, let know please.

extends Node2D

const BUTTON_MASK = (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4);
const COMBO = [(1<<0), (1<<0), (1<<3), (1<<3)|(1<<8)];

const BUTTONS = {
    "high_punch": (1<<0),
    "low_punch": (1<<1),
    "block": (1<<2),
    "high_kick": (1<<3),
    "low_kick": (1<<4),
    #"run": (1<<5),
    "ui_up": (1<<6),
    "ui_down": (1<<7),
    "ui_left": (1<<8),
    "ui_right": (1<<9)
};

var _time: int;
var _buffer: Array;

func _ready() -> void:
    pass;

func _unhandled_key_input(event: InputEventKey) -> void:
    if !event.is_echo() && event.is_pressed():
        if OS.get_ticks_msec() - _time > 250:
            _buffer.clear();

        _time = OS.get_ticks_msec();

        for action in BUTTONS:
            if (BUTTON_MASK & BUTTONS[action]) && InputMap.event_is_action(event, action):
                _buffer.append(BUTTONS[action]);
                print("Just pressed '%s'" % action);

                if _buffer.size() > 10:
                    _buffer.pop_front();

                _check_combo();
                break;

func _check_combo() -> void:
    var buttons: int = 0;
    if Input.is_action_pressed("ui_left"):
        buttons |= BUTTONS["ui_left"];

    for i in range(COMBO.size()):
        if _buffer.size() < COMBO.size():
            return;

        if ((_buffer[i]|buttons) & COMBO[i]) != COMBO[i]:
            return;

    print("Do combo!");
    _buffer.clear();

About finite state machine, I did to control player actions such as idle, jump, duck, etc but I'm not sure how I would do it in combos. Do you have an example?

I thought about Lei Wulong. In Tekken 5 he had multiple stances (the five animals form) and each one had a different combo set (maybe he does that in other tekkens but I only played the 5th) and FSM came to mind.
Some MK game from PS2, Deception I think, had all(?) the characters with 2 fighting styles and a weapon style. There were combos specific to each style but the characters could throw iceballs/spears anytime.
Also, if you're going to animate your character based on a combo sequence in execution, you could have a state for each step of the combo. Don't know if it's the most efficient approach, though.
I think you can compare buffer and COMBO sizes outside the loop.

I got what you mean. Btw the above code I did it's not working well. I'm trying to do this https://www.youtube.com/watch?v=Syj7nvSCVQE (check up to 0:17).

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.