Custom 'Animate' script

after two years of being busy with whatever (i still am busy), i decided to continue learning lua/luau. 'tis good? please give advice and point out if there’s anything dumb in this script, thanks! it works but what i want to know is if there are any dumb practices in this script

local plr = game:GetService('Players').LocalPlayer
local chr = plr.Character or plr.CharacterAdded:Wait()
local hum = chr:FindFirstChildOfClass('Humanoid')

local rs = game:GetService('RunService')

--> <Id_Weight> <--
local animations = {
	Running = '123_1',
	Jumping = '1234_1',
	Freefall = '12345_1',
	Climbing = '123456_1',
	Seated = '1234567_1',
	Idle = '12345678_1'
}

local currentAnim = nil
local animTracks = {}
local baseWalkSpeed = hum.WalkSpeed
local lastWalkSpeed = 1

local init_Animations = function()
	local animator = hum:FindFirstChildOfClass('Animator') or Instance.new('Animator', hum)

	for stateName, animData in pairs(animations) do
		local animId, weightStr = string.match(animData, "^(%d+)_(%d+)$")
		local weight = tonumber(weightStr) or 1
		local state = stateName == "Idle" and "Idle" or Enum.HumanoidStateType[stateName]

		local anim = Instance.new('Animation')
		anim.AnimationId = 'rbxassetid://'..animId

		local track = animator:LoadAnimation(anim)
		track:AdjustWeight(weight)
		animTracks[state] = track
	end
end

local stopCurrentAnim = function(speed)
	if currentAnim and currentAnim.IsPlaying then
		currentAnim:Stop(speed or 0.1)
	end
	currentAnim = nil
end

local playStateAnim = function(state)
	local track = nil

	track = state == Enum.HumanoidStateType.Running
		and (hum.MoveDirection.Magnitude > 0.1 and animTracks[state] or animTracks.Idle)
		or animTracks[state] 
		or animTracks.Idle

	if track and currentAnim ~= track then
		if state == Enum.HumanoidStateType.Jumping then
			if currentAnim then stopCurrentAnim(0.15) end
			track:Play(0.3)
		else
			stopCurrentAnim()
			track:Play()
		end

		if state == Enum.HumanoidStateType.Running then
			lastWalkSpeed = hum.WalkSpeed / baseWalkSpeed
			track:AdjustSpeed(lastWalkSpeed)
		else
			track:AdjustSpeed(1)
		end

		currentAnim = track
	end
end

local handleMovement = function()
	local lastState = hum:GetState()

	hum:GetPropertyChangedSignal("WalkSpeed"):Connect(function()
		if currentAnim == animTracks[Enum.HumanoidStateType.Running] then
			local newSpeed = hum.WalkSpeed / baseWalkSpeed
			if math.abs(newSpeed - lastWalkSpeed) > 0.1 then
				lastWalkSpeed = newSpeed
				currentAnim:AdjustSpeed(newSpeed)
			end
		end
	end)

	hum.StateChanged:Connect(function(_, newState)
		lastState = newState
		playStateAnim(newState)
	end)

	while true do
		rs.Heartbeat:Wait()

		if lastState == Enum.HumanoidStateType.Running then
			playStateAnim(lastState)
		end

		if not hum or hum.Health <= 0 then break end
	end
end

local main = function()
	init_Animations()
	hum.Died:Connect(stopCurrentAnim)
	coroutine.wrap(handleMovement)()
	playStateAnim(hum:GetState())
end

local status, err = pcall(main)
if not status then warn(script.Name.." made the following oops: ", err) end
1 Like

Imo this is is very good code. There are a couple small things I could pick on, but none of those are really important tbh. Like for example, loops no longer require the use of pairs and ipairs , just the table is enough. And I personally wouldn’t do

while true do
    RunService.Heartbeat:Wait()
    ...
end

But rather

while RunService.Heartbeat:Wait() do
    ...
end

But again, that’s not really important and is probably a more personal preference.

And lastly, I don’t really see the point in the last error handling line, because error lines already give you the information of in what script the error occured, on what line it occures and what are some of the specifics of it.

But yeah, nothing much. Keep it up!

2 Likes