Recently I wrote a simple pathfinding npc system along with greatly optimizing it
The npc model properties such as: CanCollide
, CanQuery
, CanTouch
, and CastShadow
set to off
Code part has been simplified as I could make it be, instead of setting each NPC CFrame seperately I use Roblox’s BulkMove Api, because as I know this method doesnt apply any updates on physics of the model and only change the position of it.
The system is OOP structured. Client-sided rendering and server-sided spawn replication to all the clients.
My current goal is to improve the performance of my code as much as possible
What I tried and did not work:
-
Using Parallel Luau and creating a seperate actor for each NPC, resulted in almost the whole movement code being REQUIRED to run in serial. The conclusion is using chopped Parallel Luau to move parts is less performant than the serial way.
-
Anchoring all the parts inside of a npc model and not just PrimaryPart with welding them to each other. Did not work for idk reason
My suggestions:
- PartCache and ObjectCache are looking like a good option to use in my case, although reading their logic of work they didn’t seem anything special.
Vid: NPC PathFinding System Demonstration
Place: NPC System Stress Test - Roblox
Code
-- Parameters
local StepStuds = 50
function class.New(index : number, model : Model)
local self = setmetatable({},class)
self.Index = index
self.Character = model:Clone()
self.Character.Parent = workspace.NPCs
self.CFrame = self.Character.PrimaryPart.CFrame
self.Path = PathfindingService:CreatePath()
entities[self.Index] = self
cFrames[self.Index] = self.CFrame
parts[self.Index] = self.Character.PrimaryPart
Step(self)
return self
end
function Step(self)
while true do
local randomPoint = Vector3.new(math.random(-walkRange,walkRange), 0, math.random(-walkRange,walkRange))
self.Path:ComputeAsync(self.Character.PrimaryPart.Position, randomPoint)
local wps = self.Path:GetWaypoints()
for i=2,#wps do
self.CFrame = CFrame.lookAt(self.Character.PrimaryPart.Position, wps[i].Position)
while true do
self.CFrame += self.CFrame.LookVector * (StepStuds/Ticks)
if (wps[i].Position - self.CFrame.Position).Magnitude <= 1 then
break
end
task.wait(1/Ticks)
end
end
end
end
function BulkMove()
for index, entity in entities do cFrames[index] = entity.CFrame end
workspace:BulkMoveTo(parts,cFrames,Enum.BulkMoveMode.FireCFrameChanged)
end
I’m up for any of your ideas