You can check when the zombie gets within the radius and raycast to check if it will walk through anything so you don’t have to make a new path
From the video it looks like the dummy is walking for a bit and then ending at a spot behind the character before resuming again. This is most likely caused by me getting the direction wrong. My bad! I can never keep it straight, which vector to subtract from the other. Just try replacing this line:
with this maybe?
local direction = (zombiePos - characterPos)
Let me know if it works!
I made the change you suggested and the AI seems to be making changes in its pathfinding a little faster and getting a little closer, but it still can’t catch up to me.
Try multiplying the direction by two or something then.
EDIT: Also, I noticed that the npc is stopping before resuming. This shouldn’t be happening. You should be updating the path or whatever enough that the npc always has a goal it’s working towards.
I multiplied the direction by two and this is the result. I can’t tell if anything is different, though I don’t have the sharpest eye when it comes to something like this, so maybe you can spot something I can’t.
As for the stopping and starting, I’m not sure what could be causing that. The script should be checking whether there is anything in the way of the bot and its target and then choosing a pathfinding solution accordingly for every cycle, and the loop runs via the Heartbeat function. I don’t understand how I could get the function to run any faster than that, or how it could be not finding pathways unless they were just straight up not possible, which shouldn’t be the case in all the videos I’ve posted.
I decided to look through the documentation and examples for SimplePath.
I think it’d be simpler if you simply used the path events rather than a loop. This is based on personal preference that’s based on experience, as the more logic you put into something the more difficult it is to understand. SimplePath is meant to make pathfinding simpler.
--Computes path again if something blocks the path
Path.Blocked:Connect(function()
Path:Run(Goal)
end)
--If the position of Goal changes at the next waypoint, compute path again
Path.WaypointReached:Connect(function()
Path:Run(Goal)
end)
--Computes path again if an error occurs
Path.Error:Connect(function(errorType)
Path:Run(Goal)
end)
Also, I think you might want to try replacing the Vector3
goal that you pass to Run
and replace it with the RootPart of the target character instead. So something like this:
local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local SP = require(SS:WaitForChild("SimplePath"))
local sprinter = script.Parent
local hum = sprinter:WaitForChild("Humanoid")
local HRP = sprinter:WaitForChild("HumanoidRootPart")
sprinter.PrimaryPart:SetNetworkOwner(nil) -- set sprinter network owner to server
local path = SP.new(sprinter)
path.Visualize = true
local names = {}
for _, name in pairs(RS:WaitForChild("Enemies"):GetChildren()) do
table.insert(names, name.Name)
end
function checkNames(query)
for _, name in pairs(names) do
if query == name then
return true
end
end
return false
end
function findTarget()
local aggro = 200
local target
local blocked = true
for _, character in pairs(workspace:GetChildren()) do
local human = character:FindFirstChild("Humanoid")
local RP = character:FindFirstChild("HumanoidRootPart")
if human and RP and human.Health > 0 and not checkNames(RP.Parent.Name) then
if (RP.Position - HRP.Position).Magnitude < aggro then
aggro = (RP.Position - HRP.Position).Magnitude
target = RP
local rayInfo = RaycastParams.new()
rayInfo.FilterType = Enum.RaycastFilterType.Blacklist
rayInfo.FilterDescendantsInstances = sprinter:GetChildren()
local hit = workspace:Raycast(HRP.Position, (RP.Position - HRP.Position).Unit * 200, rayInfo)
if hit.Instance == RP then
blocked = false
end
end
end
end
return target, blocked
end
local target: BasePart, heartbeat: RBXScriptConnection
heartbeat = game:GetService("RunService").Heartbeat:Connect(function()
target = findTarget()
if target then
heartbeat:Disconnect()
end
end)
-- Recompute Path:
local function recomputePath()
path:Run(target)
end
-- Event Connections:
path.Error:Connect(recomputePath)
path.Blocked:Connect(recomputePath)
path.WaypointReached:Connect(recomputePath)
Path.Reached:Connect(recomputePath)
path:Run(target) -- compute path
This may not solve your issue, but it may make it simpler to script the pathfinding. If it doesn’t solve, then please tell me. I have one more idea for how to fix it.
I tested your code just now and the AI doesn’t seem to be moving at all. Did you mean to put the initial “path:Run(target)” in the conditional in the Heartbeat function? I checked and it is getting inside that condition when I get in range, but it’s not doing anything after that.
I’m basing it off one of the examples the authors provided, so no. I edited the code a teeny bit now, but not much. If it doesn’t work then it just means the module isn’t working as intended. That would be a bad thing because it would mean the module is unreliable.
If it would be easier for you to diagnose, we could switch back to the default pathfinding system and recreate the script using it. I just tried out your revised code and I got the same result. The only reason I used SimplePath was that it was less complex and required fewer lines of code to run properly. At this rate, I’ll take any solution that works.
I had tried to use the object-oriented pathfinding (using an instance as the target instead of a vector3) starting out but it hadn’t really changed anything.
Use a while loop and change all the waits to task.wait
so it doesn’t yield. If you are still having trouble its probably because of your pathfinding method, not the loop.
I fixed this by setting my AI’s networkownership to the currently chasing player, No clue if it would work for you though.
This is actually a pretty good solution, but it is still vulnerable to exploiters.
I’ll just do some testing on my own later today and see if I can find one of my past projects. If I do I’ll let you know.
Hey, I’m having the same issue with the same module. I’ll put this conversation at watching because I REALLY need solutions to this.
Just so you know, I’m working on this as I speak. Sorry it took me a few days to get to it, I had to deal with other stuff in life. Nevertheless, this problem is sort of fun. It’s a break from the usual problems I deal with. Anyways, I have done some initial testing, and I should have a solution later tonight. It turns out I was on the right track earlier when I provided the getPathfindPoint
function, but I took a wrong turn, as well as I didn’t account for the actual latency between the client and server.
I’ll post in an hour or two with the actual solution. Just know that with it, it will definitely require you to centralize your system. However, it can also be done with a single script in a character for demonstration purposes.
EDIT: Never mind, it’s going to be until tomorrow until I complete this. I decided to go the perfectionist route and create a new module that has functionality above what is required to make this work. Oh well.
I’m having this issue as well. I’m beginning to think this is an engine bug…
It’s not an engine bug, It’s caused by latency between the client and the server. The problem doesn’t show up in studio unless you go to the network settings in the studio settings tab and set the incoming replication lag to something above 0.
Also, just updating to let everyone else know I’m still working on this. The problem turned out to be a bit more complex, but I’m on the right path now and can get the NPC within 2-3 studs of the player. I should have it completed either today or tomorrow.
EDIT: I am now able to predict the position of the player rather accurately whenever the player walks in a straight line. However, I am dealing with an issue whenever they are turning. So now I’m modeling the player walk path along a curve, something I’ve never done before. This may take a few more days.
Alright, I’ve completed my testing. I’ll update this reply later with the finished product. As for now, all I can say is this. I’ve found a method for predicting the future position of the player, but it only works with low latency/ping. If the player has a relatively high ping (around/above 100) there is no reliable way to predict their current position. The latency is just too great. With that said, it should still work with those of a lower ping.
This solution isn’t bad, just beware that giving network ownership of the character models to the player will allow them to do anything physics related to their body and it will replicate.
E.g WalkSpeed and JumpPower.
Now I’m no like genius I’m just trying to put in an effort or attempt to help
But really the latency happening is the functions you have, although yes run and execution time of the functions still happen very fast
I didn’t make very hard changes, just some I feel like would improve
local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local SP = require(SS:WaitForChild("SimplePath"))
local sprinter = script.Parent
local hum = sprinter:WaitForChild("Humanoid")
local HRP = sprinter:WaitForChild("HumanoidRootPart")
local path = SP.new(sprinter)
path.Visualize = true
local names = {}
for _, name in pairs(RS:WaitForChild("Enemies"):GetChildren()) do
table.insert(names, name.Name)
end
sprinter.PrimaryPart:SetNetworkOwner(nil)
local Target
local Blocked = true
local Distance
function findTarget()
local aggro = 200
local ThisTarget = nil
local ThisBlock = true
for _, character in pairs(workspace:GetChildren()) do
local human = character:FindFirstChild("Humanoid")
local RP = character:FindFirstChild("HumanoidRootPart")
if human and RP and human.Health > 0 and not table.find(names,RP.Parent.Name) then
local dist = (RP.Position - HRP.Position).Magnitude
if dist < aggro then
aggro = dist
ThisTarget = RP
local rayInfo = RaycastParams.new()
rayInfo.FilterType = Enum.RaycastFilterType.Blacklist
rayInfo.FilterDescendantsInstances = sprinter:GetChildren()
local hit = workspace:Raycast(HRP.Position, (RP.Position - HRP.Position).Unit * 200, rayInfo)
if hit.Instance == RP then
ThisBlock = false
end
end
end
end
Target = ThisTarget
Distance = (ThisTarget ~= nil and aggro) or nil
Blocked = (ThisTarget ~= nil and ThisBlock) or nil
end
function getPathfindingPosition(agent, target)
local agentPos = agent:GetPivot().Position
local targetPos = target:GetPivot().Position
local direction = (agentPos - targetPos)
local distance = direction.Magnitude
if distance < 5 then
local result = workspace:Raycast(targetPos, direction)
return if result then result.Position else (targetPos + direction)
elseif distance < 10 then
local humanoid = target.Parent.Humanoid
direction = (humanoid.MoveDirection*humanoid.WalkSpeed)
local result = workspace:Raycast(targetPos, direction)
return if result then result.Position else (targetPos + direction)
else
return targetPos
end
end
game:GetService("RunService").Heartbeat:Connect(function()
coroutine.resume(coroutine.create(function()
findTarget()
end))
if Target then
if not Blocked then
print("not blocked")
hum:MoveTo(getPathfindingPosition(sprinter, target), workspace.Terrain)
elseif Blocked then
path:Run(getPathfindingPosition(sprinter, target))
end
end
end)
You could give this try, obviously keep the code you have currently, as this is only just trial and error at this point and having older versions could lead to a solution
I also recommend maybe
when it does hum:MoveTo()
to just take the Target and do
local cf = Target.CFrame*CFrame.new(0,0,-3)
hum:MoveTo(cf.Position)