NPC jitters when reaching destination

I’m making an NPC system, which follows a path set in a table of positions. The script loops through the table, and executes this function with the position. This script moves the NPC towards the position and changes the movedirection attribute which another script uses to rotate the NPC towards the position. (Which is why it uses heartbeat)

A problem I found though is the NPCs stop for like a millisecond after getting to each position which can be noticeable at times and I want it to be smooth. I’m not sure if it’s caused by the task.wait() at the bottom because I tried removing it and putting all the positions in the heartbeat but the issue was still visible. (Not 100% sure though, that’s just what I found with testing)

Function with the problem

local MoveTo = {}

local replicatedStorage = game:GetService("ReplicatedStorage")
local settingsModule = require(replicatedStorage.ModuleScripts.Settings)

local moveAcceleration = settingsModule["WALK_ACCELERATION"]
local RunS = game:GetService("RunService")

function MoveTo.MoveToPosition(humanoid, rootPart, position)
	local connection

	local moveDirection = humanoid:GetAttribute("MoveDirection")
	humanoid:SetAttribute("MoveDirection", (Vector3.new(position.X, 0, position.Z) - Vector3.new(rootPart.Position.X, 0, rootPart.Position.Z)).Unit)
	local targetMoveVelocity = Vector3.new()
	local moveVelocity = Vector3.new()
	local moveVelocityInt = 0

	connection = RunS.Heartbeat:Connect(function(dt)
		if humanoid and rootPart then
			local distance = (Vector3.new(position.X, 0, position.Z) - Vector3.new(rootPart.Position.X, 0, rootPart.Position.Z)).Magnitude
			if distance > 3.5 then
				if distance > 4 then
					humanoid:SetAttribute("MoveDirection", (Vector3.new(position.X, 0, position.Z) - Vector3.new(rootPart.Position.X, 0, rootPart.Position.Z)).Unit)
				end

				local moveDir = humanoid:GetAttribute("MoveDirection")
				if moveDir then
					if moveDir == Vector3.zero then
						moveVelocityInt = math.clamp(moveVelocityInt - (dt * moveAcceleration / 2), 0,1)
					else
						moveVelocityInt = math.clamp(moveVelocityInt + (dt * moveAcceleration / 2), 0,1)
					end

					humanoid:Move(rootPart.CFrame.LookVector.Unit*moveVelocityInt)
				end
			else
				--humanoid:SetAttribute("MoveDirection", Vector3.zero)
				if math.random(1,10) >= 10 then
					task.wait(math.random(2,4))
				else
					--humanoid:Move(Vector3.zero)
				end

				connection:Disconnect()
			end
		else
			connection:Disconnect()
		end
	end)
	
	repeat task.wait() until not connection.Connected
end

return MoveTo

Server Script which calls the function

local function startMovement(NPC, NPCRoot, NPCHum)
		local position = calculateRandomPosition(NPCRoot)
		if not position then return end

		local path = pathfinder.FindPath(NPCRoot.Position, position)
		if path then
			for _, pos in ipairs(path) do
				moveToModule.MoveToPosition(NPCHum, NPCRoot, pos)
			end
			startMovement(NPC, NPCRoot, NPCHum)
		end
	end

	pathfinder:Init()

	for _, NPC in pairs(workspace.ActiveNPCs:GetChildren()) do
		local nodesTable = nodes:GetChildren()
		local NPCRoot = NPC:WaitForChild("HumanoidRootPart")
		local NPCHum = NPC:WaitForChild("Humanoid")

		local randomNumber = math.random(1, #nodesTable)
		local node = nodesTable[randomNumber]
		local position = node.Value

		NPC:SetPrimaryPartCFrame(CFrame.new(position))

		task.spawn(function()
			startMovement(NPC, NPCRoot, NPCHum)
		end)
	end

Video of what happens

You can see they stop for like a milisecond before continuing to the next node

try to set :SetNetworkOwner(nil) for all parts in the npc model.

I’ve already done that, It’s only when they reach the nodes that they pause for like a millisecond. They should turn or continue straight to the next node smoothly.

maybe the problem is in the interpolation formula.
try this

if moveDir == Vector3.zero then
    moveVelocityInt = math.clamp(moveVelocityInt + (0 - moveVelocityInt) * moveAcceleration * dt, 0, 1)
else
    moveVelocityInt = math.clamp(moveVelocityInt + (1 - moveVelocityInt) * moveAcceleration * dt, 0, 1)
end
1 Like

Nevermind, I found the problem. MoveVelocityInt was being reset every time the function was run, I switched it to an attribute in the humanoid and that seemed to solve the problem. Thank you for your help though!

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