I'm doing a tower defense game, but I'm having trouble in the tower, can anyone help me?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I want my tower to shoot the first enemy that enters its range

  2. What is the issue? Include screenshots / videos if possible!
    My tower shoots at first enemy, but the fastest enemies when it enters the range of the tower, ends up having privileges and the tower attacks them instead of what is actually first

Gifs: https://gyazo.com/199226b919f22d08190d94fd734a90c0

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub? Yes, had a topic talking about, but did not talk much about how to fix this problem, so I ended up having more doubts, so I decided to create this topic

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

My Tower Script

-- local RunService = game:GetService("RunService")
local Enemys = workspace.Enemys


local EnemysTable = {}
local EnemysOrdener = {}

local HumanoidRootPart = script.Parent.HumanoidRootPart
local Torso = script.Parent.Torso

local Configuration = script.Parent.Configuration

local Path = workspace.Map.TesteMap.Path

function UpdateMatriz()
	table.sort(EnemysOrdener, function(x,y)
		return x[2] > y[2]
	end)
end

function insert()
	for i,v in pairs(Enemys:GetChildren())do
		if v and v:IsA("Model") then
			if EnemysOrdener[v] == nil and (HumanoidRootPart.Position - v.HumanoidRootPart.Position).magnitude <= Configuration.Scope.Value then
				table.insert(EnemysOrdener, {v, v.Configuration.Distance.Value})
			end
		end
	end
end

local function insertEnemys()
	table.clear(EnemysOrdener)
	EnemysTable = {}
	insert()
	UpdateMatriz()
	print(EnemysOrdener)
	for i,v in pairs(EnemysOrdener)do
		if v and type(v) == "table" then
			local enemySelected = v[1]
			local Position = v[2]
			
			local mag = (HumanoidRootPart.Position - enemySelected.HumanoidRootPart.Position).magnitude
			if mag <=  Configuration.Scope.Value then
				table.insert(EnemysTable, enemySelected)
			end
		end
	end
	init()
end

function init()
	if EnemysTable[1] == nil then return end
	
	if Configuration.EnemyAttack.Value == "First" then
		local EnemySelected = EnemysTable[1]
		
		if EnemySelected ~= nil then
			
			if EnemySelected:FindFirstChild("Humanoid") then
				Torso.CFrame = CFrame.new(Torso.Position, Vector3.new(EnemySelected.HumanoidRootPart.Position.X, HumanoidRootPart.Position.Y, EnemySelected.HumanoidRootPart.Position.Z))
				EnemySelected.Humanoid:TakeDamage(Configuration.Damage.Value)
				
				if EnemySelected.Humanoid.Health <= 0 then
					EnemySelected:Destroy()
					EnemysOrdener[EnemySelected] = nil
					EnemysTable[1] = nil
				end
			end
		end
	elseif Configuration.EnemyAttack.Value == "Last" then
		local EnemySelected = EnemysTable[#EnemysTable]
		if EnemySelected ~= nil then

			if EnemysTable:FindFirstChild("Humanoid") then
				HumanoidRootPart.AlignOrientation.Attachment1 = EnemySelected.HumanoidRootPart.Attachment
				EnemySelected.Humanoid:TakeDamage(Configuration.Damage.Value)
			end
		end
	end
	wait(Configuration.Seconds.Value)
end

while true do
	wait(0.1)
	insertEnemys()
end

My Distance Script

local RunService = game:GetService("RunService")

local first = {}

local EnemysFolder = workspace.Enemys

local isRunning = false

EnemysFolder.ChildAdded:Connect(function(child)
	if child:IsA('Model') then
		first[child] = tick()
	end
end)

RunService.Heartbeat:Connect(function(dt)
	for i,v in pairs(EnemysFolder:GetChildren())do 
		if not v:IsA("Model") then continue end
		coroutine.wrap(function()
			local Speed = v.Humanoid.WalkSpeed - 0.5
		
			v.Humanoid.Running:Connect(function(Run)
				if Run >= Speed then
					isRunning = true
			else
					isRunning = false
				end
			end)
			if isRunning then
				v.Configuration.Distance.Value += v.Humanoid.WalkSpeed * dt
				v.Head.BillboardGui.TextLabel.Text = v.Configuration.Distance.Value
			end		
		end)()
	end
end)
1 Like

So, you use magnitude between NPC - End whenever your NPC play backward the magnitude is gonna increase, I recommend you to use NPC - Next Node and Get Current Node which is the highest such as

NPC1[mag 10,node1]
NPC2[mag 15,node1]

NPC1 is the first

I’m not sure with my method, but if it doesn’t work let me know.

1 Like

That way I won’t be able to go down the paths that go back to the beginning

1 Like

So in order to target enemies who are furthest down the track you want to check their distance from the beginning.

Distance = Speed * Time

What I would do, is for each enemy you have a loop continuously incrementing their distance traveled based on their speed. Whenever a new enemy steps into the towers range the tower will check all enemies within range and target the one with the furthest distance value.

It would be best to have a single loop go over each enemy on the track, having a separate loop for each one would be rather expensive.

Just to give you an idea, this is a rough example of what could work.

game:GetService("Run Service").Heartbeat:Connect(function(dt)
    for _, enemy in ipairs(enemies) do
        enemy.DistanceTravelled += enemy.Speed * dt
    end
end)

Run service events return the time between the last event, which I have called dt.

1 Like

I’m going to test it right now, and then I’m going to tell you the result.

kept having the same problem i had before :frowning:

but I realized it worked much better than before

The only cause for this I can think of is if the enemy’s speed value isn’t the same as their actual speed.
If you can’t get that working another thing you could try is instead of setting a speed value for each enemy, you could do Enemy.BodyPart.Velocity.Magnitude or if you are using humanoids you could read from their walkspeed.

1 Like

I’m using moveTo to move enemies, but still having the problem of the tower stop shooting at the first enemy to shoot in the runner

Did you try DistanceTravelled = dt * enemy.Humanoid.WalkSpeed

Yeah that seems like it should work. Did it?

that way worked properly, thanks!

Ok, I’ve looked over your distance script and there seems to be a few problems. Firstly, that coroutine is unneeded since the thread does not yield. Also, each time you loop over the enemies you are connecting a new Running event, which will cause a memory leak. I think the logic with you comparing Run with Speed and the isRunning stuff is causing problems which reduces the accuracy of the distance the enemies have actually traveled, leading to the problems you are experiencing now.

Glad to hear it worked! You should also check my last reply as the things listed there could become a problem later.

I’ll send my current code and you can analyze it to see if you find any problems?

Is everything all right on this one?

What’s the point in the Speed variable? Also I recommend using ipairs over pairs in this case, ipairs is faster at iterating through an array.

Just discards that part of the code above RunService

Right, i forgot about that, i’ve changed already

1 Like