Combat State Machine for Block / Attack / Stun System

Hello everyone,

I recently implemented a state machine for my combat system and would appreciate feedback and code review.

My system currently includes attack, block, perfect block, and stun mechanics. I’d like to know whether this state machine design is safe and sufficient for such a system, or if there are any potential issues or improvements I should consider.

local StateMachine = {}

StateMachine.Priorities = {
	Idle = 1,
	Attack = 2,
	Block = 2,
	Stunned = 3
}

StateMachine.Transitions = {
	Idle = {
		Attack = true,
		Block = true,
		Stunned = true
	},

	Attack = {
		Idle = true,
		Stunned = true
	},

	Block = {
		Idle = true,
		Stunned = true
	}
}

function StateMachine:CanTransition(character, newState)
	local currentState = character:GetAttribute("State") or "Idle"

	local currentPriority = self.Priorities[currentState]
	local newPriority = self.Priorities[newState]

	if not currentPriority or not newPriority then
		return false
	end

	local transitions = self.Transitions[currentState]

	if transitions and transitions[newState] then
		return true
	end

	return newPriority > currentPriority
end

local StateTokens = setmetatable({}, {
	__mode = "k"
})

function StateMachine:SetState(character: Model, newState: string, duration: number?)
	if not self:CanTransition(character, newState) then return false end

	character:SetAttribute("State", newState)

	local token = (StateTokens[character] or 0) + 1
	StateTokens[character] = token

	if duration then
		task.delay(duration, function()
			if not character.Parent then return end
			if StateTokens[character] ~= token then return end

			character:SetAttribute("State", "Idle")
		end)
	end

	return true
end

return StateMachine
1 Like

Well, I don’t see any particular usage in comparison with other state machines out there. You’re basically inventing a wheel which is also limited.

1 Like

Well, honestly, I’m not sure how to properly design a state machine. The main reason I implemented it this way was to prevent invalid transitions that shouldn’t happen, such as going directly from Attack to Block.

If you have experience with state machines, could you explain how they are usually structured and how transitions are handled? I’d like to understand the proper approach so I can try building one myself.