The npc keeps twitching when they are turning corners.
Video showing that :
- I don’t want the waypoints to stick to the wall
- The npc starts to twitch around corners
The script is here (line 97 is where the pathfinding stuff is):
local npcTag = "StandardBroccoli"
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local TweenService = game:GetService("TweenService")
local ServerStorage = game:GetService("ServerStorage")
local NPCFunctions = require(ServerStorage.NPCFunctions)
local DamageModule = require(ServerStorage.DamageModule)
local EffectModule = require(ServerStorage.EffectsModule)
local function visualise(pos)
local p = Instance.new("Part")
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(1, 1, 1)
p.Position = pos
p.Color = Color3.new(0, 1, 0)
p.Parent = workspace
game:GetService("Debris"):AddItem(p, 5)
end
local function HandleNPC(npc)
local humanoid: Humanoid = npc:FindFirstChild("Humanoid")
local root: BasePart = npc:FindFirstChild("HumanoidRootPart")
local neck: Motor6D = npc:FindFirstChild("Torso").Neck
local originalNeckC0 = neck.C0
if root:IsDescendantOf(workspace) then
for _, part in pairs(npc:GetDescendants()) do
if part:IsA("BasePart") then
part:SetNetworkOwner(nil)
end
end
end
if not humanoid or not root then
warn("NPC missing Humanoid or Root part: " .. npc.Name)
return
end
local statsFolder = npc:FindFirstChild("Statistics")
if not statsFolder then
warn("NPC missing Statistics folder: " .. npc.Name)
return
end
local npcStats = {
TargetDistance = statsFolder:WaitForChild("TargetDistance").Value,
StopDistance = statsFolder:WaitForChild("StopDistance").Value,
Damage = statsFolder:WaitForChild("Damage").Value,
AttackDistance = statsFolder:WaitForChild("AttackDistance").Value,
AttackCooldown = statsFolder:WaitForChild("AttackCooldown").Value
}
local lastAttack = tick()
task.spawn(function()
while humanoid and humanoid.Health > 0 do
for _, stat in pairs(statsFolder:GetChildren()) do
if stat:IsA("NumberValue") or stat:IsA("IntValue") then
npcStats[stat.Name] = stat.Value
end
end
task.wait(0.1)
end
end)
local animator: Animator = humanoid:WaitForChild("Animator")
local animations = npc:WaitForChild("Animations")
local idleAnimation = animations:WaitForChild("Idle")
local walkingAnimation = animations:WaitForChild("Walking")
local attackAnimation = animations:WaitForChild("Attack")
local idleTrack = animator:LoadAnimation(idleAnimation)
local walkingTrack = animator:LoadAnimation(walkingAnimation)
local attackTrack = animator:LoadAnimation(attackAnimation)
idleTrack:Play()
local function UpdateNPCBehavior()
local attackingTag = npc:FindFirstChild("AttackingTag").Value
local nearestNPC, distance, position = NPCFunctions.findNearestNPCWithTag(npc, attackingTag)
local modelSize = npc:GetExtentsSize()
local agentRadius = modelSize.X
local agentParams = {
AgentRadius = agentRadius,
AgentHeight = modelSize.Y,
AgentCanJump = false
}
if nearestNPC and distance < npcStats.TargetDistance and distance > npcStats.StopDistance and nearestNPC.Humanoid.Health > 0 then
local path = PathfindingService:CreatePath(agentParams)
path:ComputeAsync(root.Position, nearestNPC:GetPivot().Position)
if path.Status == Enum.PathStatus.Success then
TweenService:Create(neck, TweenInfo.new(.25, Enum.EasingStyle.Sine), {C0 = originalNeckC0}):Play()
local waypoints = path:GetWaypoints()
if not walkingTrack.IsPlaying then
walkingTrack:Play()
end
for _, waypoint: PathWaypoint in ipairs(waypoints) do
visualise(waypoint.Position)
humanoid:MoveTo(waypoint.Position)
while not humanoid.MoveToFinished do
task.wait()
end
task.wait()
end
elseif path.Status == Enum.PathStatus.NoPath then
NPCFunctions.lookat(position, root, distance, neck)
humanoid:MoveTo(root.Position)
walkingTrack:Stop()
end
elseif (nearestNPC and (distance <= npcStats.StopDistance or distance > npcStats.TargetDistance)) then
NPCFunctions.lookat(position, root, distance, neck)
humanoid:MoveTo(root.Position)
walkingTrack:Stop()
if not nearestNPC then
humanoid:MoveTo(root.Position)
walkingTrack:Stop()
end
end
if nearestNPC and distance <= npcStats.AttackDistance and tick() - lastAttack >= npcStats.AttackCooldown then
lastAttack = tick()
if humanoid and humanoid.Health > 0 and nearestNPC:FindFirstChild("Humanoid") and nearestNPC.Humanoid.Health > 0 then
NPCFunctions.lookat(position, root, distance, neck)
TweenService:Create(root, TweenInfo.new(.4, Enum.EasingStyle.Sine), {CFrame = CFrame.lookAt(root.Position, Vector3.new(nearestNPC.PrimaryPart.Position.X, root.Position.Y, nearestNPC.PrimaryPart.Position.Z))}):Play()
task.wait(.1)
attackTrack:Play()
task.defer(function()
DamageModule:Hurt(nearestNPC, npcStats.Damage, npc.Name)
end)
end
end
end
RunService.Heartbeat:Connect(function()
task.wait(.1)
if humanoid.Health > 0 then
task.wait(0.2)
UpdateNPCBehavior()
else
npc:Destroy()
end
end)
end
local function BindNPCsWithTag(tag)
for _, npc in pairs(CollectionService:GetTagged(tag)) do
print("Binding NPC: " .. npc.Name)
HandleNPC(npc)
end
CollectionService:GetInstanceAddedSignal(tag):Connect(function(npc)
print("New NPC added: " .. npc.Name)
HandleNPC(npc)
end)
end
BindNPCsWithTag(npcTag)