Groove Animator - Lightweight and Virtual Animation Playback

Can you send a repro? I can check it out.

The example works in F8 testing mode. If you implement it to run on the client, it will work on the client. This is a utility that can handle animation playback, it is not a drop in replacement for the Roblox Animator.

like the script i used it in?

uhh here it is a config module for test character. its unfinished but the variable

local transitionSpeed = 0.001

if set to zero causes animations to only play once


local ReplicatedStorage = game:GetService("ReplicatedStorage")

local UserInputService = game:GetService("UserInputService")
local Modules = ReplicatedStorage:WaitForChild("Modules")
local Utilities = require(Modules:WaitForChild("Utilities"))
local Groove = require(Modules:WaitForChild("GrooveAnimator")) -- this is the animation engine I am using
local ConfigModule = {}

function ConfigModule.new()
	local GrooveController
	local Animations = {}
	local Fighter
	local Config = {
		weight = 90,
		gravity = 120,
		jumpSquat = 0.4,
		stageBounds = {
			floor = 3.5,
			wall = 2,
			ceiling = 2,
		},
		hipHeight = 3,
		groundFriction = 40,
		airFriction = 16,
		airSpeed = 9,
		maxAirSpeed = 20,
		landingLag = {
			short = 0.07,
			long = 0.12,
		},
		jumpPower = {
			fullhop = 70,
			shorthop = 40,
			air = 70,
		}
	}
	Config.attacks = {}
	Config.rig = nil
	local transitionSpeed = 0.001
	
	local function StopAllAnimations()
		for i, v in ipairs(script.Animations:GetChildren()) do
			local anim = Animations[v.Name]
			if anim.IsPlaying then
				anim:Stop(transitionSpeed)
			end
		end
	end
	
	function Config.AirJump()
		
	end
	
	function Config.StateChanged(new,old)
		StopAllAnimations()
		if new.value == "idle" then
			Animations.idle:Play(transitionSpeed)
		elseif new.value == "fall" then
			Animations.fall:Play(transitionSpeed)
		elseif new.value == "landing" then
			Animations.landingLag.TimePosition = 0
			Animations.landingLag:Play(transitionSpeed)
		end
	end
	
	function Config.Load(f:any)
		Fighter = f
		Config.rig = script:WaitForChild("Rig"):Clone()
		Config.rig.Parent = workspace:FindFirstChild("Fighters")
		GrooveController = Groove.newController()
		GrooveController.AttachRig(GrooveController,Config.rig)
		Animations = {}
		for i, v in ipairs(script:WaitForChild("Animations"):GetChildren()) do
			local anim = Groove.newTrack(Groove:ImportKeyframeSequence(v))
			Animations[v.Name] = anim
			GrooveController.AddTrack(GrooveController,anim)
		end
		for i, v in ipairs(script.AttackData:GetChildren()) do
			Config.attacks[v.Name] = require(v).Load(Fighter,Config,Animations)
		end
		-- Animations.classicIdle:Play() -- this is just for testing
	end
	
	function Config.Update(dt:number,castX:RaycastResult?,castY:RaycastResult?)
		--Fighter.state.time = 0
		local RootPart:BasePart = Config.rig.HumanoidRootPart
		local angle = -90
		if Fighter.facing then
			angle = -90
		end
		RootPart.CFrame = CFrame.new(Utilities.To3D(Fighter.position)) * CFrame.Angles(0,math.rad(angle),0)
		GrooveController:Step(dt) -- update animations
	end
	
	function Config.AttackInput()
		if Fighter.state.value == "fall" or (Fighter.state.value == "tumble" and Fighter.state.type == "end") or Fighter.state.value == "jump" or Fighter.state.value == "airJump" then
			
		else
			
		end
	end
	
	UserInputService.InputBegan:Connect(function(input)
		if input.KeyCode == Enum.KeyCode.LeftShift then
			Fighter.position = Vector2.new(0,14)
			Fighter.velocity = Vector2.new(24,math.random(-12,12))
		end
	end)
	
	return Config
end

return ConfigModule

yes, it is a platform fighter i’m making

Are you using the latest version from GitHub? Or the version in the example project?

im using the example project version of the module.

The latest version on Github should have that fixed.