I have been working with classes and objects made via module scripts. When it comes to NPCs and objects of that sort, what is the best way to control a lot of objects of the same class (for example controlling 10 enemy NPCs)?
Currently I’ve been implementing an Update method in my classes that I then call in RunService.Heartbeat, so all objects Update every frame. Is there a better method than this? In particular for pathfinding.
Example:
local NPC = {}
NPC.__index = NPC
function NPC.new()
local self = setmetatable({}, NPC)
self.model = some model
self.target = Vector3.new(x, y, z) --RANDOM POSITION
return self
end
function NPC:Update()
--do pathfinding stuff to move the npc
end
Server Script
local NPC = require("NPC class")
npcs = {} --array of npcs or NPC.new()
game:GetService("Run Service").Heartbeat:Connect(function()
for i, v in pairs(npcs) do
v:Update()
end
end)
These are just rough examples and not how the code actually is, but let me know if there is a better method to loop objects every frame.
Running that much code every heartbeat will take up much more memory than needed. Can you elaborate more on what code is run in the pathfinding function?
Running this pathfinding code in parallel might help increase performance as well.
I would use events. You can set your objects to listen to an event for an action or control to go through.
Also, about your path finding. If you are using the pathfinding service, DO NOT USE IT EVERY HEARTBEAT. I would HIGHLY recommend putting pathfinding in an error protected while true loop. After every 3rd waypoint, you can allow the loop to loop.
I had done a similar mistake like this and one path finding npc costed a 5th of the whole entire server processing with the custom logic included.
Can you specify more about events? For pathfinding I have a module similar to Simple Path so it ‘pathfinds’ after a set amount of seconds already. My biggest concern is removing those objects, because when I remove them the Update function works maybe 2 more times after the object and its data have been removed. Using a debounce does not help because all data from the object is removed, including the debounce so error control in this situation is difficult.
There are two ways that you could use events to control your NPCs.
For explanation, I will be using BindableEvents. You can use RemoteEvents as well
Simple Method:
The simple way involves creating a BindableEvent and parenting it under a common place like ReplicatedStorage. When an NPC object is created, it will start listening to that BindableEvent for events fired by the controller object. The controller can send information such as actions or positions where you want your NPCs to go.
If you ever wanted to expand on the control aspect such as only allowing some controllers to control certain NPCs, you can add an ID property to your controller. When creating an NPC, add a property where it stores the ID it will listen to. Once the controller fires the event, it will also send the remote ID which the NPC can either ignore the request or accept it.
Advanced Method
If you want to get more savvy, you can create BindableEvents when instantiating the controller object. Add a property such as ControlInput which will reference the created BindableEvent. You can add more events if you would like.
When it comes to the NPC object, add a property like Controller which will reference the controller that the NPC will listen to. You can also add a property named Connections which will store all of the event connections. All you have to do after that is store the connection to ControlInput and connect it to the method of your choice.
If you are deleting the Controller, you can also use an event that signals any object that contains the controller that is being destroyed (like Instance.Destroying). When the controller is getting destroyed, disconnect all controller events in all of the NPCs with that controller. Once the Destroying event has been fired, the controller can call :Destroy() on all of the BindableEvents created.
TLDR
The simple method includes creating an event that the controller and all NPCs will use. You can use identifiers to dictate which NPCs will listen to which controllers.
The advanced method includes creating an event on Controller instantiation and adding a reference to the controller and its events as a property in NPC.