Tweening model while also using AnimationController resulting in jittery movement

Hello,

I’m working on creating a custom humanoid :MoveTo() functionality. This is in hopes to remove the humanoid from my NPC characters and alleviate any unnecessary overhead of humanoids to optimize on performance.

The way I’m implementing this is by Tweening the rig’s root part to a predetermined location. The animations happen locally to ensure that they look smooth and also to lessen the load on the server. I’m running animations through an AnimationController.

This works for the most part except for one small issue where the rig basically jitters when moving. I suspect that this is caused because the movements/adjustments of parts done by the animation are overlapped with the tweening process which doesn’t look clean. Is there a different approach to solve this issue?

while wait(math.random(3)) do
	ind = math.random(#destinations) -- randomly choose a destination (these are parts)
	
	local target = destinations[ind]
	
	local dist = math.sqrt(math.pow(target.Position.X - root.Position.X,2) + math.pow(target.Position.Z - root.Position.Z,2)) -- calculate distance
	local waitToStop = dist / 12 -- calculate wait, the 12 would be in studs per second
	
        -- Creating CFrame based on lookVector to find the appropriate angles to rotate the model
	local rot = CFrame.new(root.Position, Vector3.new(target.Position.X, root.Position.Y, target.Position.Z)) 
	radX, radY, radZ = rot:ToOrientation() 
	
	
	local rotate = TweenService:Create(root, TweenInfo.new(1,Enum.EasingStyle.Sine), {CFrame = rot})
	rotate:Play()
	
        -- The state is a string value found inside the rig
	state.Value = "Walk"
	
	wait(1)
	
	print("Moving to "..target.Name..ind)
	
        -- Calculating final position and tweening the model
	local fin = CFrame.new(target.Position.X, root.Position.Y, target.Position.Z) * CFrame.Angles(radX, radY, radZ)
	local tween = TweenService:Create(root, TweenInfo.new(waitToStop, Enum.EasingStyle.Linear), {CFrame = fin})
	tween:Play()

	wait(waitToStop)
	
        -- Updating state
	state.Value = "Stop"
end

Below is what I am running locally.

local rig = workspace:WaitForChild("Cougar1")

controller = rig.AnimationController
anim = controller:LoadAnimation(rig.Walk)
anim.Looped = true
anim.Priority = Enum.AnimationPriority.Movement

local state = rig:WaitForChild("State")

state.Changed:Connect(function()
	if state.Value == "Walk" then
		anim:Play()
	else
		anim:Stop()
	end
end)

This video shows the jitter quite well : https://gyazo.com/ce1be8c15692463b91d8d76a9786e6e2

I’ve resolved that it’s an issue with the Tween (not the animation), but I’m not sure how to proceed or fix the Tween to be smoother

Is is an animation made with animation editor if so, check that you have set the animation priority to movement and added all the parts of the body to the animation which can be done by the plus sign on the left or right hand side depending on what you’re using. Nevermind i don’t think what i’m saying has anything to do with what you want.

I’m setting the animation priority to Movement through the script, but it shouldn’t matter as this is the only animation I anticipate running on these objects (there won’t be any need to resolve which animation has highest priority, if there is only one).

I’m 99% sure there isn’t any issue with the animations themselves (unless there is some difference in how animations are run through humanoids/AnimationControllers). The animations run smoothly when overlayed with the humanoid:MoveTo() function.

1 Like

Bumping this in case anyone else has any input.

Can we see a screen recording, might help.

https://gyazo.com/e847553e00a5403a3e820e4d1183000f

It’s hard to see, but this is the best I could record. The jitters is sorta like a vibration of the model which doesn’t happen when I animate in conjuction with Humanoid:MoveTo()

What happens if you break it out of the loop and just use one single tween. To move one part. Does it still not work?

I’d break it down and rebuild it until I found out which part of code introduced the jitter.

I am only moving one part at a time. Since all the parts are joined together by Motor6D’s, they drag along with the root part that I’m moving. The loop just randomly selects a new point for the model to move to, I don’t think it’s the cause of the problem here necessarily.

The first tween is to rotate the model btw, and the second is to move the model. The jittering mainly happens with the second tween I’m playing in the loop.

Yeah, but is it still possible to remove it from the loop and test it?

I’d be curious to see if the loop is introducing some weirdness like playing it 2x for some weird reason.

And we can isolate that as not an issue if we remove it from any kind of loop and break it into its most simple form.

What is the most simple code to make just one part move with that second tween you are suspicious about?

I’ve found this to be helpful in the past:

  1. Don’t trust anything. Test each tiny part in isolation
  2. Break it down to the simplest testable part (hopefully just 1 line of code)
  3. Test just that one line and see if it’s doing what you think it’s doing (usually print the result)
  4. Add 1 more line of code and go back to 2
1 Like

Alright I simplified it up till this point:

target = destinations[1]

controller = rig.AnimationController
anim = controller:LoadAnimation(rig.Walk)
anim.Looped = true
anim.Priority = Enum.AnimationPriority.Movement


dist = math.sqrt(math.pow(target.Position.X - root.Position.X,2) + math.pow(target.Position.Z - root.Position.Z,2))
waitToStop = dist / 16

rot = CFrame.new(root.Position, Vector3.new(target.Position.X, root.Position.Y, target.Position.Z))
radX, radY, radZ = rot:ToOrientation()

rotate = TweenService:Create(root, TweenInfo.new(1,Enum.EasingStyle.Sine), {CFrame = rot})
rotate:Play()

fin = CFrame.new(target.Position.X, root.Position.Y, target.Position.Z) * CFrame.Angles(radX, radY, radZ)
tween = TweenService:Create(root, TweenInfo.new(waitToStop, Enum.EasingStyle.Linear, Enum.EasingDirection.In), {CFrame = fin})
tween:Play()

anim:Play()

I’m not sure there’s much more I can remove, but the jittery movement still occurs with this. Keep in mind the animation is now being played through the server as opposed to the client (but that doesn’t make much difference).

EDIT : I actually found that the Tween alone also causes the jitters to occur, so it’s not an issue with the animation as far as I can tell.

Here is a video to show more clearly the issue that I’m trying to fix:
https://gyazo.com/ce1be8c15692463b91d8d76a9786e6e2

Basically, I don’t want the model to be vibrating. I want the movement to be as clean as possible.

What happens if you just take a basic part just 1 block and run the same tween on it?

Still having vibrations : https://gyazo.com/682b4e6730ebb7774d0bc9fb04fc1894

You can see the jittery movement really clearly towards the end

The script so far:

root = workspace.Part
target = destinations[1]

dist = math.sqrt(math.pow(target.Position.X - root.Position.X,2) + math.pow(target.Position.Z - root.Position.Z,2))
waitToStop = dist / 16

rot = CFrame.new(root.Position, Vector3.new(target.Position.X, root.Position.Y, target.Position.Z))
radX, radY, radZ = rot:ToOrientation()

rotate = TweenService:Create(root, TweenInfo.new(1,Enum.EasingStyle.Sine), {CFrame = rot})
rotate:Play()

wait(1)

fin = CFrame.new(target.Position.X, root.Position.Y, target.Position.Z) * CFrame.Angles(radX, radY, radZ)
tween = TweenService:Create(root, TweenInfo.new(waitToStop, Enum.EasingStyle.Linear, Enum.EasingDirection.In), {CFrame = fin})
tween:Play()

What happens if you remove the rotate tween?

Still choppy…I’m going to try running the tween on the client and see if that works.

Ah, yes, we want to run all animation on the client if possible. Is there any reason you need to run this tween on the server rather than each person’s client?

Well, I want the server to perform some of the movement as well, so that the movement is synchronized between all clients. I could probably use Lerp on the server and Tweening on the client which is what I’m trying now.

Aaand that solves it. Running the tween on the client is significantly cleaner than on the server. I’m quite happy with this approach as it further reduces the load on the server and allows for smoother animations. I might play around with using Lerp() on the server, but for now this is working as intended albeit in need of some refinement

I’ll mark this as the solution in case anyone stumbles on this thread:

  • Run as much animation as possible on the client.
  • Keep track of the positions of the object through the server. If you’re tweening a model to move, just use :SetPrimaryPartCFrame() on the server and tween the object locally.
4 Likes

The other thing I want to point out to folks reading this, is always break down your problem into the absolute minimum amount of variables to reproduce the bug!

If you follow this thread you will see we had all sorts of speculation about what the problem was. It was not until we broke it down to just - 1 tween - 1 block - and the problem STILL happened. It was only then that it was obvious this was not a code issue and must be an environment issue.

For Fastest Possible Debugging

  1. Don’t trust anything. Test each tiny part in isolation (one by one)
  2. Comment out all your code except for the first line
  3. Test the line and make sure it is doing what you think it’s doing (usually print the result)
  4. Uncomment 1 more line of your code and go back to 3
1 Like