Need advice on how I can make an Enemy follow a path and then follow a Player instead if in range?

Hello!

I am trying to make an enemy approach the player if they are in range. I want the enemy to follow a preset path using waypoints and only deviate from this path when a player enters its range. I have set it up to print the nearest player and check if they are in range, but I am stuck on how to code the actual movement.

Ultimately, I want the enemy to leave its path, move toward the player, and then, after eliminating the player, return to its next waypoint and continue following the path to the end.

Any help or advice on how to achieve this would be greatly appreciated! If any additional information is needed, I’ll do my best to provide it.

This is my enemy script, which is a module script required by another script in ServerScriptService. In the script that requires the enemy module, I can create a simple wave layout and spawn enemies using enemyscript.Spawn(“(EnemyName)”, (Amount of Enemys I want), map.

-- Services --
local PS = game:GetService("PhysicsService")
local SSS = game:GetService("ServerScriptService")
local SS = game:GetService("ServerStorage")
local plrs = game:GetService("Players")

-- Locations --
EnemyStore = SS.EnemyStore

local enemy = {}

function enemy.Track(newEnemy, plrs)
	local rootPart = newEnemy:WaitForChild("HumanoidRootPart")
	local range = newEnemy:WaitForChild("Config"):WaitForChild("Range").Value
	
	while newEnemy do
		wait(1)
		local nearestPlayer, nearestDistance
		for _, player in pairs(plrs:GetPlayers()) do
			local character = player.Character
			local distance = player:DistanceFromCharacter(rootPart.Position)
			if not character or distance > range or (nearestDistance and distance >= nearestDistance) then
				continue
			end
			nearestDistance = distance
			nearestPlayer = player
			-- Scanning for nearby player
		end
		if nearestPlayer ~= nil and newEnemy:FindFirstChild(tostring(rootPart)) then
			print("From", newEnemy.Name, "the nearest player is", nearestPlayer)
			newEnemy:WaitForChild("Config"):WaitForChild("InRange").Value = true
		-- Sees if there is a player, inputs the player then makes InRange True	
		elseif nearestPlayer == nil and newEnemy:FindFirstChild(tostring(rootPart)) then
			newEnemy:WaitForChild("Config"):WaitForChild("InRange").Value = false
		-- Sees if there is not a player, inputs the player then makes InRange False		
		elseif newEnemy:FindFirstChild(tostring(rootPart)) == false then
			break
		-- Sees if the enemy no longer exists, if so then it stops the checking process
		end

	end
end

function enemy.Move(enemy, map)
	local humanoid = enemy:WaitForChild("Humanoid")
	local waypoints = map.Waypoints

	for waypoint=1, #waypoints:GetChildren() do
		humanoid:MoveTo(waypoints[waypoint].Position)
		humanoid.MoveToFinished:Wait()
	end 
	enemy:Destroy()
end

function enemy.Spawn(name, quantity, map)
	local enemyExists = SS.EnemyStore:FindFirstChild(name)
	
	if enemyExists then
		for i=1, quantity do
			task.wait(0.5)
			local newEnemy = enemyExists:Clone()
			newEnemy.HumanoidRootPart.CFrame = map.Spawn.CFrame
			newEnemy.Parent = workspace.Enemies
			newEnemy.HumanoidRootPart:SetNetworkOwner(nil)
			
			for i, object in ipairs(newEnemy:GetDescendants()) do
				if object:IsA("BasePart") then
					object.CollisionGroup = "Enemy"
				end
			end
			
			coroutine.wrap(enemy.Move)(newEnemy, map)
			coroutine.wrap(enemy.Track)(newEnemy, plrs)
		end
	else
		warn("Enemy doesn't exists", name)
	end
end

return enemy

If the code is recognizable its because I modified the Wave System from Gnomecode’s Tower Defense Tutorial.

2 Likes

Gnomecode also has some videos of the video game Teddy where he does something like what you want to do, the only thing you should change would be that when the player is eliminated, the enemy returns to the next point.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.