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.


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.


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.


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

	return nodes, nodeDists, cumulativeDist

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

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

	local prevNodeI, nextNodeI = 1, 2

	while offset > nodeDists[nodes[nextNodeI]] do
		prevNodeI += 1
		nextNodeI += 1
	local segmentDiff = nodes[nextNodeI].Position - nodes[prevNodeI].Position
	return nodes[prevNodeI].Position + segmentDiff.Unit * (offset - nodeDists[nodes[prevNodeI]])

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

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

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

		if path ~= pathFollower.Path.Value then
			path = pathFollower.Path.Value
			pathNodes, pathNodeDistances, pathLength = getPathNodes(path)

		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)

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? :wink:

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)

function addEvent ()
	for i,v in pairs(detectionParts) do
			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
                hit.Parent.alreadyTouched.Value = true


-- then in your enemy script

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

	currentTarget = model -- update target

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 :smile:

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