Help with NPC Commands

How would I go about programming this?

Hypothetical NPC example:

  • An NPC that walks to a point, and jumps when they are done walking.
  • Has the ability to stop / interrupt this walk command

Let’s say I don’t want to use coroutines because I want a bunch of npcs at the same time.

What is the most efficient way to program this using a step/update method?
How would you program command capability for an NPC? Thanks in advance.

Surely you could use a service such as the Pathfinding Service, and just hard code stuff like jumping when they have reached the final point?

In the example I gave this would work to some degree, since I could use connections like MoveToFinished.

However it wouldn’t hold up with more complex NPC commands. Assume for example, we couldn’t use the MoveToFinished event and had to check for ourselves in a step function whether or not our NPC has reached its destination.

Haven’t touched the pathfinding in a while, so a bit of a disclaimer if I’m overlooking something here.

The pathfinding route would’ve finished if the NPC has reached all the waypoints, you can simply do a check for how many waypoints are left per NPC surely? I remember doing something similar and printing “finished” before.

Another option would be to store the destination location from the pathfinding and do a check if the character is within (x) radius of there.

What you’re saying definitely works, and if we put it all into a step/update method it would certainly do the job of getting to the destination and jumping. But what comes next, is trying to program a framework that lets the command be interrupted/canceled. Or a command being inputted, overwriting another command that is running.

More specifically, I’m trying to find different ways to structure an NPC command system like that without using coroutines.

I suppose if we are doing a for loop going through the waypoints (or similar), you can just break out of that if you get a command to interrupt it, and then set a new “thing” if something is overwriting it.

You could have a value wherever your doing this that is “cancel” or similar, you then could every iteration in the for loop, check to see if cancel is still false. If it turns true, you can break the loop.

There are many other ways I suppose it could be done. Optimisations can always come later if it’s a performance issue, as long as functionality is there, we can optimise it later, surely?

Someone else reading this thread may have a better idea how this could be achieved, and if so feel free to drop it in a reply here.

My solution to the problem

local my_npc = {}
my_npc.__index = my_npc

	if not (plyr and plyr.Character) then return end
	local self = setmetatable({}, my_npc)
	self.Commands = deepcopy(my_npc.Commands) --deepcopy function just clones table 1:1
	self.CurrentCommand = nil
	return self

function my_npc:Step()
	if self.CurrentCommand then

my_npc.Commands = {}

function my_npc:IssueCommand(cmd, ...)
	if self.Commands[cmd] then
		if self.CurrentCommand then
		self.CurrentCommand = self.Commands[cmd]

function my_npc:StopCommand()
	if self.CurrentCommand then
		self.CurrentCommand = nil

--first command from hypothetical
my_npc.Commands.WalkToJump= {}

function my_npc.Commands.WalkToJump:Issue(...)
	--set temporary data passed from original object (to avoid cyclic tables)

function my_npc.Commands.WalkToJump:Step()
	--if destination reached then
	--	jump

function my_npc.Commands.WalkToJump:Cleanup()
	--code that fires if command is interrupted/cancelled

return my_npc

The use case for this would be:

--wait! maybe I want to cancel it instead!

I can’t shake the feeling that there is a better solution (;⌣̀_⌣́). So if anyone spots something that could be improved on let me know!

( ´ ω ` )ノ゙

1 Like