How to break this for loop?

Hello, I am trying to make a game where there is an enemy npc that is using raycast to detect whenever the npc can see the player. If the npc loses sight of the player, it will start a countdown to delete the npc. However, if the npc can see the player again, I would want to cancel the countdown and make the npc resume chasing the player and avoid deletion. How do I achieve this?

Here is the script:

local function canSeeTarget(target)
	local origin = killer.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - killer.HumanoidRootPart.Position).unit * 100
	local rayParams = RaycastParams.new()
	rayParams.FilterType = Enum.RaycastFilterType.Blacklist
	rayParams.FilterDescendantsInstances = {game.Workspace.Foliage:GetChildren(), game.Workspace["OutsideShed/Fence"].ShedItself:GetChildren()}
	
	local rayCastResult = workspace:Raycast(origin, direction, rayParams)
	
	if rayCastResult and rayCastResult.Instance then
		if rayCastResult.Instance:IsDescendantOf(target) then
			FirstSight = true
			LostSight = false
			CountDown = false
			print("Player spotted")
			return true
		else
			LostSight = true
			CountDown = true
			if FirstSight == true then
				print("player lost")
				while CountDown do
					for i = 4, 0 , -1 do
						if CountDown then
							print(i.." seconds before deletion")
							task.wait(1)
						else
							break
						end
						--[[if i == 0 then
							Deletion = true
							print("killer will be deleted")
						else
							CountDown = false
							Deletion = false
							break
						end]]
					end
					task.wait()
				end
			end
		end
	else
		return false
	end
end

I appreciate any help/advice I can receive for this. Thank you.

1 Like

what’s the error? what happens when you run it?

There is no error. The script endlessly counts down from 4 to 0 seconds if the npc cannot see the player and npc does not continue pursuing the player until it reaches zero seconds. I would like to break/stop the countdown mid countdown so it can continue chasing the player.

Do you give it some time before it starts the for i loop, and made sure the “CountDown” is set to true/false, with print checks?

How do I make it print a true/false variable?

It seems to be working though, it’s just the main problem here is I don’t know how to break the for loop mid countdown, it has to finish the countdown before the npc checks to see if they can see the player again and continue chasing. I would like for the npc to be able to chase the player immediately once they can see them and cancel out the countdown so the npc doesn’t end up getting deleted.

print(CountDown)

I get what you mean now. So you’re saying it checks after the for i loop ends, but you want it to check while it’s on a for i loop?

Yes. Once the countdown reaches zero, the npc chases the player, I would want it to do it mid countdown to cancel it. Since once it reaches zero, I will be making the npc get deleted. So basically I want the npc to avoid deletion if they can see the player and get deleted if they can’t see any player in 4 seconds.

So this is what I see wrong, you’re using a loop for an “for loop” so remove the “while countdown do” then I’d would add a If statement, checking if the Countdown is set to true and if so then return to the chasing part.

Create a timer using task.defer():

local function canSeeTarget(target)
	local origin = killer.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - killer.HumanoidRootPart.Position).unit * 100
	local rayParams = RaycastParams.new()
	rayParams.FilterType = Enum.RaycastFilterType.Blacklist
	rayParams.FilterDescendantsInstances = {game.Workspace.Foliage:GetChildren(), game.Workspace["OutsideShed/Fence"].ShedItself:GetChildren()}

	local rayCastResult = workspace:Raycast(origin, direction, rayParams)

	if rayCastResult and rayCastResult.Instance then
		if rayCastResult.Instance:IsDescendantOf(target) then
			FirstSight = true
			LostSight = false
			print("Player spotted")
			return true
		else
			LostSight = true
			if FirstSight == true then
				print("player lost")
				
				local timer
				timer = task.defer(function()
					for i = 4, 0 , -1 do
						if LostSight and i > 0 then
							print(i.." seconds before deletion")
							task.wait(1)
						elseif i <= 0 then
							print("Countdown ended")
							--// Do your deletion
							timer:Disconnect()
						elseif not LostSight then
							timer:Disconnect()
						end
					end
				end)
				
			end
		end
	else
		return false
	end
end

I’m unable to test this in studio because I don’t have the models, but let me know if it works! :smile:
Note: defer is delayed a cycle for performance, if you want it to be immediate use task.spawn()

You should use tick() to check for the elasped time since something happened, instead of having a for loop countdown,

With this usage, you dont have to reset the loop, and you can simply set the time to now, and then check for a specific number under that elapsed time.
Another thing you can do is utilize task or coroutines, and cancel ir using task.cancel or coroutine.close.

Its basically the same speed as spawn()

1 Like

I’ll try your solution tomorrow when I get a chance and let you know if it works thanks.

Definitely forgot about corountines. I’ll see if I can make use of them if the other suggestion doesn’t end up working for me.

I tried using tick() and it seems to be the closest I’ve gotten to getting it to work, however the very first time the enemy loses sight of the player, it errors. The 2nd time it loses sight of the player, it works just fine. I’ve tried reordering stuff around a bunch of times and honestly I’m out of ideas at this point.

Here’s the changed script:

local function canSeeTarget(target)
	local origin = killer.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - killer.HumanoidRootPart.Position).unit * 100
	local rayParams = RaycastParams.new()
	rayParams.FilterType = Enum.RaycastFilterType.Blacklist
	rayParams.FilterDescendantsInstances = {game.Workspace.Foliage:GetChildren(), game.Workspace["OutsideShed/Fence"].ShedItself:GetChildren()}
	
	local rayCastResult = workspace:Raycast(origin, direction, rayParams)
	
	if rayCastResult and rayCastResult.Instance then
		if rayCastResult.Instance:IsDescendantOf(target) then
			FirstSight.Value = true
			LostSight = false
			CountDown.Value = false
			print("Player spotted")
			return true
		else
			LostSight = true
			CountDown.Value = true
			if FirstSight.Value == true then
				print("player lost")
				CountDown:GetPropertyChangedSignal("Value"):Connect(function()
					if CountDown.Value == true and LostSight == true then
						CountStarter = tick()
						print("count started")
					
					elseif CountDown.Value == false and LostSight == false then
						countDownSeconds = (tick() - CountStarter)
						print(countDownSeconds)
					end
				end)
				--[[if CountDown  and LostSight == true then
					coroutine.resume(countDownCorountine)
				elseif CountDown == false then
					coroutine.yield(countDownCorountine)]]
				--[[for i = 4, 0 , -1 do
					if CountDown == true and LostSight == true then
						print(i.." seconds before deletion")
						task.wait(1)
					elseif CountDown == false and LostSight == false then
						break
					end
				end]]
			end
		end
	else
		return false
	end
end

And there’s the screenshot of the error I receive. As I said, it errors on first time the enemy loses sight of player, but works as it should if enemy loses sight of player the 2nd time.

Where does the error take you to? It’s saying you are adding a number to nil