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):
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.
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.
Animator Setup:
It creates an Animator instance using the “Rusa” module.
Animator Initialization:
It loads animations for the Humanoid using the Animator:LoadAllAnims(Humanoid) method.
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.
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).
Record Input Function:
RecordInput(inputObject) is a function that records keyboard input (keycodes) into the inputs table. It also updates currentInput and timeAtInput variables.
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.
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.
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.