The problem
My AI is not working very well ,sometimes she starts walking slowly (after killing a player), and sometimes she gets stuck on a wall.
local teddy = script.Parent
local humanoid = teddy:WaitForChild("Humanoid")
local root = teddy:WaitForChild("HumanoidRootPart")
local animFolder = teddy:WaitForChild("Anims")
local pathService = game:GetService("PathfindingService")
local players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
teddy.PrimaryPart:SetNetworkOwner(nil)
-- Sons
local chaseSound = teddy:FindFirstChild("Chase")
-- Som passos
local footstepsSound = root:FindFirstChild("Footsteps")
if not footstepsSound then
footstepsSound = Instance.new("Sound")
footstepsSound.Name = "Footsteps"
footstepsSound.SoundId = "rbxassetid://128581675687648"
footstepsSound.Parent = root
end
footstepsSound.MaxDistance = 90
footstepsSound.EmitterSize = 10
footstepsSound.RollOffMode = Enum.RollOffMode.Linear
footstepsSound.Volume = 10
footstepsSound.Looped = true
local isFootstepsPlaying = false
local isInteracting = false
local function updateFootstepsSound()
local speed = root.Velocity.Magnitude
if speed > 1 then
if not isFootstepsPlaying then
footstepsSound:Play()
isFootstepsPlaying = true
end
else
if isFootstepsPlaying then
footstepsSound:Stop()
isFootstepsPlaying = false
end
end
end
-- Waypoints
local allWaypoints = workspace:WaitForChild("waypoints"):GetChildren()
local availableWaypoints = {}
local patrolSpeed = 18
local chaseSpeed = 22
local closeRangeSpeed = 10
-- Anti-stuck
local lastPos = root.Position
local stuckTime = 0
local stuckThreshold = 2
local minMoveDistance = 1
local currentTarget = nil
local lastKnownPos = nil
local targetLostTime = 0
local targetLoseDelay = 3
-- Funções de waypoint
local function resetAvailableWaypoints()
availableWaypoints = {}
for _, point in ipairs(allWaypoints) do
table.insert(availableWaypoints, point)
end
for i = #availableWaypoints, 2, -1 do
local j = math.random(1, i)
availableWaypoints[i], availableWaypoints[j] = availableWaypoints[j], availableWaypoints[i]
end
end
resetAvailableWaypoints()
local function getNextWaypoint()
if #availableWaypoints == 0 then
resetAvailableWaypoints()
end
return table.remove(availableWaypoints)
end
-- Função de visão
local function canSeeTarget(target)
if not target or not target:FindFirstChild("HumanoidRootPart") then return false end
local origin = root.Position
local maxSeeDistance = 150
local direction = (target.HumanoidRootPart.Position - origin)
if direction.Magnitude > maxSeeDistance then
return false
end
direction = direction.Unit * maxSeeDistance
local ray = Ray.new(origin, direction)
local hit = workspace:FindPartOnRay(ray, teddy)
return hit and hit:IsDescendantOf(target)
end
local function findTarget()
local maxDistance = 150
local nearestTarget = nil
-- Jogadores
for _, player in pairs(players:GetPlayers()) do
local char = player.Character
if char and char:FindFirstChild("Humanoid") and char:FindFirstChild("HumanoidRootPart") then
if char.Humanoid.Health > 0 and char:GetAttribute("Invisible") ~= true then
local dist = (root.Position - char.HumanoidRootPart.Position).Magnitude
if dist <= maxDistance and canSeeTarget(char) then
maxDistance = dist
nearestTarget = char
end
end
end
end
-- DummyBait
for _, model in pairs(workspace:GetChildren()) do
if model:IsA("Model") and model ~= teddy and model:FindFirstChild("Humanoid") and model:FindFirstChild("HumanoidRootPart") then
if model:GetAttribute("DummyBait") == true then
local dist = (root.Position - model.HumanoidRootPart.Position).Magnitude
if dist <= maxDistance and canSeeTarget(model) then
maxDistance = dist
nearestTarget = model
end
end
end
end
return nearestTarget
end
-- Pontos exclusivos com rotação fixa para todos os pontos 1-10
local exclusivePoints = {
["Point1"] = Vector3.new(1,0,0),
["Point2"] = Vector3.new(-1,0,0),
["Point3"] = Vector3.new(0,0,1),
["Point4"] = Vector3.new(1,0,1),
["Point5"] = Vector3.new(-1,0,1),
["Point6"] = Vector3.new(0,0,-1),
["Point7"] = Vector3.new(1,0,0),
["Point8"] = Vector3.new(-1,0,-1),
["Point9"] = Vector3.new(0,0,1),
["Point10"] = Vector3.new(0,0,1),
}
-- Função de interação com animação de 10s
local function playAnimationFor(pointName)
local pointNum = string.match(pointName, "Point(%d+)")
local animName = "Interact" .. pointNum
local soundName = "InteractSound" .. pointNum
local animObj = animFolder:FindFirstChild(animName)
local soundObj = teddy:FindFirstChild(soundName)
local point = workspace:WaitForChild("waypoints"):FindFirstChild(pointName)
if animObj and point then
humanoid.WalkSpeed = 0
isInteracting = true
humanoid:MoveTo(point.Position)
humanoid.MoveToFinished:Wait()
-- Rotação usando exclusivePoints de todos os pontos
if exclusivePoints[pointName] then
local lookVector = exclusivePoints[pointName]
root.CFrame = CFrame.new(root.Position, root.Position + lookVector)
end
-- Toca som de interação
if soundObj and soundObj:IsA("Sound") then
soundObj:Play()
end
-- Toca animação
local anim = humanoid:LoadAnimation(animObj)
anim:Play()
-- Espera 10 segundos antes de parar
local elapsed = 0
local duration = 10
while elapsed < duration do
local delta = RunService.Heartbeat:Wait()
elapsed = elapsed + delta
updateFootstepsSound()
end
anim:Stop()
isInteracting = false
humanoid.WalkSpeed = patrolSpeed
else
warn("Animação ou ponto não encontrados:", animName, pointName)
end
end
-- Função de ataque com jumpscare primeiro, depois animação
local function attackTarget(target)
if isInteracting or not target or not target:FindFirstChild("HumanoidRootPart") then return end
local dist = (root.Position - target.HumanoidRootPart.Position).Magnitude
if dist > 8 then
humanoid.WalkSpeed = chaseSpeed
humanoid:MoveTo(target.HumanoidRootPart.Position)
if not target:GetAttribute("DummyBait") then
if chaseSound and not chaseSound.IsPlaying then
chaseSound:Play()
end
end
else
humanoid.WalkSpeed = 0 -- NPC fica parado
-- Dispara jumpscare e mata jogador imediatamente
if players:GetPlayerFromCharacter(target) then
local player = players:GetPlayerFromCharacter(target)
target.Humanoid.Health = 0
local jumpscareEvent = ReplicatedStorage:FindFirstChild("JumpscareEventGrandpa")
if jumpscareEvent then
jumpscareEvent:FireClient(player)
end
end
-- Para o chaseSound imediatamente
if chaseSound and chaseSound.IsPlaying then
chaseSound:Stop()
end
-- Reseta alvo e posição antiga para não travar
currentTarget = nil
lastKnownPos = nil
targetLostTime = 0
-- Toca animação de ataque visual
local attackAnim = Instance.new("Animation")
attackAnim.AnimationId = "rbxassetid://122003013927263"
local animTrack = humanoid:LoadAnimation(attackAnim)
animTrack:Play()
animTrack.Stopped:Wait() -- espera terminar visual
humanoid.WalkSpeed = patrolSpeed
end
end
-- Caminhar usando Pathfinding
local function walkTo(destination)
local path = pathService:CreatePath()
path:ComputeAsync(root.Position, destination.Position)
if path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
for _, waypoint in ipairs(waypoints) do
humanoid:MoveTo(waypoint.Position)
local reached = false
local connection = humanoid.MoveToFinished:Connect(function(r)
reached = true
end)
local elapsed = 0
local timeout = 3
while not reached and elapsed < timeout do
RunService.Heartbeat:Wait()
elapsed = elapsed + 0.1
updateFootstepsSound()
local target = findTarget()
if target then
connection:Disconnect()
attackTarget(target)
return true
end
end
connection:Disconnect()
end
else
humanoid:MoveTo(destination.Position)
end
return false
end
-- Anti-stuck aprimorado
local function antiStuck()
local distMoved = (root.Position - lastPos).Magnitude
if distMoved < minMoveDistance then
stuckTime = stuckTime + 0.5
else
stuckTime = 0
end
lastPos = root.Position
if stuckTime >= stuckThreshold then
warn("NPC preso! Tentando corrigir...")
stuckTime = 0
-- Tenta mover para um waypoint aleatório próximo
local safePoint = getNextWaypoint()
if safePoint then
humanoid.WalkSpeed = patrolSpeed
humanoid:MoveTo(safePoint.Position)
end
-- Caso continue preso, teleporta levemente para frente
local forward = root.CFrame.LookVector
root.CFrame = root.CFrame + forward * 5
humanoid:MoveTo(root.Position)
end
end
-- Atualiza alvo
local function updateTarget()
local found = findTarget()
if found then
currentTarget = found
lastKnownPos = found.HumanoidRootPart.Position
targetLostTime = 0
else
if currentTarget then
if currentTarget:FindFirstChild("Humanoid") and currentTarget.Humanoid.Health > 0 then
targetLostTime = targetLostTime + 0.25
else
currentTarget, lastKnownPos, targetLostTime = nil, nil, 0
end
if targetLostTime >= targetLoseDelay then
currentTarget, lastKnownPos, targetLostTime = nil, nil, 0
end
end
end
end
-- Loop principal
while true do
wait(0.25)
updateFootstepsSound()
antiStuck()
updateTarget()
if currentTarget then
if canSeeTarget(currentTarget) then
attackTarget(currentTarget)
else
if lastKnownPos then
humanoid.WalkSpeed = chaseSpeed
humanoid:MoveTo(lastKnownPos)
end
end
elseif lastKnownPos then
humanoid.WalkSpeed = chaseSpeed
humanoid:MoveTo(lastKnownPos)
lastKnownPos = nil
else
humanoid.WalkSpeed = patrolSpeed
if chaseSound and chaseSound.IsPlaying then
chaseSound:Stop()
end
local point = getNextWaypoint()
if point then
local interrupted = walkTo(point)
if not interrupted and point.Name ~= "POINTT" then
playAnimationFor(point.Name)
end
end
end
end