Creating a simple AI

Hi everyone, i’m currently trying to create a simple AI like the one in Retail Tycoon where NPCs will walk around a definied map and eventually look at some shelf, paints and so on.

The problem is that i almost have 0 experience in AI and Pathfinding and the tutorials on Youtube or Devforum don’t really help.

This is the script i’ve made so far and works fine with only 1 NPC active

	NPC = Workspace.NPC.NPC1
	NPC_Humanoid = NPC.Humanoid



	Destinations = workspace.Waypoints:GetChildren()


	Path = PS:CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = false,
	})
	Path:ComputeAsync(NPC.HumanoidRootPart.Position,Destinations[math.random(1,#Destinations)].Position)
	Waypoint = Path:GetWaypoints()

	NextWaypoint = NPC_Humanoid.MoveToFinished:Connect(function(reached)
		if i < #Waypoint then
			NPC_Humanoid:MoveTo(Waypoint[i].Position)
			i += 1
		else
			Path:ComputeAsync(NPC.HumanoidRootPart.Position,Destinations[math.random(1,#Destinations)].Position)
			Waypoint = Path:GetWaypoints()
			i=1
			NPC_Humanoid:MoveTo(Waypoint[i].Position)
		end
	end)

	i = 1
	NPC_Humanoid:MoveTo(Waypoint[i].Position)

the problem is when i try to add more NPCs, the Waypoint output will simply be an empty table and will compute the waypoints for only the first one

for i,v in ipairs(workspace.NPC:GetChildren()) do

	NPC_Humanoid = v.Humanoid
	Destinations = workspace.Destinations:GetChildren()
	
	Path = PS:CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = false,
	})
	
	Path:ComputeAsync(v.HumanoidRootPart.Position,Destinations[1].Position)

	Waypoint = Path:GetWaypoints()
	print(Waypoint)
end

Output will be

{…} (Table of positions)
{} (Just nothing)

Does anyone know how to fix or where to start to create something like this?
Thanks in advance

Here is what I recommend doing, using coroutines for every NPC so that each and every NPC can run at the same time with one script. Here is a short script that effectively uses it:

I do think the reason why the waypoint table was empty was a localization error but hopefully this helps with your understanding!

First we get the variables:

local NPC_Folder = -- The foder of NPC's you want to use, put a :GetChildren() at the end of this
local Destination_Folder = -- Same thing but with destinations

local ps = game:GetService("PathfindingService") -- Serivices
local path = ps:CreatePath()

Next we create two functions:

local function setNPCOwnerShip(model) -- I recommend this, for some reason pathfinding is laggy if not
	for _, object in pairs(model:GetChildren()) do 
		if object:IsA("BasePart") then 
			object:SetNetworkOwner()
		end
	end
end

local function initalizeNPCStatus() -- Next we iterate through all NPC's with one goal
	local status = {} -- To create a status list of who is and who isn't pathfinding
	for index, npc in pairs(NPC_Folder) do
		status[index] = false
		setNPCOwnerShip(npc)
	end
	return status -- I don't know if status is a key word but if it is just change it easily
end

Lastly is the main code:

local statuses = initalizeNPCStatus() -- We use that function, you will have to make a code that runs
-- this everytime a new NPC is added to that folder

while wait() do
	for index, npc in pairs(NPC_Folder) do
		coroutine.wrap(function() -- Just so all other npcs can run at the same time
			if statuses[index] == false then
				statuses[index] = true
				local chosenDestination = math.random(1, #Destination_Folder)
				local hrp = npc.HumanoidRootPart.Position 
				local hum = npc.Humanoid
				-- Pathfinding
				path:ComputeAsync(hrp, Destination_Folder[chosenDestination].Position)
				local waypoints = path:GetWaypoints()

				for i, waypoint in pairs(waypoints) do
					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				end
				statuses[index] = false
			end
			
		end)()
	end
end

Thank you so much, this is exactly what i was trying to make, i will surely edit it and dissect the script to fully understand how it works, just one question, i’ve noticed that when starting all NPCs walk to the same first end point before spreading, is not a problem about it, i can simply make it simulate a bit before showing to the player but just curious of why does it this way even with different end point chosen

I noticed that too when running it, it actually is an easy fix! Just move the right before the pathfinding script into the coroutine function.

local path = ps:CreatePath()
-- Put this ^ before this v it should be at the beginning 
path:ComputeAsync(hrp, Destination_Folder[chosenDestination].Position)
local waypoints = path:GetWaypoints()

That was my mistake because the path variable is being altered and used by all the Npc’s rather than each Npc individually