ohhhh im sorry accident sometimes
Wait do you mean Parallal Lua? Because I don’t have that much expensive operations, i do not think this will help that much.
The reason we have client side rendering is because it can run smoothly even if we have up to 1000 enemies. It’s just that the tower targeting is lagging the game. Without towers, there is next to no lag.
Can anyone help? This is bugging me a lot
Please ask me stuff if you are confused. This is my first reply so idk what to say here…
EDIT: Fixed some bugs and cut out some fluff to optimize it a bit more
NOTE: If “self.range” is calculated in studs then the range has to be dividided by two like this “self.range / 2” because 1 radius basically translates to 2 studs so be careful about that.
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Include
local function detectenemies(targetMode: "first"|"last")
params.FilterDescendantsInstances = {map.enemies}
local towerPos = model.PrimaryPart.Position
local potentialEnemies = workspace:GetPartBoundsInRadius(towerPos, self.range / 2, params) --This gets all enemy parts that are in the range of the tower within a sphere (range is divided by two cuz i assume the range is in studs)
local range: number? -- this range var is for getting the closest or the furthest enemy
local target: Part?
target = potentialEnemies[1]
for i, potentialEnemyPart: Part in ipairs(potentialEnemies) do
local distance = (towerPos - potentialEnemyPart.Position).Magnitude
if not range then
range = distance
end
if distance <= range and targetMode == "first" then
range = distance
target = potentialEnemyPart
elseif distance >= range and targetMode == "last" then
range = distance
target = potentialEnemyPart
end
end
return target and target:FindFirstAncestorOfClass("Model") --this can return nil if no enemy was found
--^ NOTE: if the enemies have more models inside them this might return that model instead, you could fix this by ungrouping the models inside the enemy models
end
Old Version (don't use this, it has bugs and it sucks)
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Include
local function detectenemies(targetMode: "first"|"last")
params.FilterDescendantsInstances = map:WaitForChild("enemies"):GetDescendants()
local towerCFrame = model.PrimaryPart.CFrame
local potentialEnemies = workspace:GetPartBoundsInRadius(towerCFrame.Position, self.range, params) --This gets all enemy parts that are in the range of the tower
local range: number -- this range var is for getting the closest or the furthest enemy
local target: Part
target = potentialEnemies[1]
for i, potentialEnemyPart: Part in pairs(potentialEnemies) do
local distance = (target.Position - potentialEnemyPart.Position).Magnitude
if not range then
range = distance
end
if distance <= range and targetMode == "first" then
range = distance
target = potentialEnemyPart
elseif distance >= range and targetMode == "last" then
range = distance
target = potentialEnemyPart
end
end
if target then
target.Destroying:Once(function()
target = nil
end)
end
return target:FindFirstAncestorOfClass("Model") --this can return nil if no enemy was found
--^ NOTE: if the enemies have more models inside them this might return that model instead, you could fix this by ungrouping the models inside the enemy models
end
This might not improve that much to be honest, i tried my best. You can test it to see if it improves anything.
I would also like to inform you there is better ways to detect enemies than your solution so i would advise to think about how you handle towers so it can be fast and efficient, so please don’t hold back on trying more ways to handle your towers. I never tried something like this so i don’t have exprience in this stuff
Our enemy system has client side rendering, so the position is refrenced of attrubute
If you want to use the CFrame attribute then this is the modified version.
But the problem here is the hitbox of the tower is a square instead of a radius (sphere) which may not be what you want
local function detectenemies(targetMode: "first"|"last")
local towerPos = model.PrimaryPart.Position
local target, range
for i, potentialEnemy: Model in pairs(enemies:GetChildren()) do
if not target then
target = potentialEnemy
end
local distance = (towerPos - potentialEnemy:GetAttribute("CFrame").Position).Magnitude
if not range then
range = distance
end
if distance <= range and targetMode == "first" then
range = distance
target = potentialEnemy
elseif distance >= range and targetMode == "last" then
range = distance
target = potentialEnemy
end
end
if (towerPos - target:GetAttribute("CFrame").Position).Magnitude > self.range then --checks if the target is out of range
return nil
end
return target
end
Also --!native
probably doesn’t help in your case i don’t know much about it but it seems that your script doesn’t really need it and also because --!native
ly generated scripts take up more memory and resources so it is not recomended to use it in all of our scripts
here is when to use --!native
link to the post: Luau Native Code Generation Preview [Studio Beta]
You should target every 1-2 seconds, or if enemy is gone, then you can prevent mass spaming 100 per second
Sorry for late reply, but it locks onto an enemy and doesn’t go to any other enemy until the locked enemy dies.
Hmm… Can you tell me does the targeted enemy still stays targeted even when it exits the range? and also what does the “Exit” attribute for enemies mean?
It moves on after the targeted enemy leaves the range. Exit means the final destination for the enemy. Also I noticed that the range is a bit bigger than what the range is supposed to be.
I dont know how to fix why your range being a bit bigger but that may be because the code i sent detects in the shape of a square, so the code i sent previously could work if the positions are updated on the server.
But try this to fix the locked target problem, not sure if it will fix it but i am trying alright!
local function detectenemies(targetMode: "first"|"last")
local towerPos = model.PrimaryPart.Position
local target, range
for i, potentialEnemy: Model in pairs(enemies:GetChildren()) do
local distance = (towerPos - potentialEnemy:GetAttribute("CFrame").Position).Magnitude
if distance > self.range then
continue
end
if not target then
target = distance
range = distance
end
if (distance <= range) and (targetMode == "first") then
range = distance
target = potentialEnemy
elseif (distance >= range) and (targetMode == "last") then
range = distance
target = potentialEnemy
end
end
return target
end
The biggest performance gains that you will get will probably be from changing the targeting algo from O(N^2). It’s not easy but a broadphase for target detection would be a big bonus. Maybe look into quad trees.
I concur. A quadtree or octree is probably the best option for increasing performance significantly. I’m working on a tower defense myself right now and implementing an octree has increased performance by a lot.
And how would I implement an Octree? My enemies are rendered client sided
An octree is simply a spacial partitioning datastructure. It doesn’t matter whenever it’s clientside or serverside. It’s only important that it’s implemented on whatever side has the authority so that it can actually be used properly.
The use case here is that by grouping entities in separate nodes, it’s more efficient to search for a target because you’re no longer searching and comparing with every entity on the map, but only the entities inside the nodes that are in range.
As for implementation, I’d suggest you go search it up and try to learn for yourself - it will be much more useful if you can understand what is going on.
Okay, thank you! Ima try to implement this
From my understanding, octtrees seem to be more for static objects? Because all the modules that are public all are for static uses.
You could try looking at this module.
Keep in mind that I haven’t tested it myself. Though it’s important to understand how octrees work and adapt them to your specific use-case.
Octrees, though best suited for static objects, are still really good for dynamic objects - in fact, Roblox uses one for their physics engine. Ideally, insertion and removal are O(logN), which is good enough since you’re not adding new entities every frame the entire game. The main performance boost is the fast searching (compared to a brute force method).
Octrees can seem daunting. That’s ok. Challenging yourself and persevering is the way to success!
A quadtree might be better for your situation if the height level of the enemies and towers don’t matter to gameplay, like if your towers will hit enemies no matter the distance is height-wise. And it might have better performance than an octree aswell. But if you plan on including height/length as a part of the towers hit range then it’s a better idea to stick with the octree.