How can i have a lot of ai npc only run on a single master script

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I am making a game where ai npc battle each other with random weapons, the problem is that handling a lot of npc causes a lot of lag, and i thought about using a single script that will handle each npc.
  2. What is the issue? Include screenshots / videos if possible!
    i dont know how i can implement this
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I tried looking for something similar and found about using collection service but i have no idea how can i use it

heres the code i tried so far

-------Services-------
local AssetFolder = game.ReplicatedStorage:WaitForChild("Assets")
local PathFindingService = game:GetService("PathfindingService")
local AiModule = require(AssetFolder.Modules.AiModule)
local Tags = game:GetService("CollectionService")
local PathfindingService = game:GetService("PathfindingService")
------Parameters---------
local Bot_Tag = "Bots"
local viewRange = 30
local fieldOfView = 70
local Path = PathFindingService:CreatePath(
	{

		AgentRadius = 4;
		AgentHeight = 5;
		AgentCanJump = true;
		Costs = {
			FreeZone = math.huge
		};
	}
)

function FollowPosition(Position : Vector3,waypoints, BlockConnection, NextwaypointIndex, ReachConnection, Humanoid)
	local Sucsess, ErrorMessage = pcall(function()
		Path:ComputeAsync(script.Parent.HumanoidRootPart.Position, Position)
	end)

	if Sucsess and Path.Status == Enum.PathStatus.Success then
		waypoints = Path:GetWaypoints()
		BlockConnection = Path.Blocked:Connect(function(blockedwaypointindex)
			if blockedwaypointindex > NextwaypointIndex then
				BlockConnection:Disconnect()
				FollowPosition(Position, waypoints, BlockConnection, NextwaypointIndex, ReachConnection)
			end
		end)

		if not ReachConnection then
			ReachConnection = Humanoid.MoveToFinished:Connect(function(Reached)
				if Reached and NextwaypointIndex < #waypoints then
					NextwaypointIndex += 1
					Humanoid:MoveTo(waypoints[NextwaypointIndex].Position)
				end
			end)
		end
		NextwaypointIndex = 2
		Humanoid:MoveTo(waypoints[NextwaypointIndex].Position)
		task.wait()
	end
end


function setupNpc(Bot)
	print("Test Loop")
	--start AI loop for this NPC
	--other NPC stuff
	spawn(function()
		
		local waypoints
		local NextwaypointIndex
		local ReachConnection
		local BlockConnection
		
		while task.wait() do
			
			
			
			local Target = AiModule:FindTargetForBot(Bot)

			if Target then
				Bot.Data.Target.Value = Target
				FollowPosition(Target:FindFirstChild("HumanoidRootPart").Position, waypoints, BlockConnection, ReachConnection, Target:FindFirstChild("Humanoid"))
			end
		end
	end)
end

function cleanupNpc(Bot)
	--free resources used by the NPC
end

game.ReplicatedStorage.Remotes.Server.SetupAi.Event:Connect(function()
	for _, Bot in ipairs(workspace.Bots:GetChildren()) do
		setupNpc(Bot)
	end
	Tags:GetInstanceAddedSignal(setupNpc)
	Tags:GetInstanceRemovedSignal(cleanupNpc)

end)

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

1 Like

This works for me with about 100 - 200 pathfinding npcs before server physics lag hits (i use single part npcs with clientside models nowadays but the code is practically the same), there is probably better ways to do this

(you can also add extra checks such as an AI value/tag to have different types of NPCs at the same time)

while task.wait(0.2) do --i usually keep this at 0.25 or 0.3, less updates = more performance
	local Success,Error = pcall(function()
		for i,Model in pairs(workspace:GetChildren()) do
			if Model:IsA("Model") then
				--Check if model is a player's character
				local IsPlayer = game.Players:GetPlayerFromCharacter(Model)
				if not IsPlayer then
					--Check if it has a humanoid and if it's alive
					local Humanoid = Model:FindFirstChildOfClass("Humanoid")
					if Humanoid and Humanoid.Health > 0 then
						task.spawn(function() --so it goes through faster
							--Get Nearest
							--Pathfinding
							--Other stuff
						end)
					end
					--
				end
				--
			end
		end
	end)
	if not Success then warn(script.Name.." Error: "..Error) end --I usually just comment this and ignore errors until it becomes an issue
end

This is on 1 server script which loops through workspace for humanoids that aren’t players, I don’t know if it suits your needs though

you could also add another loop for differing weapon cooldowns/firerates when the npc finds a target (of course debouncing it so it doesn’t repeat the loop 10+ times)

1 Like

If you are encountering lag. Condensing it all to a single script will likely not help unless you are able to share the values of some expensive functions between multiple NPCs. Ultimately if you run it in a single script you are either handling the dispatching of your code yourself (technically could have a minor benefit, but probably negligible) or you are having Roblox schedule your code (separate scripts or task.spawn and similar will do this).

You need to find a way to do less work in your scripts (like maybe pathfind less often) or find a way to reduce what Roblox has to do.

Parallellizing can help too if you want to look into parallel luau

3 Likes

thank you very much for the information, i will try doing that

3 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.