I’m working on AI for enemies in my game. However, with terrain, it seems to break the pathfinding.
Below is a video of when the pathfinding error occurs. Note that this doesn’t happen and the pathfinding is fine on any flat surface, if I place a floor as a part, the pathfinding works.
Below is the entire code for my skeleton. This includes animations & sounds.
local npc = script.Parent
local human = npc.Humanoid
local rootPart = script.Parent.HumanoidRootPart
local PFS = game:GetService("PathfindingService")
local RUNSERVICE = game:GetService("RunService")
local PLAYERS = game:GetService("Players")
local animator = human:FindFirstChildOfClass("Animator")
local attackAnimation1 = human:LoadAnimation(game.ReplicatedStorage.Animations.Enemy.Skeleton:WaitForChild("attack1"))
local deathAnimation1 = human:LoadAnimation(game.ReplicatedStorage.Animations.Enemy.Skeleton:WaitForChild("death1"))
local runAnimation1 = human:LoadAnimation(game.ReplicatedStorage.Animations.Enemy.Skeleton:WaitForChild("run1"))
local idleAnimation1 = human:LoadAnimation(game.ReplicatedStorage.Animations.Enemy.Skeleton:WaitForChild("idle1"))
local stuns = game.ReplicatedStorage.Animations.Enemy.Skeleton.Stuns:GetChildren()
local damage = 50
local lastHealth = human.Health
local hitboxL = npc:FindFirstChild("HitboxL")
local hitboxR = npc:FindFirstChild("HitboxR")
local deathSound = script.Parent.HumanoidRootPart.Death
local attackSounds = script.Parent.Jaw:GetChildren()
local trailL = npc.TrailL.Trail
local trailR = npc.TrailR.Trail
local bones = npc.HumanoidRootPart.Bones
local D = true
local SD = true
local enemymodel = npc:WaitForChild("HumanoidRootPart")
npc.PrimaryPart:SetNetworkOwner(nil)
--Hit Sounds
local hit1 = script.Parent.HumanoidRootPart.HitBone1
local hit2 = script.Parent.HumanoidRootPart.HitBone2
local hit3 = script.Parent.HumanoidRootPart.HitBone3
local hits = {
hit1,
hit2,
hit3
}
--Stun Settings
local function playRandomHitSound()
if #hits > 0 then
local randomSound = hits[math.random(1, #hits)]
randomSound:Play()
end
end
local function playRandomAttack()
if #attackSounds > 0 and CD == true and human.Health > 0 then
local randomAttack = attackSounds[math.random(1, #attackSounds)]
randomAttack:Play()
end
end
local function onFirstKeyframeReached1()
trailL.Enabled = true
trailR.Enabled = true
CD = true
playRandomAttack()
end
local function onSecondKeyframeReached1()
trailL.Enabled = false
trailR.Enabled = false
CD = false
end
local function onKeyframeReached()
bones:Play()
end
attackAnimation1:GetMarkerReachedSignal("damage1"):Connect(onFirstKeyframeReached1)
attackAnimation1:GetMarkerReachedSignal("damage2"):Connect(onSecondKeyframeReached1)
deathAnimation1:GetMarkerReachedSignal("bones1"):Connect(onKeyframeReached)
local function onPartTouched1(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
if CD == true then
humanoid:TakeDamage(damage)
CD = false
end
end
end
local function onPartTouched2(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
if CD == true then
humanoid:TakeDamage(damage)
CD = false
end
end
end
hitboxL.Touched:Connect(onPartTouched1)
hitboxR.Touched:Connect(onPartTouched2)
local function findTarget()
local players = PLAYERS:GetPlayers()
local nearesttarget
local maxDistance = 50 -- distance
for i, player in pairs(players) do
if player.Character then
local target = player.Character
local distance = (npc.HumanoidRootPart.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude
if distance < maxDistance then
nearesttarget = target
maxDistance = distance
end
end
end
return nearesttarget
end
local function getPath(destination)
local path = PFS:CreatePath()
path:ComputeAsync(npc.HumanoidRootPart.Position, destination)
return path
end
local function pathFindTo(destination)
local path = getPath(destination)
local target = findTarget()
if target and target.Humanoid.Health > 0 then
for i, waypoint in pairs(path:GetWaypoints()) do
human:MoveTo(waypoint.Position)
human.MoveToFinished:Wait()
end
end
end
local function onDeath()
-- Find the player who killed the NPC
local killer = human:FindFirstChildOfClass("ObjectValue")
if killer and killer.Name == "creator" and killer.Value then
local player = killer.Value
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local fame = leaderstats:FindFirstChild("Fame")
if fame then
fame.Value = fame.Value + 10
end
local Gold = leaderstats:FindFirstChild("Gold")
if Gold then
Gold.Value = Gold.Value + 3
end
end
end
end
human.Died:Connect(onDeath)
RUNSERVICE.Heartbeat:Connect(function()
local target = findTarget()
if target then
local stopDistance = 3 -- Stop 5 studs away from target
local targetPosition = target:WaitForChild("HumanoidRootPart").Position
local directionToTarget = (targetPosition - npc.HumanoidRootPart.Position).Unit
local stopPosition = targetPosition - directionToTarget * stopDistance
local distanceToTarget = (npc.HumanoidRootPart.Position - targetPosition).Magnitude
if distanceToTarget <= 5 and D == true and human.Health > 0 then
npc:SetPrimaryPartCFrame(CFrame.lookAt(npc.HumanoidRootPart.Position, targetPosition))
human.WalkSpeed = 0
attackAnimation1:Play()
D = false
task.wait(3)
D = true
human.WalkSpeed = 10
else
human.WalkSpeed = 10
pathFindTo(stopPosition)
end
end
end)
local function onNPCDied()
local Eye1 = script.Parent.Accessories.EyeL.Glow
local Eye2 = script.Parent.Accessories.EyeR.Glow
local Glow = script.Parent.Accessories.Glow.PointLight
rootPart.Anchored = true
deathSound:Play()
Eye1.Enabled = false
Eye2.Enabled = false
Glow.Enabled = false
deathAnimation1:Play()
task.wait(deathAnimation1.Length)
npc:Destroy()
end
--Stun Settings
local function playRandomStunAnimation()
if #stuns > 0 and SD == true then
local randomStun = animator:LoadAnimation(stuns[math.random(1, #stuns)])
SD = false
randomStun:Play()
human.WalkSpeed = 0
human.JumpPower = 0
playRandomHitSound()
task.wait(.4)
SD = true
human.WalkSpeed = 10
human.JumpPower = 45
end
end
human.HealthChanged:Connect(function(health)
if health < lastHealth then
playRandomStunAnimation()
end
lastHealth = health
end)
--End Death
human.Died:Connect(onNPCDied)
What are some solutions would you think would help with this glitch? Should I move away from terrain? If so, what are some good ways to create worlds with parts?