Hi devs!
Today. I was trying to make my tower find enemies from a table regarding their positions and alpha value. It works really well when there are 1 or more enemies with the same name or type. But for some reason when a enemy with a different name came into range the towers constantly switch random targets and sometimes it even stops attacking
Video showing the issue::
Here are my scripts involving targetting
Enemy
local activeCharacters = {}
function class.new(name)
local modelTemplate = game.ReplicatedStorage.Common.Server:FindFirstChild(name)
if not modelTemplate then
warn(tostring(name) .. "' does not exist in ReplicatedStorage.Characters")
return nil
end
local model = modelTemplate:Clone()
model:SetAttribute("Alpha", 0)
model:SetAttribute("Speed", model:GetAttribute("Speed") or 1)
model:SetAttribute("Health", model:GetAttribute("Health") or 100)
model.Parent = workspace.Characters
local self: Enemy = setmetatable({}, class) :: any
self.Model = model :: Model
self.CFrameIndex = 1
for _, playerObj in plr:GetPlayers() do
myPacket.SpawnEnemy:FireClient(playerObj, self.Model)
end
local rotationalOffset = Vector3.new(0, 0, math.rad(0))
local minOffset = -1.1111
local maxOffset = 1.1111
local offset = (math.random() * (maxOffset - minOffset)) + minOffset
local rotationalCFrame = CFrame.Angles(rotationalOffset.X, rotationalOffset.Y, rotationalOffset.Z)
local offsetCFrame = CFrame.new(offset, 0, 0)
if #wayPositions == 0 then
for i = 1, #workspace.Path:GetChildren() do
local waypoints = workspace.Path[i]
local adjustedPositions = waypoints.Position
table.insert(wayPositions, adjustedPositions)
end
end
self.NewPath = BezierPath.new(wayPositions, 2.5)
self.PathLength = self.NewPath:GetPathLength()
if self.PathLength == 0 then
warn("Path length is zero for " .. model.Name)
model:Destroy()
return nil
end
self.Alpha = 0
self.Speed = model:GetAttribute("Speed")
self.Health = self.Model.Health.Value
self.Destroyed = false
self.EnemyCFrame = CFrame.new(0,0,0)
table.insert(activeCharacters, self)
local canCFrame = true
while self.Alpha < 1 and self.Model do
if not self.Model then return end
if not self.Model:FindFirstChild("Alpha") then return end
if self.Model then
if self.Destroyed then return end
if self.Health <= 0 then
for i = 1, #activeCharacters do
if activeCharacters[i] == self then
table.remove(activeCharacters, i)
break
end
end
self.Alpha = 1
self.Model:Destroy()
self.Destroyed = true
return
end
if self.Alpha < 1 then
self.Alpha = self.Alpha + (self.Model:GetAttribute("Speed") * game:GetService("RunService").Heartbeat:Wait()) / self.PathLength * workspace.GlobalInfo.Tick.Value
if self.Alpha > 1 then self.Alpha = 1 end
local newCFrame = self.NewPath:CalculateUniformCFrame(self.Alpha)
self.EnemyCFrame = newCFrame
else
game.ReplicatedStorage.GlobalSounds.BaseHit:Play()
self.Model:Destroy()
self.Destroyed = true
end
end
end
workspace.Map.Base.Health.Value -= self.Model.Health.Value
self.Model:Destroy()
self.Destroyed = true
game.ReplicatedStorage.GlobalSounds.BaseHit:Play()
return self
end
function class:Spawn(name, amount, cooldown, waitBetween)
task.delay(waitBetween / workspace.GlobalInfo.Tick.Value, function()
for i = 1, amount do
task.spawn(function()
class.new(name)
end)
task.wait(cooldown / workspace.GlobalInfo.Tick.Value)
end
end)
end
function class:GetTargetingData(Enemy): { any }
return {
activeCharacters
}
end
–
Tower
function class.new(name, cfra, player)
local self = setmetatable({}, class)
local modelTemplate = game.ReplicatedStorage.Common.Towers.Server:FindFirstChild(name)
if not modelTemplate then
warn("Character: Model '" .. tostring(name) .. "' not found in ReplicatedStorage.Characters")
return nil
end
local model = modelTemplate:Clone()
model.Parent = workspace:FindFirstChild("Towers")
self.cfra = cfra
self.Model = model
self.Model.TowerCFrame.Value = cfra
self.module = require(game.ReplicatedStorage.Common.Towers[self.Model.Name])
player.Money.Value -= game.ReplicatedStorage.Common.TowerPrices:FindFirstChild(self.Model.Name).level1.Value --self.Model.Cost.Value
self.Model.Owner.Value = player.Name
for _, playerObj in plr:GetPlayers() do
myPacket.SpawnTower:FireClient(playerObj, self.Model)
end
task.spawn(function()
local lastAttackTime = 0 --os.clock() - self.module[self.Model.level.Value].firerate
local connection
connection = runService.Heartbeat:Connect(function()
if not self.Model or not self.Model.Parent or self.Model.Health.Value <= 0 then
if connection then
connection:Disconnect()
end
return
end
local now = os.clock()
if now - lastAttackTime >= self.module[self.Model.level.Value].firerate / workspace.GlobalInfo.Tick.Value then
local target = class:FindTarget(self.Model, self.module[self.Model.level.Value].range)
if target and self.Model.Health.Value > 0 then
local damage = self.module[self.Model.level.Value].damage
local targetHealth = target[1].Health
local actualDamage = math.min(damage, targetHealth)
target[1].Health = targetHealth - actualDamage
player.Money.Value += actualDamage
for _, playerObj in plr:GetPlayers() do
myPacket.TowerAttack:FireClient(playerObj, self.Model, target[1].EnemyCFrame.Position)
end
end
lastAttackTime = now
end
end)
end)
return self
end
function class:FindTarget(Model, range)
local targetingData = shared.Enemy:GetTargetingData()
local bestTarget = nil
local bestWaypoint = nil
local bestDistance = nil
local bestHealth = nil
for _, mob in pairs(targetingData) do
local distanceToMob = (mob[1].EnemyCFrame.Position - Model.TowerCFrame.Value.Position).Magnitude
local distanceToWaypoint = mob.Alpha
if distanceToMob <= range then
if Model.Mode.Value == "Near" then
range = distanceToMob
bestTarget = mob
elseif Model.Mode.Value == "First" then
if not bestWaypoint or mob.Alpha > bestWaypoint then
bestWaypoint = mob.Alpha
bestDistance = nil
if not bestDistance or distanceToWaypoint < bestDistance then
bestDistance = distanceToWaypoint
bestTarget = mob
end
end
if mob.Alpha == bestWaypoint and (not bestDistance or distanceToWaypoint < bestDistance) then
bestDistance = distanceToWaypoint
bestTarget = mob
end
elseif Model.Mode.Value == "Last" then
if not bestWaypoint or mob.Alpha < bestWaypoint then
bestWaypoint = mob.Alpha
bestDistance = nil
if not bestDistance or distanceToWaypoint > bestDistance then
bestDistance = distanceToWaypoint
bestTarget = mob
end
end
if mob.Alpha == bestWaypoint and (not bestDistance or distanceToWaypoint > bestDistance) then
bestDistance = distanceToWaypoint
bestTarget = mob
end
end
else
end
end
return bestTarget
end
Any help is appreciated!