How would I make a input/combo system similar to Injustice2? [2.5D Fighting Game]

What do you want to achieve?

Make a proper input system that is similar to Injustice2, where they have attack strings and special moves.

For example:

Attack String:


The required inputs are: (1, 1, 2 in “fighting game notation” or X,X,Y for Xbox.)

Special Move:


The required inputs are: (Down, Forward, 1)

What is the issue?

I’ve made multiple attempts to make this system, they’ve mostly ONLY been successful in executing special moves and not attack strings. I just can’t seem to wrap around how the two same buttons being inputted can cause different animations and hitboxes to execute. The sources I’ve used to make my previous systems are I wanna make a fighting game! A practical guide for beginners — part 6 | by Andrea "Jens" Demetrio | Medium.

I don’t want a whole system (you can if you want to), if anyone has an article I could read that explains how NetherRealm Studios (Creator of Injustice2) managed to achieve the attack strings in conjunction with special moves that’d be highly appreciated. Any sort of help would be appreciated. Thank you.

Nevermind, for anyone also stuck on this problem here you go:

local Player: Player = game.Players.LocalPlayer
local Character: Model = Player.Character or Player.CharacterAdded:Wait()
local Humanoid: Humanoid = Character:WaitForChild('Humanoid')


local UserInputService = game:GetService("UserInputService")
local Rusa = require(game.ReplicatedStorage.Rusa).new()
local Animator = Rusa.Animator.new()

-- Rusa Boilerplate

Animator:LoadAllAnims(Humanoid)

-- Initialize variables

local inputs = {}  -- Table to store inputs
local timeAtInput = 0
local alreadyProcessed = false
local currentInput
local previousAnimations: {} = {}
-- Dataset	

local Moveset = {
	-- Punch
	
	["H"] = {
		Action = function()
			Animator:GetTrack("Follow_Punch"):Stop()
			Animator:GetTrack("Basic_Punch"):Play()
		end, -- First initial hit.
		["H"] = {
			Action = function()
				Animator:GetTrack("Basic_Punch"):Stop()
				Animator:GetTrack("Follow_Punch"):Play()
			end,
		}, -- H has been inputted after H was previously inputted
		["H_J"] = {
			Action = function()
				
			end,
		} -- H was inputted after H_J was inputted prior.
	}
	
	-- Kick
}


-- Function to record input

local function RecordInput(inputObject)
	table.insert(inputs, inputObject.Name)  -- Store the name of the input key
	currentInput = inputObject.Name
	timeAtInput = tick()
	alreadyProcessed = false
end

-- Function to check and print the inputs if the time threshold is reached

local function ProcessInputs()
	if #inputs > 0 then
		
		if alreadyProcessed == false then
			local inputString = table.concat(inputs, "_")  -- Convert the inputs table to a string			
			local previousInputs = inputString:sub(1, -3)
			previousInputs:gsub(",","_")
			print("Input(s): " .. inputString)  -- Print the inputs as a string
			print(`Previous Input(s): {previousInputs}`)
			
			if previousInputs ~= "" and Moveset[currentInput] then -- Check if there were any previous inputs.
				if Moveset[currentInput][previousInputs] then
					Moveset[currentInput][previousInputs].Action()
				end
			else
				if Moveset[currentInput] then
					Moveset[currentInput].Action()
				end
			end
			
			
			alreadyProcessed = true
		end
	

		-- Make sure it doesn't go past 3 inputs

		if #inputs >= 3 or tick() - timeAtInput >= 0.15 then
			inputs = {}
			previousAnimations = {}
		end
		
	end	
end

-- Connect UserInputService to record inputs
UserInputService.InputBegan:Connect(function(inputObject, gameProcessedEvent)
	if gameProcessedEvent then return end
	if inputObject.UserInputType == Enum.UserInputType.Keyboard then
		RecordInput(inputObject.KeyCode)
	end
end)

game:GetService("RunService").Heartbeat:Connect(ProcessInputs)

Rusa is just my custom-framework and Animator is exactly what it says, it helps play animations and loads them.

Let’s break down the code step-by-step first (Featuring ChatGPT):

  1. Player and Character Setup:
  • It begins by getting the LocalPlayer from the game.Players service.
  • It then retrieves the player’s character (Player.Character) or waits for it to be added (Player.CharacterAdded:Wait()).
  • Finally, it gets the Humanoid object within the character.
  1. Required Services and Modules:
  • It obtains the UserInputService and requires a module called “Rusa” from game.ReplicatedStorage. This module is expected to have a class with a constructor (new) and an Animator.
  1. Animator Setup:
  • It creates an Animator instance using the “Rusa” module.
  1. Animator Initialization:
  • It loads animations for the Humanoid using the Animator:LoadAllAnims(Humanoid) method.
  1. Input Handling:
  • It initializes various variables to manage input handling, including an empty inputs table to store input key names, timeAtInput to track when an input was recorded, and flags like alreadyProcessed to avoid double processing of inputs.
  • currentInput and previousAnimations are initialized as well.
  1. Moveset Definition:
  • The Moveset table is defined, which appears to specify different actions for various combinations of input keys (e.g., “H” for punch, “H_H” for a combo, “H_J” for a different action).
  1. Record Input Function:
  • RecordInput(inputObject) is a function that records keyboard input (keycodes) into the inputs table. It also updates currentInput and timeAtInput variables.
  1. Process Inputs Function:
  • ProcessInputs() function is used to check and process inputs.
  • It checks if inputs are available and not already processed.
  • Converts the input table into a string and prints it, as well as any previous inputs.
  • Then, it checks the Moveset to determine the action to perform based on the current and previous inputs.
  1. Input Event Connection:
  • UserInputService.InputBegan is connected to a function that records keyboard inputs when they are detected. It checks if the input type is a keyboard input.
  1. Heartbeat Connection:
  • The ProcessInputs function is connected to the RunService.Heartbeat event, which continuously checks and processes inputs.

Essentially all it does is get the current input and does something BASED off of it’s previous inputs. So for example:

“Punch_Punch” is our current input string.

Our new and current input is “Punch”, now notice how there’s a punch beforehand. You’re not going to play the same animation again right? You’re gonna change it to a follow-punch or something. So the script recognizes that and changes the animation according to the previous input which in this case is still punch. If there were no inputs beforehand meaning this is the first input in the input string, then it plays the starting animation.

This has obvious flaws, especially with timing but I’m sure you can figure that out.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.