Search Unity

How to check if a list contains the contents of another in for fighting game motion inputs

Discussion in 'Scripting' started by treese_, Sep 21, 2022.

  1. treese_

    treese_

    Joined:
    Oct 15, 2019
    Posts:
    3
    So I'm currently working on a little personal programming project, I'm trying to make a sort of fighting game prototype to at least understand the programming that goes into making a fighting game. One thing I'm having particular trouble with is the motion inputs. I've got a system rigged up where, when an input for a direction is detected, a string representing that input is temporarily added to a list, and then shortly removed. Let's use a classic quarter-circle forward motion, the Hadouken, as an example. The script checks that "Recent Inputs" list for a down input, then checks if the two inputs that come after it are down-forward and forward, if so, then...hadouken!

    Code (CSharp):
    1. foreach (string input in recentInputs)
    2. {
    3.     #region Quartercricles
    4.  
    5.         //Quartercircle Forward
    6.         if (input == "down")
    7.         {
    8.             if(input != null)
    9.             {
    10.                 int startingInputIndex = recentInputs.IndexOf(input);
    11.                 int baseStartingInputIndex = startingInputIndex;
    12.                              
    13.                              
    14.                 if (recentInputs[startingInputIndex += 1] == "downForward" && recentInputs[startingInputIndex += 1] == "forward")
    15.                 {
    16.                     Debug.Log("Hadouken!");
    17.                     startingInputIndex = baseStartingInputIndex;
    18.  
    19.                 }
    20.             }
    21.         }
    This code kind of works, but I feel like it's really unreliable and inefficient. I mean look how long the code for a half circle is. Imagine if I wanted to do a 360 or a 720, it'd be a nightmare.

    Code (CSharp):
    1. if (input == "back")
    2. {
    3.     int startingInputIndex = recentInputs.IndexOf(input);
    4.     int baseStartingInputIndex = startingInputIndex;
    5.  
    6.     if (recentInputs[startingInputIndex += 1] == "downBack" && recentInputs[startingInputIndex += 1] == "down" && recentInputs[startingInputIndex += 1] == "downForward" && recentInputs[startingInputIndex += 1] == "forward")
    7.     {
    8.         Debug.Log("Yoga Fire!");
    9.         startingInputIndex = baseStartingInputIndex;
    10.     }
    11.  
    12. }
    What I would ideally like to do is just set up lists with the motions already in them, and then see if my Recent Inputs list contains that same set of inputs, so if there's a series of inputs that match up with the QCF list, it runs the Hadoken code, and so on. This would allow me to create a far more universal input system than what I have currently. Is there a way to do that, to see if a list contains the contents of another, in order? Or can you think of some better way to pull off these motion inputs, something more efficient?
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    1. Don't use strings, use enums.
    2. Don't manually check every element of the array with bespoke code for every move like this. Do something like make an expected list of inputs and use a loop to compare each element in order (or just compare the lists with e.g. https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.sequenceequal?view=net-7.0

    Then you can reuse that code for every single move, just passing in the different move lists. You could even put all the moves in a prefix tree/ trie for maximum efficiency:
    https://en.wikipedia.org/wiki/Trie

    Basically whenever input comes in, you check all the children of the current node in the prefix tree and see if any match. If they do, you go down that node. Eventually you get to a leaf node which is a completed move.
     
    Last edited: Sep 22, 2022
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Assuming timing is irrelevant (it isn't in most fighting games), just turn the inputs into a series of tokens.

    When a new token is added to the list of ongoing tokens, check it against known combo sequences.

    If you want to add in timing, make the ongoing token list get cleared if too much time passes on a given token, or perhaps too much time passes overall.

    If you want to add in quality of input, store timing marks with each input token and see how close thy are to optimal.

    A lot of golf or baseball games do systems like this, where you are graded according to being close to a particular input cadence.

    In a single expression, this practice is sub-ideal:

    Because the
    +=1
    is a side effect that is only conditionally executed in the second half if the first half was true.