Fighting Game Input Buffer Implementation in Unity

 avatar
unknown
csharp
5 months ago
7.4 kB
11
Indexable
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

public class FightingGameInputBuffer : MonoBehaviour
{
    // Defines a special move with a name and its required input sequence.
    [System.Serializable]
    public class SpecialMove
    {
        public string moveName; // Name of the special move.
        public List<string> inputSequence; // Input sequence to execute the move.
    }

    // UI components for displaying special move and input buffer.
    public Text specialMoveText;
    public Transform bufferDisplayParent;
    public GameObject buttonImagePrefab;

    // Duration for displaying a special move message.
    public float specialMoveDisplayDuration = 2f;
    private float specialMoveTimer = 0f;

    // Sprites representing various input directions and actions.
    public Sprite upSprite, downSprite, leftSprite, rightSprite;
    public Sprite lightSprite, mediumSprite, heavySprite, specialSprite;
    public Sprite upLeftSprite, upRightSprite, downLeftSprite, downRightSprite;

    // Input buffer and configuration.
    private List<string> buffer = new List<string>();
    private float bufferStoreTime = 0.4f; // Maximum time an input remains in the buffer.
    private int maxBufferSize = 10; // Maximum size of the input buffer.
    private List<float> inputTimestamps = new List<float>(); // Timestamps for inputs.

    // List of all defined special moves.
    public List<SpecialMove> specialMoves = new List<SpecialMove>();

    // Mappings of input strings to their corresponding sprites.
    private Dictionary<string, Sprite> buttonSprites;

    // Tracks the state of each input key.
    private Dictionary<string, bool> keyStates = new Dictionary<string, bool>
    {
        { "up", false }, { "down", false }, { "left", false }, { "right", false },
        { "upleft", false }, { "upright", false }, { "downleft", false }, { "downright", false },
        { "light", false }, { "medium", false }, { "heavy", false }, { "special", false }
    };

    // Initialize button sprite mappings.
    void Start()
    {
        buttonSprites = new Dictionary<string, Sprite>
        {
            { "up", upSprite }, { "down", downSprite }, { "left", leftSprite }, { "right", rightSprite },
            { "upleft", upLeftSprite }, { "upright", upRightSprite }, { "downleft", downLeftSprite }, { "downright", downRightSprite },
            { "light", lightSprite }, { "medium", mediumSprite }, { "heavy", heavySprite }, { "special", specialSprite }
        };
    }

    // Main update loop to handle inputs, buffer management, and special move detection.
    void Update()
    {
        // Handle individual directional and action inputs.
        HandleInput("up", "Vertical", 1);
        HandleInput("down", "Vertical", -1);
        HandleInput("left", "Horizontal", -1);
        HandleInput("right", "Horizontal", 1);
        HandleInput("light", "LightAttack", 1);
        HandleInput("medium", "MediumAttack", 1);
        HandleInput("heavy", "HeavyAttack", 1);
        HandleInput("special", "SpecialAttack", 1);

        // Handle combined diagonal inputs.
        HandleDiagonalInput("upleft", 1, -1);
        HandleDiagonalInput("upright", 1, 1);
        HandleDiagonalInput("downleft", -1, -1);
        HandleDiagonalInput("downright", -1, 1);

        // Remove expired inputs from the buffer.
        CleanExpiredInputs();

        // Check if any special moves are executed.
        CheckSpecialMoves();

        // Update the special move display timer.
        if (specialMoveTimer > 0)
        {
            specialMoveTimer -= Time.deltaTime;
            if (specialMoveTimer <= 0)
            {
                specialMoveText.text = ""; // Clear the display when timer expires.
            }
        }
    }

    // Tracks input states and adds valid inputs to the buffer.
    void HandleInput(string button, string axisName, float expectedValue)
    {
        bool isButtonPressed = Input.GetAxisRaw(axisName) == expectedValue;
        if (isButtonPressed && !keyStates[button])
        {
            keyStates[button] = true; // Mark input as pressed.
            AddToBuffer(button); // Add to buffer.
        }
        else if (!isButtonPressed)
        {
            keyStates[button] = false; // Reset input state.
        }
    }

    // Handles diagonal input detection by combining vertical and horizontal axes.
    void HandleDiagonalInput(string button, float verticalExpectedValue, float horizontalExpectedValue)
    {
        bool isButtonPressed = Input.GetAxisRaw("Vertical") == verticalExpectedValue &&
                               Input.GetAxisRaw("Horizontal") == horizontalExpectedValue;

        if (isButtonPressed && !keyStates[button])
        {
            keyStates[button] = true;
            AddToBuffer(button); // Add diagonal input to buffer.
        }
        else if (!isButtonPressed)
        {
            keyStates[button] = false;
        }
    }

    // Adds an input to the buffer and maintains buffer size.
    void AddToBuffer(string input)
    {
        buffer.Add(input);
        inputTimestamps.Add(Time.time); // Store timestamp.

        // Remove the oldest input if buffer exceeds maximum size.
        if (buffer.Count > maxBufferSize)
        {
            buffer.RemoveAt(0);
            inputTimestamps.RemoveAt(0);
        }

        UpdateBufferDisplay(); // Update UI to reflect buffer.
    }

    // Removes inputs from the buffer that have expired.
    void CleanExpiredInputs()
    {
        float currentTime = Time.time;
        for (int i = buffer.Count - 1; i >= 0; i--)
        {
            if (currentTime - inputTimestamps[i] >= bufferStoreTime)
            {
                buffer.RemoveAt(i); // Remove expired input.
                inputTimestamps.RemoveAt(i);
            }
        }
    }

    // Checks the buffer for valid special move sequences.
    void CheckSpecialMoves()
    {
        foreach (var move in specialMoves)
        {
            // Compare the buffer's recent inputs with the special move's sequence.
            if (buffer.Count >= move.inputSequence.Count &&
                buffer.Skip(buffer.Count - move.inputSequence.Count).SequenceEqual(move.inputSequence))
            {
                // Display special move and clear the buffer.
                specialMoveText.text = move.moveName + " Executed!";
                specialMoveTimer = specialMoveDisplayDuration;
                buffer.Clear();
                inputTimestamps.Clear();
                break;
            }
        }
    }

    // Updates the UI to display the current input buffer.
    void UpdateBufferDisplay()
    {
        // Clear existing buffer display.
        foreach (Transform child in bufferDisplayParent)
        {
            Destroy(child.gameObject);
        }

        // Instantiate UI elements for each input in the buffer.
        foreach (var button in buffer)
        {
            if (buttonSprites.ContainsKey(button))
            {
                GameObject icon = Instantiate(buttonImagePrefab, bufferDisplayParent);
                icon.GetComponent<Image>().sprite = buttonSprites[button];
            }
        }
    }
}
Editor is loading...
Leave a Comment