Is it possible to use wait() within a coroutine?

Hello!

I’m trying to make an AI for an attacking NPC.

The problem is, I don’t know how to delay/wait within a coroutine.

My NPC is supposed to wander around if it can’t find something to attack, but it ends up wiggling around like this:

Is there a way to use wait() within coroutines, or is there a better way I could do this?

Here’s my code.

AI:

local humanoid = script.Parent.Humanoid
local killer = script.Parent
local distance, closestHumanoid 
local module = require(game.ServerScriptService:WaitForChild("NearstPlayerModule"))
local maxDistance = 100
local root = killer.HumanoidRootPart
local target
local dmg = 25



local wander = function()
	humanoid.WalkSpeed = 16

	humanoid:MoveTo(Vector3.new(math.random(-500,500),5.992,math.random(-500,500)))
	
	local ray = workspace:Raycast(killer.HumanoidRootPart.Position,killer.HumanoidRootPart.CFrame.LookVector)
	if ray and ray.Instance ~= nil then
		humanoid.Jump = true
	end 
	
end

local chase = function()
	humanoid:MoveTo(closestHumanoid.HumanoidRootPart.Position)
	humanoid.WalkSpeed = 40
	
	local ray = workspace:Raycast(killer.HumanoidRootPart.Position,killer.HumanoidRootPart.CFrame.LookVector)
	if ray and ray.Instance ~= nil then
		humanoid.Jump = true
	end 
end

local attack = function()
	if target then
		target:TakeDamage(dmg)
		wait(0.5,1.5)
	end
end


while true do
	distance, closestHumanoid = module.GetClosestPlayer(maxDistance,root,killer)
	print(distance,closestHumanoid)
	if closestHumanoid  and distance <= maxDistance and distance > 15 and closestHumanoid.Humanoid.Health ~= 0 then
		local chaseRoutine = coroutine.create(chase)
		coroutine.resume(chaseRoutine)
	elseif closestHumanoid and distance <= 15 and closestHumanoid.Humanoid.Health ~= 0 then
		print("damage!!")
		target = closestHumanoid.Humanoid
		local attackRoutine = coroutine.create(attack)
		coroutine.resume(attackRoutine)
	elseif closestHumanoid == nil and distance == nil or closestHumanoid.Humanoid.Health == 0 then
		local wanderRoutine = coroutine.create(wander)
		coroutine.resume(wanderRoutine)
	end
	wait()
end
1 Like

Hi, since coroutines only affect their own thread, using wait will not affect other functions like your while true do loop. The easiest way to get around this is to create a table with values telling the script whether the action is ready. It will take me some minutes to code this

So like a debounce kind of thing?

Coroutines execute a separate thread, like task.spawn(). Wait won’t effect anything else since it’s executed separate from the actual host thread.

In short: Yes, you can use wait().

Turns out the problem was I forgot to use math.random() in the wait() instructions

1 Like

Not really related to your question, but I think in this case it’d be better to replace wait() with task.wait()

1 Like

It really depends on the situation. If it’s client based and it’s an action that should wait for the client to continue, you can use wait(). However, if it’s something that HAS to be done in time, you can then use task.wait().

1 Like

this will probably be good

local humanoid = script.Parent.Humanoid
local killer = script.Parent
local distance, closestHumanoid 
local module = require(game.ServerScriptService:WaitForChild("NearstPlayerModule"))
local maxDistance = 100
local root = killer.HumanoidRootPart
local target
local dmg = 25
local functionReady = {}

local wander = function()
	local function NotReady(value)
		functionReady[2] = value
	end
	NotReady(true)
	humanoid.WalkSpeed = 16
	humanoid:MoveTo(Vector3.new(math.random(-500,500),5.992,math.random(-500,500)))
	NotReady()

	local ray = workspace:Raycast(killer.HumanoidRootPart.Position,killer.HumanoidRootPart.CFrame.LookVector)
	if ray and ray.Instance ~= nil then
		humanoid.Jump = true
	end 
end

local chase = function()
	humanoid:MoveTo(closestHumanoid.HumanoidRootPart.Position)
	humanoid.WalkSpeed = 40
	
	local ray = workspace:Raycast(killer.HumanoidRootPart.Position,killer.HumanoidRootPart.CFrame.LookVector)
	if ray and ray.Instance ~= nil then
		humanoid.Jump = true
	end 
end

while true do
	distance, closestHumanoid = module.GetClosestPlayer(maxDistance,root,killer)
	print(distance,closestHumanoid)
	if closestHumanoid and distance <= maxDistance and distance > 15 and closestHumanoid.Humanoid.Health ~= 0 then
		coroutine.wrap(chase) -- coroutine.wrap really isn't necessary unless a wait is inserted into the function
	elseif closestHumanoid and distance <= 15 and closestHumanoid.Humanoid.Health ~= 0 then
		local tick = tick()
		if not functionReady[1] or tick > functionReady[1] then
			target = closestHumanoid.Humanoid
			if target then
				print("damage!!")
				target:TakeDamage(dmg)
				functionReady[1] = math.random(0.5,1.5) + tick
			end
		end
	elseif closestHumanoid == nil and distance == nil or closestHumanoid.Humanoid.Health == 0 then
		if not functionReady[2] then
			coroutine.wrap(wander)
		end
	end

Uh, I’m sorry to bring your hopes down, but:

  1. The topic was already solved.
  2. There was no need for a huge script.

I appreciate your help, though!

They were making edits to my original script but yes, I’ve already figured out the problem.

1 Like

This should stop the ai from spazzing out when wandering

Thank you, but I’ve already solved the problem.

Now while you can argue that task.wait() is better than wait() due to it being more accurate (wait() has an internal physics-based throttler and updates 2x slower than task.wait()). That doesn’t dismiss wait() as being entirely useless (for now atleast).

Like for example how it has a physics-based throttler (as I mentioned above). This makes it yield more than the expected yield time whenever the server lags due to physics calculations.

2 Likes