Getting a model to find the nearest target then attacking

Hi, can someone please help me get a model to attack something? Right now it attacks 1 thing and when the thing dies, it keeps flying towards it and shooting and will not attack the second thing. Code:

local function getTargets()
	for v,t in pairs(workspace:GetDescendants()) do
		local enemyPlane = t:FindFirstAncestor("JapFighter")
		local enemyBomber = t:FindFirstAncestor("JapBomber")
		local enemyHeavy = t:FindFirstAncestor("JapHeavy")
		if enemyPlane or enemyBomber or enemyHeavy or t.Name == "JapFighter" or t.Name == "JapBomber" then
			t.Parent = targets
		end
	end
end
local function attack()
	getTargets()
	
	local distance = 999999999999999
	local target = nil
	local distance1 = nil
	for i,v in pairs(targets:GetChildren()) do
		distance1 = (script.Parent.hemalurgicSpike.Position - v.Position).magnitude
		if distance > distance1 then
			target = v
			distance = distance1
			local origincframe = engine.BodyGyro.cframe
			local dir = (engine.Position - target.Position).unit
			local spawnPos = engine.Position
			local pos = spawnPos + dir
			engine:findFirstChild("BodyGyro").maxTorque = Vector3.new(10000,10000,10000)
			engine:findFirstChild("BodyGyro").cframe = CFrame.new(pos, pos+dir)
			shoot(target) 
				
		end
	end
end

I think you need to order the targets from shortest to longest distance in a table beforehand. There will be a case where when the for loop first selects an object that is the closest distance, the target will not change and thus the model will always go to that object because it is the closest.

So maybe like

function orderTargets(relativePos, allTargets)  --Bubble sorting
	for i = 1, #allTargets do
		for j = 1, #allTargets - 1 do
			if (relativePos-allTargets[i].Position).magnitude 
				< (relativePos-allTargets[i + 1].Position).magnitude then
				local temp = allTargets[i]
				allTargets[i] = allTargets[i+1]
				allTargets[i+1] = temp
			end
		end
	end
	return allTargets
end

local function attack()
	local orderedTargets = orderTargets(script.Parent.hemalurgicSpike.Position,targets:GetChildren())
	
	local target = nil
	for i,v in pairs(orderedTargets) do
		target = v
		local origincframe = engine.BodyGyro.cframe
		local dir = (engine.Position - target.Position).unit
		local spawnPos = engine.Position
		local pos = spawnPos + dir
		engine:findFirstChild("BodyGyro").maxTorque = Vector3.new(10000,10000,10000)
		engine:findFirstChild("BodyGyro").cframe = CFrame.new(pos, pos+dir)
		shoot(target) 
	end
end
1 Like

@Psvita65594 so what you are saying is I should make 3 functions: One to return closest target, one to attack and one to put them together?

1 Like

Yeah pretty much. One for ordering targets and one for attacking. The getTargets() is actually okay to me.

1 Like

@Psvita65594
Alright, here is the code.
Attempted to index nil with position.

local function getTargets()
	for v,t in pairs(workspace:GetDescendants()) do
		local enemyPlane = t:FindFirstAncestor("JapFighter")
		local enemyBomber = t:FindFirstAncestor("JapBomber")
		local enemyHeavy = t:FindFirstAncestor("JapHeavy")
		if enemyPlane or enemyBomber or enemyHeavy or t.Name == "JapFighter" or t.Name == "JapBomber" then
			t.Parent = targets
		end
	end
end
local function findNearestEnemy()
	getTargets()
	local distance = 999999999999999
	local target = nil
	local distance1 = nil
	for i,v in pairs(targets:GetChildren()) do
		distance1 = (script.Parent.hemalurgicSpike.Position - v.Position).magnitude
		if distance > distance1 then
			target = v
			distance = distance1

		end
	end
end
local function attackNearestEnemy()
	local target = findNearestEnemy()
	local origincframe = engine.BodyGyro.cframe
	local dir = (engine.Position - target.Position).unit
	local spawnPos = engine.Position
	local pos = spawnPos + dir 	
	engine:findFirstChild("BodyGyro").maxTorque = Vector3.new(10000,10000,10000)
	engine:findFirstChild("BodyGyro").cframe = CFrame.new(pos, pos+dir)
	if target then
		shoot(target)
	end
end
while true do
	attackNearestEnemy()
	wait(0.01)
end

Thanks for your help, here is some nice music.

1 Like
script.Parent.hemalurgicSpike.Position

Here’s where you error occurs, should be fairly self explanatory.

@Forummer That is the primary part of the model. Here is my code. Can you please help? Right now it attacks one target before going haywire despite there having more targets to attack. Help?

@Psvita65594 I have been working on this for days. It refuses to work. My best one was when it attacked three targets before quitting. Please help

wait(7)
local model = script.Parent
local e = Instance.new('BodyGyro')
e.Parent = script.Parent.Engine
local engine = script.Parent.Engine
targets = workspace.americanTargets
local function destroyBullet(bullet)
	bullet:Destroy()
end
local function FireGuns(target)
	for v,Part in pairs(script.Parent:GetDescendants()) do
		if Part.Name == "MachineGun" then
			wait(0.05)
			local bullet = Instance.new("Part")
			bullet.Position = Part.Position
			bullet.CanCollide = false
			local v = Instance.new("BodyVelocity")
			bullet.Orientation = Part.Orientation
			bullet.Name = "AB"
			v.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
			v.Velocity = bullet.CFrame.LookVector * -500
			v.Parent = bullet
			bullet.Parent = workspace
			bullet.Size = Vector3.new(0.5,0.5,2)
			bullet.Material = "Neon"
			bullet.BrickColor = BrickColor.new("Bright blue")
			bullet.Touched:connect(function(part)
				local plane = part:FindFirstAncestor("USFighter")
				if not plane then
					bullet:Destroy()
				end
			end)
			delay(3,function() bullet:Destroy() end)
		end
	end
end
local function getTargets()
	for v,t in pairs(workspace:GetDescendants()) do
		local enemyPlane = t:FindFirstAncestor("JapFighter")
		local enemyBomber = t:FindFirstAncestor("JapBomber")
		local enemyHeavy = t:FindFirstAncestor("JapHeavy")
		if enemyPlane and enemyPlane.Health.Value > 0 then
			enemyPlane.Parent = targets
		end
		if enemyHeavy and enemyHeavy.Health.Value > 0  then
			enemyHeavy.Parent = targets
		end
		if enemyBomber and enemyBomber.Health.Value > 0 then
			enemyBomber.Parent = targets
		end
	end
end
local function shoot(target)
	if target.hemalurgicSpike then
		if (script.Parent.hemalurgicSpike.Position - target.hemalurgicSpike.Position).magnitude < 801 and target.Health.Value > 0 then
			FireGuns(target)
		end
	end
end
local function findNearestEnemy()
	local target = nil
	local distance = nil
	getTargets()
	for i,v in pairs(targets:GetChildren()) do
		if v:IsA("Model") and v.hemalurgicSpike and v.Health.Value > 0 then
			local previousDistance = 99999
			distance = (script.Parent.hemalurgicSpike.Position - v.hemalurgicSpike.Position).magnitude
			if distance < previousDistance and v.Health.Value > 0 and (script.Parent.hemalurgicSpike.Position - v.hemalurgicSpike.Position).magnitude < 1501 then
				previousDistance = distance
				target = v
			end
		end
	end
	return target
end
local function attack(target)
	if target and target.hemalurgicSpike and target.Health.Value > 0 then
		local origincframe = engine.BodyGyro.cframe
		local dir = (script.Parent.hemalurgicSpike.Position - target.hemalurgicSpike.Position).unit
		local spawnPos = script.Parent.hemalurgicSpike.Position
		local pos = spawnPos + dir 	
		engine:findFirstChild("BodyGyro").maxTorque = Vector3.new(10000,10000,10000)
		engine:findFirstChild("BodyGyro").cframe = CFrame.new(pos, pos+dir)
		shoot(target)
	else
		findNearestEnemy()
	end
end

local function patrolAi()
	local target = findNearestEnemy()
	if target and target.Health.Value > 0 and target.hemalurgicSpike then
		attack(target)
	end
	
end
while true do
	patrolAi()
	wait(0.01)
end