# Having tower shoot first enemy for Tower Defense game

I am creating a tower defense game and can detect which enemy is closest to a tower, but I want the tower to attack the enemy closest to the end (or the first enemy).

Spawn time will not work as some enemies are faster than others, and overtake them.

What would be a good system/feature to achieve this? I was thinking an array/table could work.

2 Likes

Idea:
You got the path from the start to the end right? So if you calculate the total distance the enemy will do (basically add up each part of the path’s size) you get the total distance, and then you can make each enemy have an IntValue (or NumberValue) and it’s value will be equal to the distance they already did.
Then, your tower should check which enemy has the highest value.

2 Likes

I made a system where it works the other way around: instead of having the mobs move along the path and then calculating how far along it they’ve moved at any given point, it keeps track of their “path offset” and calculates the actual position based on that. That way it’s trivially easy to compare two mobs to see which one is further along the path.

Here’s the relevant code, although it’s very unfinished. Might want to return an object from setupPathFollower and put them all in a table.

``````--PathFollowerManager

local TagS = game:GetService("CollectionService")
local RunS = game:GetService("RunService")
local InputS = game:GetService("UserInputService")

function getPathNodes(path)
local nodes = {}
local nodeDists = {}

local nextNode = path.FirstNode.Value
local cumulativeDist = 0

while nextNode do
table.insert(nodes, nextNode)
nodeDists[nextNode] = cumulativeDist

local prevNode = nextNode
nextNode = nextNode.Next.Value
if nextNode then
cumulativeDist += (nextNode.Position - prevNode.Position).Magnitude
end
end

return nodes, nodeDists, cumulativeDist
end

function getPathOffsetPoint(nodes, nodeDists, pathLength, offset): Vector3
if #nodes == 1 then
return nodes[1].Position
end

if offset >= pathLength then
return getPathOffsetPoint(nodeDists, pathLength, offset % pathLength)
end

local prevNodeI, nextNodeI = 1, 2

while offset > nodeDists[nodes[nextNodeI]] do
prevNodeI += 1
nextNodeI += 1
end

local segmentDiff = nodes[nextNodeI].Position - nodes[prevNodeI].Position

return nodes[prevNodeI].Position + segmentDiff.Unit * (offset - nodeDists[nodes[prevNodeI]])
end

function getPathUnitOffsetPoint(nodes, nodeDists, pathLength, offset): Vector3
error("Not yet implemented!")
end

function findGroundPointUnderPoint(point: Vector3): Vector3
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Whitelist
params.FilterDescendantsInstances = {game.Workspace.Baseplate}
local result = game.Workspace:Raycast(
point,
Vector3.FromNormalId(Enum.NormalId.Bottom) * 5000,
params
)

if result then
return result.Position
end

return point
end

function setupPathFollower(pathFollower)
local path
local pathNodes
local pathNodeDistances
local pathLength
local offset = 0

RunS.Heartbeat:Connect(function(dt)
if path ~= pathFollower.Path.Value then
path = pathFollower.Path.Value
pathNodes, pathNodeDistances, pathLength = getPathNodes(path)
end

if #pathNodes > 0 then
offset += 10 * dt
offset = offset % pathLength

local offsetPoint = getPathOffsetPoint(pathNodes, pathNodeDistances, pathLength, offset)
local groundPoint = findGroundPointUnderPoint(offsetPoint)
pathFollower:SetPrimaryPartCFrame(CFrame.new() + groundPoint)
end
end)
end

table.foreachi(TagS:GetTagged("PathFollower"), function(_, v) setupPathFollower(v) end)
``````
1 Like

This question gets asked so frequently someone should make a tutorial for it.

That’s true. You wanna do it?

Hi, I am not the best scripter but I am trying hard to get better. Is this basically just getting the length between path nodes and adding them up depending on the mobs position? And would this allow me to find the 1st mob in the tower’s range?

I already have something like this implemented, now I need to figure out how to find the first enemy in the range of the tower.

Agreed, It’s often overlooked in tutorials, but can prove to be quite a challange

If you want a really easy way to determine this, you can put parts along the path and trigger whenever the first enemy touches it and update the towers current target. It would probably look something like this

``````local updateTargets = game:GetService("ServerStorage"):WaitForChild("UpdateTargets") -- bindable event to update the towers targets
local detectionParts = workspace:WaitForChild("DetectionParts"):GetChildren() -- a group or folder containing the detection parts

function onDetection (model)
updateTargets:Fire(model)
end

for i,v in pairs(detectionParts) do
v.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") --[[ you should probably change this to something only the enemies exclusively have ]] and hit.Parent:FindFirstChild("alreadyTouched") == false --[[ bool value to ensure that the part hasn't been touched anymore ]] not game.Players:GetPlayerFromCharacter(hit.Parent) and not game.Players:GetPlayerFromCharacter(hit.Parent.Parent) then -- ensure it's an enemy and not a player, you should probably edit this
onDetection(hit.Parent) -- send the model over
end
end)
end
end

-- then in your enemy script

local updateTargets = game:GetService("ServerStorage"):WaitForChild("UpdateTargets")
local currentTarget

updateTargets.Event:Connect(function(model)
currentTarget = model -- update target
end)
``````

if you don’t feel comfortable using .Touched events, you can loop raycasts, although that’s going to be more costly on performance than you would probably like.

hope this helps

I also did a post similar to this very recently, perhaps take a look?

I’d do a for loop that scans each enemy. first, check it’s distance from the tower. if the distance is lower than what you want, you can then see if it is the farthest.

Thanks a lot! I will defo try this out. How far would you recommend spacing out the detection parts?

I have never heard of zoneplus before, I presume this can be used to find positions within the zone? I will take a look into it.

If you’re using the .Touched event it won’t cause much lag at all, so you can place the parts close to each other or far apart depending on how fast you want towers to react to the first enemy. Although I recommend adding debounce to the .Touched event to prevent the event firing multiple times.

Maybe a 0.1 second debounce, considering towers wont be attacking at such a fast speed. Again I am not the best of scripters but I will attempt to refine the code and implement it into my game. This is actually the best solution I have heard to this problem yet.

1 Like