I’m designing an AI for an NPC to do with melee combat.
For this I decided to go about using a common hybrid method between Behavior Trees and Finite State Machines.
Where there are a number of states the NPC can be in, such as:
Idle - No target, do nothing
Chase - Target nearby, move towards them
Attack - When close enough, attack
Return - No longer target, return to original position
And then the more exact behavior for each state is governed by a behavior tree inside.
The issue I am having is how to handle interruptions. For example, if the NPC is chasing a player (Chase state) but then is attacked by a different player. I would want the NPC to stop chasing its current target and target the other.
Do I simply just add monitors for every case? Checking for a new target at every step? If I do this then won’t I also need clean ups for every occasion? And if I do that then I can’t scale this model very well as eventually I’ll need many different clean up functions and monitors.
I would try making a task + priority system. Idle task has the lowest priority. Then, things like the NPC being attacked only need to increase the priority of the attack task corresponding to the attacker. This makes it so you do not need to think of entry and exit code for every state transition.
Oh would you mind telling me how you’ve chosen to approach it? maybe i can learn.
i figured out this interruption system based in coroutines:
function NPCai:startAdventure()
local co = coroutine.create(function()
self:decide() -- going on our infinite adventure
end)
coroutine.resume(co) -- triggering coroutine
print("when does this happen")
local co2 = coroutine.create(function()-- simultaneous process
while true do -- constantly checks whether interrupted changed
if self.Interrupted then -- checks the status of Interrupted
coroutine.close(co) -- this closes adventure coroutine
break -- break out of loop
else
task.wait(1) -- wait one second to check again (otherwise crashes)
end -- is automatically closed
end
end)
coroutine.resume(co2) -- triggering coroutine
print(coroutine.status(co2))
end
this seems to work on a basic level. I plan on adding ‘uninterruptable’ functions by doing a check what function we are at and waiting if it is uninterruptable. possibly i should replace the loop with renderstep or heartbeat or whatever. i still need to trigger a variable change with an event and test if that works
dont know what downsides could be to this system. but it does seem to work.
Actually looking over it today. Because bindable events also make a fake simultaneous thread you can just do this way easier and simpler no second co routine necessary:
function NPCai:startAdventure()
local co = coroutine.create(function()
self:decide() -- going on our infinite adventure
end)
coroutine.resume(co) -- triggering coroutine
print("when does this happen")
InterruptionEvent.Event:Connect(function()
coroutine.close(co)
print(coroutine.status(co))
-- interruption() function
end)
--at end of this coroutine it is suspended because it is still listening
-- after interruption has been received the coroutine is dead
-- go to interruption function which eventually calls startAdventure() again
print(coroutine.status(co))
end