Performance worries: using .Heartbeat to run a function multiple times; contains a loop (for every individual vehicle)

That could be a possibility. However, would a better way of using .Heartbeat or any other loop on multiple vehicle models, would be to fire a BindableEvent and have that listened to in every vehicle model, so your constantly checking when each car has moved without using .Heartbeat for every vehicle separately.

Also, is .Stepped a better choice than using .Heartbeat in my use case, or is there not much of a difference? In addition, I’ve heard something about using Parallel Luau but I am not sure if this is particularly related or would be particularly useful in my use case, or whether this would just over-complicate things further. Otherwise, by what you suggested regarding using a .Touch event for region blocks where the car would respawn if it touches these blocks, would probably be the best option here.

I believe that there’s little difference in performance in connecting functions to .Heartbeat or to a BindableEvent connected to .Heartbeat.
Also, you could try out CollectionService (or the forum about it) to handle vehicle respawns inside a single script.

Since the vehicle respawn function has a loop check with task.wait(), using .Stepped or .Heartbeat shouldn’t matter since the task.wait() yields then resumes the thread on the next Heartbeat step.

According to Computation again, Parallel Luau (multithreading) can be used if you for expensive tasks that doesn’t access the data model. You could use it by parenting the script to an Actor and calling task.desynchronize() for calculations like Position / Vector distance or cooldown, then task.synchronize() for respawning the car.
However, using multithreading for just a small number of computations could be over-complicating things. You also don’t want to use multithreading for long computations, as mentioned in Best Practices.

1 Like

What would that look like if I tried to implement it into the script instead of using .Heartbeat, or how would I fire the vehicleCooldown() function using CollectionService?

This is the jeepRespawn() function by the way:

local newJeep = originalJeep:Clone()
newJeep:SetPrimaryPartCFrame(originalCFrame)

for _, passenger in ipairs(Seat:GetChildren()) do
	if passenger:IsA("Model") then
		passenger:Destroy()
	end
end

Jeep:Destroy()
newJeep.Parent = workspace
print("Jeep regenerated in the same place!")

If you use CollectionService, you would probably add a tag to each car model, then use something like this:

-- Collection Service functions
local function onInstanceAdded(car : Model)
	-- Validate car
	-- Set up function
end

local function onInstanceRemoved(car : Model)
	-- Validate car
	-- Clean up stored connections
end

local function setUpCars()
	task.defer(function()
		for _, object in CollectionService:GetTagged(tag) do
			onInstanceAdded(object)
		end
	end)
	CollectionService:GetInstanceAddedSignal(tag):Connect(onInstanceAdded)
	CollectionService:GetInstanceRemovedSignal(tag):Connect(onInstanceRemoved)
end

-- Calling functions
setUpCars()

You would probably have to use local scopes, tables, or modules to store the cooldown information for each car.
The .Heartbeat connection can be stored for each car & cleaned up in the instance removed function, or it can be a single function that calls vehicleRespawn() for every car.
The implementation will be slightly complicated, but it is better than creating a script for each individual car.

So I would put all the cars together inside a Folder or something, place the script you provided inside ServerScriptService or the Folder and do it like that?

Also, where you are setting up a function that makes sense but removing a function; how does that work?:

local function onInstanceRemoved(car : Model)
	-- Validate car
	-- Clean up stored connections
end

Finally, you have shown me what the separate script would look like but how would I connect that to each script inside each vehicle model? Would I still keep .Heartbeat or how would that work?

You can put the script inside ServerScriptService, but the car models can be in different folders since they are obtained through tags and CollectionService.

onInstanceRemoved() fires when a tag is removed from an instance. This is often used for cleaning up resources to prevent memory leaks.
The code sample for GetInstanceRemovedSignal shows an example of cleaning up connections when a tag is removed.

I’ll provide an example for what you can do:

  • First, define vehicleCooldown(car) and respawnCar(car) such that they respawn the car specified in the parameter.
  • Next, in onInstanceAdded(car), create and store the connections to vehicleCooldown(car), like how the the code sample in GetInstanceAddedSignal did with .Touched.
    The connections can be:
    • .Heartbeat (in general),
    • .Touched (for region blocks),
    • :GetPropertyChangedSignal("Occupant") (for seats).

This way, you don’t need a separate script inside each vehicle model (except for Data Sharing module scripts).

Therefore:

Do you think using a ModuleScript, where you’d require the ModuleScript from each script inside each vehicle model, would be a more effective approach than compared with using CollectionService, since this sounds more complicated, even though it seems like it might achieve pretty much the same result, which is to reduce the duplication of code in each vehicle model separately?

I think it’s more effective to use a single script to set up the vehicles. CollectionService is a way of retrieving all vehicle in the DataModel in a single script.
The functionality of the ModuleScript in my post was for extra configuration of the vehicle (ex: different cooldown seconds), not for storing general functions like respawnCar().

I meant just using a ModuleScript in general and having it being required in each vehicle model, rather than using CollectionService, entirely. Would there be any advantage of doing this or is there a more rewarding advantage of going down the CollectionService route, even though it could potentially become more complicated whilst achieving similar results, or not?:

I would avoid placing scripts containing functions in each models because it would be harder to manage than if we used CollectionService.
(see the post on CollectionService, section 2. Using CollectionService)

I’ve developed with JToH Tower Creation Kit, which currently uses a ModuleScript for each type of scripted obby mechanism.
When updating the kit, it is quite difficult to convert all of these mechanisms into the newest version. Through CollectionService, you will only need to update a single script to convert the mechanisms.

Yeah I meant the entire script placed in each vehicle model; sorry, I did not make it clear enough. The code I provided in this topic only includes the function connected to the .Heartbeat, but I was thinking of placing the entire script inside a ModuleScript, (including the .Heartbeat connected to the vehicleCooldown() function), and just requiring it in every vehicle model that needs it, instead of doing them separately which could cause greater performance issues, because I would be independently using .Heartbeat in each vehicle script, or is that not how this would work?:

The performance of using .Heartbeat for separate vehicle scripts won’t differ much from using .Heartbeat for each vehicle in a single script.
I only suggested CollectionService because it avoids the use of identical scripts that could be hard to manage in the future.
You could read more about the advantages of using CollectionService in the links attached to the previous posts.

That is what would happen also, if you required the ModuleScript containing the same code, and required it in every script that’s inside every vehicle model and not using CollectionService entirely or; since this seems to be more complex when you could just do it that way, unless there is an advantage of using CollectionService instead?:

1 Like

I can see your point.

Overall, it could just be a preference, but there’s a slight advantage with using CollectionService.

Here are the two methods that we mentioned:

  1. With Scripts in each vehicle model requiring a single ModuleScript, the DataModel will contain multiple Scripts inside Models and a single ModuleScript inside ServerStorage or ReplicatedStorage.

  2. With CollectionService, you would put a single Script in ServerScriptService or StartPlayerScripts, and perhaps an additional ModuleScript to organize the functions.

Comparing the two methods, you’ll see that:

  • The first method requires more Scripts than the second method.
    This creates more Instances, which could be difficult and messy to work with.
  • Since the Scripts in the first method are placed in Models, they will only run on the server side of the client-server model once the model is parented to Workspace. You can’t run client-sided scripts since they only run in certain containers.
    The Script used in the second method can be ran from both the server and client side of the client-server model, allowing for client-sided scripting.
    In the case of respawning vehicles, you won’t need to do client-sided scripting.

Overall, using CollectionService reduces the number of Script instances (which improves manageability and possibly performance), and it allows similar objects to function both on the server and the client side of the client-server model.

Also, using CollectionService wouldn’t make things more complex. You could let the examples in the documentation for CollectionService guide you through ways to use the service.

1 Like

I am only accessing the server side as you know. Therefore, there is less of a reason to use CollectionService and rather to use a single ModuleScript instead? In theory if I am using a ModuleScript and requiring it in every script inside every vehicle model, I’d only be using one script of which I could edit from instead of having separate scripts?

However, I assume that there must be some advantage to using either of these because there must be some performance improvements, if your using the same code all in one place?

Yes, you would only be editing one script, but there’s just the tiny issue of having extra, identical Script instances that require the ModuleScript.
Also, with CollectionService, you could do :AddTag() or :RemoveTag() to dynamically add or remove a vehicle, which is a feature that the ModuleScript method can’t provide easily.

As mentioned in ModuleScripts: “Having multiple copies of a function is disastrous when you need to change that behavior.”
The improvement in manageability is already an advantage, even though there’s little impact in performance.

True, I still have an issue even when I do use a ModuleScript. I tested 32 cars and moved them all at once and they took a while to respawn. So in terms of performance; that hasn’t really been improved at all for in which case, I could still use CollectionService on my current implementation of requiring the ModuleScript into every vehicle model that needs it?

I still feel like using CollectionService would eventually become unnecessarily complicated though, because I don’t necessarily have to have the vehicles already spawned in, especially because I am trying to use so many vehicle models at once when I could simply leave it up to the user to have to spawn them in. I’m only using .Heartbeat for when the vehicles are already spawned in for users to drive in and when they are pushed out of place they would respawn, so maybe I should have it up to the user to spawn the vehicles in, therefore no need for the use of .Heartbeat anymore or trying ways to increase the game’s performance in light of this?

When cars are moved, there’s a cooldown before cars are respawned.
Do you mean that time, or do you mean the car takes a while to load?
You can try debug-printing when vehicles start to load to see if all vehicles start reloading at the same time.

From the respawn code posted before, I see that you created a new clone of the car model.
Wouldn’t it be better to just reposition the old car and remove the car’s AssemblyLinearVelocity?

Also, in the code, I see that you used :SetPrimaryPartCFrame. SetPrimaryPartCFrame is a deprecated function that is superseded by PivotTo, so I suggest using the latter instead.

About other parts of the post:

When new vehicles are added, it would fire the GetInstanceAddedSignal event to set up the vehicle functions, so you wouldn’t need to worry about vehicles being spawned in later.
You could also remove the functions by just calling :RemoveTag on the vehicle model.

If you feel like using .Heartbeat has a significant drawback on the game’s performance, it’s okay to consider letting players spawn in the vehicles themselves.

1 Like

Like the cooldown takes a while to start if they are all moved at once, resulting in all of them taking longer to respawn back into their original spawning positions. That’s without using CollectionService at this point, of course.