I new at using collection service, so i need help…
function setupNpc(npc: Model)
--start AI loop for this NPC
--other NPC stuff
end
function cleanupNpc(npc: Model)
--free resources used by the NPC
end
for _, tagged in TagS:GetTagged(NPC_TAG) do
setupNpc(tagged)
end
TagS:GetInstanceAddedSignal:Connect(setupNpc)
TagS:GetInstanceRemovedSignal:Connect(cleanupNpc)
thank you, i will try. =) -------
i used this script but i got a problem. In theory, two NPCs should reach the point, but it turns out that one NPC reaches the middle of the path and stops, then the second goes to the middle of the path and stops, and the first goes to the point. How to fix it?(Used translator)
Script:
local Tags = game:GetService("CollectionService")
local PathfindingService = game:GetService("PathfindingService")
local plrinserver = false
game.Players.PlayerAdded:Connect(function() -- i use this thing because, script can`t find player, and my game is load +-10 seconds
wait(10)
plrinserver = true
end)
local distance = 999
function setupNpc(npc)
print("settuping..")
--start AI loop for this NPC
--other NPC stuff
local target
-- detect distance between players and npc
for i,p in ipairs(game.Players:GetChildren()) do
if p:IsA("Player") then
local dis = (p.Character:WaitForChild("HumanoidRootPart").Position - npc.HumanoidRootPart.Position).Magnitude
print(dis)
if dis <= distance then
target = p
print("nearest player: "..tostring(target))
end
end
end
if target ~= nil then -- make npc walk to nearest player
local path = PathfindingService:CreatePath()
path:ComputeAsync(npc.HumanoidRootPart.Position, target.Character:WaitForChild("HumanoidRootPart").Position) -- Change "workspace.Part.Position" to your part
local waypoints = path:GetWaypoints()
for i, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
npc.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
end
npc.Humanoid:MoveTo(waypoint.Position)
npc.Humanoid.MoveToFinished:Wait()
end
end
end
function cleanupNpc(npc)
--free resources used by the NPC
end
Tags:GetInstanceAddedSignal("NpcAI"):Connect(setupNpc)
Tags:GetInstanceRemovedSignal("NpcAI"):Connect(cleanupNpc)
while wait() do
if plrinserver == true then
for _, tagged in Tags:GetTagged("NpcAI") do
setupNpc(tagged)
end
end
end
settuping isnt a real english word btw, its just “setting up” :]
Pathfinding service just tends to do this, I personally if you want good and responsive NPCs I would use Vector3s and :MoveTo() to move NPCs. Pathfinding in general isnt effective and many developers have complained about its poor functionality- but youre on the right track!
This is almost certainly not what you want. Consider what happens the second time the loop runs when there’s a player in the server: setupNpc gets called again per NPC… and again and again. It should only be called once per NPC in it’s entire lifetime. Every time you call it, some resources are taken up. So you use e.g. more and more memory over time. This is called a memory leak and will “mysteriously” make your game run slower after 10 minutes, or 10 hours, and can be really hard to debug so it’s important to be mindful not to introduce memory leaks.
A fix could look like
while wait() do
if plrinserver == true then
for _, tagged in Tags:GetTagged("NpcAI") do
setupNpc(tagged)
end
end
break
end
or
while not plrinserver do
wait()
end
for _, tagged in Tags:GetTagged("NpcAI") do
setupNpc(tagged)
end
or
game.Players.PlayerAdded:Once(function()
-- Because script can`t find player, and my game is load +-10 seconds
wait(10)
for _, tagged in Tags:GetTagged("NpcAI") do
setupNpc(tagged)
end
end)
The last one is best IMO because you don’t need a global variable, and all the logic is contained in a single block and doesn’t “mix” very much with any other logic.
The idea is to create a logic loop for each NPC in the setup function, something like
local TagS = game:GetService("CollectionService")
local PathfindingS = game:GetService("PathfindingService")
local aggroRange = 999
function findPlayerCFrame(player: Player): CFrame?
if not player.Character then
return nil
end
return player.Character:GetPivot()
end
function findNearestPlayer(npc: Model): (Player, number)
local nearest, nearestDistance = nil, math.huge
local npcPosition = npc:GetPivot().Position
for _, player in game.Players:GetPlayers() do
local playerCF = findPlayerCFrame(player)
if not playerCF then continue end
local playerPos = playerCF.Position
local playerDistance = (playerPos - npcPosition).Magnitude
if playerDistance <= nearestDistance then
nearest, nearestDistance = player, playerDistance
end
end
return nearest, nearestDistance
end
function followPath(npc: Model, path: Path)
local humanoid = npc:FindFirstChildWhichIsA("Humanoid")
local waypoints = path:GetWaypoints()
for _, waypoint in waypoints do
if waypoint.Action == Enum.PathWaypointAction.Jump then
humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
end
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
end
function setupNpc(npc)
print(`settuping {npc:GetFullPath()}`)
-- Start AI loop for this NPC
while wait(1) do
--"Clean up" this infinite loop when the NPC should be cleaned up
if not TagS:HasTag(npc, "NpcAI") then break end
-- Target the nearest player in range
local target, targetDistance = findNearestPlayer(npc)
local targetCF = findPlayerCFrame(target)
if targetDistance > aggroRange then
target = nil
end
if not target then continue end
if not targetCF then continue end
-- Walk to target
local pathToTarget = PathfindingS:CreatePath()
pathToTarget:ComputeAsync(npc:GetPivot().Position, targetCF.Position)
followPath(npc, pathToTarget)
end
end
function cleanupNpc(npc)
--free resources used by the NPC
end
TagS:GetInstanceAddedSignal("NpcAI"):Connect(setupNpc)
TagS:GetInstanceRemovedSignal("NpcAI"):Connect(cleanupNpc)
game.Players.PlayerAdded:Once(function()
-- Because script can`t find player, and my game is load +-10 seconds
wait(10)
for _, tagged in TagS:GetTagged("NpcAI") do
setupNpc(tagged)
end
end)
I applied 3 loop options and one big script:
- NPCs don’t move at all
- worked 50%, they stop on the spot and I know why. The problem is that the script must always process the position of the player, player cannot always stand still.
- Same thing
(applied on mine and on yours)
Big script: same
(Used translator)
I found a script and remake it, it works, but it have bugs.
When npcs touch parts that heigher than npc, they stops.
Script:
local Tags = game:GetService("CollectionService")
local pathfindingService = game:GetService("PathfindingService")
function findNearestTorso(pos,npc)
local list = game.Workspace:GetChildren()
local torso = nil
local dist = 10000
local temp = nil
local human = nil
local temp2 = nil
for x = 1, #list do
temp2 = list[x]
if (temp2.className == "Model") and (temp2 ~= npc) and (temp2.Name ~= "Zombie") then
temp = temp2:findFirstChild("HumanoidRootPart")
human = temp2:findFirstChild("Humanoid")
if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
if (temp.Position - pos).Magnitude < dist then
torso = temp
dist = (temp.Position - pos).Magnitude
end
end
end
end
return torso
end
function pathfind2(Target,Hum,hroot)
local pfs = game:GetService("PathfindingService")
local enemytorso = Target
local human = Hum
local direct = Vector3.FromNormalId(Enum.NormalId.Back)
local ncf = hroot.CFrame * CFrame.new(direct)
local connection;
direct = ncf.p
local path = pfs:CreatePath()
path:ComputeAsync(direct,enemytorso.Position)
local waypoint = path:GetWaypoints()
for index, WayPoint in ipairs(waypoint) do
human:MoveTo(WayPoint.Position);
if index > 2 then return end
end
end
function checkcanpath(torso2,hroot)
local rute = pathfindingService:CreatePath()
rute:ComputeAsync(hroot.Position, torso2.Position)
local rutePoints = rute:GetWaypoints()
if #rutePoints > 0 then
return true
else
return false
end
end
function GetPlayersBodyParts(t)
local torso = t
if torso then
local figure = torso.Parent
for _, v in pairs(figure:GetChildren()) do
if v:IsA'Part' then
return v.Name
end
end
else
return "HumanoidRootPart"
end
end
function getHumanoid(model)
for _, v in pairs(model:GetChildren()) do
if v:IsA'Humanoid' then
return v
end
end
end
function setupNpc(npc)
npc.HumanoidRootPart:SetNetworkOwner(nil)
local zombie = npc
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local target = findNearestTorso(npc.HumanoidRootPart.Position,npc)
if target ~= nil then
pathfind2(target,human,hroot)
end
end
function cleanupNpc(npc)
--free resources used by the NPC
end
--Tags:GetInstanceAddedSignal("NpcAI"):Connect(setupNpc)
--Tags:GetInstanceRemovedSignal("NpcAI"):Connect(cleanupNpc)
--for _, tagged in Tags:GetTagged("NpcAI") do
-- setupNpc(tagged)
--end
while true do
wait()
for _, tagged in Tags:GetTagged("NpcAI") do
setupNpc(tagged)
end
end
Bugs:
one thing is, you are trying to access the ‘ClassName’ property, however you tried to access it as ‘className’ which does not work.
second, why don’t you just iterate over the list than use a for loop for counting
i’ve cleaned up your code a bit
local collectionService = game:GetService("CollectionService")
local pathfindingService = game:GetService("PathfindingService")
function findNearestTorso(pos,npc)
local list = workspace:GetChildren()
local torso
local dist = 10000
local temp
local human
local temp2
for _, temp2 in list do
if not temp2:IsA("Model") or not (temp2 ~= npc) or not (temp2.Name ~= "Zombie") then continue end
temp = temp2:FindFirstChild("HumanoidRootPart")
human = getHumanoid(temp2)
if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
if (temp.Position - pos).Magnitude < dist then
torso = temp
dist = (temp.Position - pos).Magnitude
end
end
end
return torso
end
function pathfind2(Target,Hum,hroot)
local enemytorso = Target
local direct = Vector3.FromNormalId(Enum.NormalId.Back)
local ncf = hroot.CFrame * CFrame.new(direct)
direct = ncf.Position
local path = pathfindingService:CreatePath()
path:ComputeAsync(direct,enemytorso.Position)
local waypoints = path:GetWaypoints()
for index, waypoint in waypoints do
Hum:MoveTo(waypoint.Position)
if index > 2 then break return end
end
end
function checkcanpath(torso2, hroot)
local route = pathfindingService:CreatePath()
route:ComputeAsync(hroot.Position, torso2.Position)
local waypoints = route:GetWaypoints()
return #waypoints > 0
end
function GetPlayersBodyParts(torso)
if torso then
local figure = torso.Parent
for _, v in figure:GetChildren() do
if not v:IsA("BasePart") then continue end
return v.Name
end
else
return "HumanoidRootPart"
end
end
function getHumanoid(model)
if not model:FindFirstChildWhichIsA("Humanoid") then return nil end
return model:FindFirstChildWhichIsA("Humanoid")
end
function setupNpc(npc)
npc.HumanoidRootPart:SetNetworkOwner(nil)
local zombie = npc
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local target = findNearestTorso(npc.HumanoidRootPart.Position,npc)
if target then
pathfind2(target,human,hroot)
end
end
--function cleanupNpc(npc)
--free resources used by the NPC
--end
--collectionService:GetInstanceAddedSignal("NpcAI"):Connect(setupNpc)
--collectionService:GetInstanceRemovedSignal("NpcAI"):Connect(cleanupNpc)
coroutine.wrap(function()
while task.wait(1) do
for _, tagged in collectionService:GetTagged("NpcAI") do
setupNpc(tagged)
end
end
end)()