Jittery Lerp Animation with CanCollide off

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I’d like to smoothly animate 3 ice-shards coming from underground to just above ground.

  1. What is the issue? Include screenshots / videos if possible!

The ice shards jitter pretty badly and flash causing a bad animation.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I tried TweenService but found a tutorial online showing the lerp method (which I’m more familiar). I have turned CanCollide OFF so as not to interfere with the terrain. Here is relavent code:

Client: (simply fires to server)

remotes.IceFiredByPlayer:FireServer()

Server: (spawns the ice)

local function randomSpawnObject(object, minVector, maxVector, offset)
	local raycastOrigin = Vector3.new(random:NextNumber(minVector.X, maxVector.X), random:NextNumber(minVector.Y, maxVector.Y), random:NextNumber(minVector.Z, maxVector.Z))
	local raycastDirection = -Vector3.yAxis
	local distance = 1000

	local result = workspace:Raycast(raycastOrigin, raycastDirection * distance)
	if result then
		object:PivotTo(CFrame.new(result.Position.X, result.Position.Y - offset, result.Position.Z))
	end
end


remotes.IceFiredByPlayer.OnServerEvent:Connect(function(player)
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("HumanoidRootPart")
	local originPosition = humanoid.Position
	
	local ice1 = iceShard:Clone()
	local ice2 = iceShard:Clone()
	local ice3 = iceShard:Clone()
	ice1.Parent = workspace
	ice2.Parent = workspace
	ice3.Parent = workspace
	
	randomSpawnObject(ice1, Vector3.new(originPosition.X - 20, originPosition.Y, originPosition.Z - 20), Vector3.new(originPosition.X + 20, originPosition.Y, originPosition.Z + 20), 0.5)
	randomSpawnObject(ice2, Vector3.new(originPosition.X - 20, originPosition.Y, originPosition.Z - 20), Vector3.new(originPosition.X + 20, originPosition.Y, originPosition.Z + 20), 0.5)
	randomSpawnObject(ice3, Vector3.new(originPosition.X - 20, originPosition.Y, originPosition.Z - 20), Vector3.new(originPosition.X + 20, originPosition.Y, originPosition.Z + 20), 0.5)
	
	debrisService:AddItem(ice1, 1.1)
	debrisService:AddItem(ice2, 1.1)
	debrisService:AddItem(ice3, 1.1)
end)

Finally, the animation (lerp) which is on the ice shard itself. The script runs when the ice is cloned into the world.

local runService = game:GetService("RunService")

local iceShard = script.Parent
local startCFrame = iceShard.Union.CFrame
local targetCFrame = iceShard.Union.CFrame * CFrame.new(0, 6, 0)

local lerpTime = 1.1
local startTime = tick()
local runningTime = 0
local lerp

lerp = runService.Heartbeat:Connect(function(deltaTime)
	runningTime += deltaTime
	
	local alpha = runningTime / lerpTime
	script.Parent.Union.CFrame = startCFrame:Lerp(targetCFrame, alpha)
	
	if alpha >= 1 then
		script.Parent.Union.CFrame = targetCFrame
		lerp:Disconnect()
	end
end)

My game will have quite a few animations like this so I’d really like to figure this one out. Thanks!

Super easy solution for this, should help out with the issues you’re having.

All that the server needs is a start point and an end point for the ice shard. During the total duration of the “animation” (the ice spike going up) just have it start at the start position, go halfway (or a third, depending on how you want it to look) then finish the “animation” on the server by setting the position to the end position. (If you want to have this ice spike deal damage or etc, I reccomend splitting it into thirds or fourths so the hitbox will seem realistic).

However, this will look really weird from a client’s perspective. So, I would fake it. You can pass the “real” ice spike (the one on the server) to the client, along with the targetCFrame value. Then you can make this server ice spike invisible on the client and duplicate the ice spike so you have a client-only reference.

A lot of the jittering will likely come from server-client discrepancies, so transfer that lerp code you sent to a function (or something similar) which will take your ice spike and a finalCFrame and lerp it smoothly. Finally, once that lerp finishes, you can destroy the client only version and set the “real” version to be visible, and you’re done!

As a bonus, if you want to optimize it even further, if a player isn’t within some range of the ice spike, you don’t have to have their client animate it, and it can just be the jerky animation on the server because there’s a good chance they won’t see it. Good luck! :slight_smile:

Thanks for the reply. After messing with it for a while I went with TweenService and it works pretty decently. This post helped a lot:

For completeness, here’s the code I ended with:

script.Parent.OnServerEvent:Connect(function(player)
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("HumanoidRootPart")
	local originPosition = humanoid.Position
	
	for i = 0, 2 do
		local ice = iceShard:Clone()
		ice.Parent = workspace
		
		randomSpawnObject(ice, Vector3.new(originPosition.X - 20, originPosition.Y, originPosition.Z - 20), Vector3.new(originPosition.X + 20, originPosition.Y, originPosition.Z + 20))
		
		game.Debris:AddItem(ice, 3)
		tweenservice:Create(ice.Root, TweenInfo.new(20, 0, 0), {CFrame = ice.Root.CFrame + Vector3.new(0, -15, 0)}, Enum.EasingStyle.Quint,Enum.EasingDirection.Out):Play()
		ice.Touched:Connect(function(hit)
			iceTouched(hit, player)
		end)
	end
	
	task.wait(2)

	alreadytouched = true
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.