Advice on crafting a modular NPC "class"

Does anybody have advice on a good tutorial or sample code to look at regarding “modularizing” NPC code into separate lua files? I’m interested in better organizing my code on my current project and would like to craft lua files for some of the major blocks in my main file.

I’m using Jon Bebee’s Modular Classes in Corona Tutorial as a reference:
http://www.coronalabs.com/blog/2011/09/29/tutorial-modular-classes-in-corona/

This is very insightful but there are a a few things I’m trying to figure out beyond this example:

  • My NPC need to “act” in the environment. How do I set up event listeners in this modularized context?
  • I need to be able to tell one NPC of the same type appart from another (presumably with some sort of unique identifier/name). Is there a way I can set that up from within the file or do I need to manage that from the main file where I’m calling the .new() function?
  • how should I handle garbage collection of my NPCs? place them all into one display group and then remove that? I am using the meta table concept from the tutorial. How does the NPC_mt come into play with this? I guess I could create a garbage collection function in my separate NPC file and then call that? If I’ve made multiple instances of NPCs would I then iterate through them calling the function for each one?

Thanks for any help you can offer with this kind of noobish questions! :slight_smile:
[import]uid: 105707 topic_id: 28418 reply_id: 328418[/import]

To start answering some of my own questions, this tutorial on spawning objects “the right way” is very helpful:
http://www.coronalabs.com/blog/2011/09/14/how-to-spawn-objects-the-right-way/

Using this tutorial as an example, I’m clear on how to “modularize” the spawn() function into a seperate Lua file. How would one also add the for control structure used to create more than one spawn and add them to the spawn table? I guess one could make a function for that as well?

I guess my question for this spawning tutorial is how much code can I transfer into a seperate file to be able to keep my main as lean as possible? [import]uid: 105707 topic_id: 28418 reply_id: 114695[/import]

Modules are very very powerful they can streamline your code and make simple functions accessible over and over again without having to recode them. Here is the best way to create a module and use it

Here is the module setup:
[lua]–Spawn.lua module
–First create a table that will hold everything in the module
local spawnTable = {}

–Spawn function that will be accessible outside of the module
local function spawnEnemy ()
print (“Enemy Spawned”)
end
–Insert the function into the module table
spawnTable.spawnEnemy = spawnEnemy

–Return the table that holds the function
return spawnTable[/lua]
Here is a main.lua example to access the module
[lua]–main.lua
–Load your spawn module
local spawn = require (“Spawn.lua”)

–grab the function from the module and create a local instance
local spawnEnemy = spawn.spawnEnemy

–Now you can use the function like you would any other function
spawnEnemy()[/lua]

It is as simple as that, the best thing about this kind of module is that everything stays in local space and you don’t have to worry about any of the package.seeall stuff.

I have modules with over 5000 lines of code with no performance issues and i use them a lot in our games.

Now for your questions of how to access the npcs once they are created from the module? I am assuming that you will have a function that creates an NPC so I would recommend just returning the object you create with the function. for instance:

[lua]–Spawn Module

–Function to create an npc
local function createNPC()
local npc = display.newRect (0, 0, 50, 50)
npc.x = 100; npc.y = 100
return the display object at the end of the function
– return npc
end[/lua]

[lua]–main.lua
local spawn = require (“spawnEnemy”)

local mainGroup = display.newGroup()

local spawnnpc1 = spawn.createNPC
mainGroup: insert (spawnnpc1)

spawnnpc1:removeSelf()
spawnnpc1 = nil[/lua]

I did not check the code but thats the way ive done it in the past, with multiple spawns that you might need to access you can add them to a table or make individual names for or what ever your want, the only key is to return the display object at the end of the module function and it will be like you created it in your main.lua and you can do what ever you want with it.

[import]uid: 126161 topic_id: 28418 reply_id: 114702[/import]

@ 3 Dreams Gaming - I appreciate such a detailed explanation. Although I’ve gone through the Corona tutorial a number of times, seeing your example has really helped drive home this technique of returning a table with the function in it. Very neat :slight_smile:

It also has made it exceptionally clear regarding returning an NPC display object.

That’s neat to hear that you’ve used this technique so well with your own modules, I hope to be able to do the same for myself.

Thank you :slight_smile: [import]uid: 105707 topic_id: 28418 reply_id: 115571[/import]

Glad i could help! let me know if you need any more help. [import]uid: 126161 topic_id: 28418 reply_id: 115582[/import]

@3 Dreams Gaming

I’m feeling good with the “modularity” aspect that you’ve been so helpful with.

What I’m working through right now is how to manage the “actions” of the NPC. I have a hierarchy of activities (if adjacent to the player, attack, if not, check pathing and move, etc.). How would you control the frequency with which a behavior is able to trigger? With a timer.performWithDelay function that sets a flag? For instance I don’t want the NPC to attack the player every frame it’s adjacent to the PC!

I’ve started a new post here related to the subject:
http://developer.coronalabs.com/forum/2012/07/15/npc-logic-use-state-machine

Thanks :slight_smile: [import]uid: 105707 topic_id: 28418 reply_id: 115615[/import]

Does anybody have any advice how to take my “AI” function for my NPCs and bake them into my NPC file? Currently I’m running my NPC movement function from my main file which worked for prototyping that functionality but now that I have it working I need to get things cleaned up and modularized.

To run the NPC movement I’m currently adding a timer.performWithDelay that calls the movement function every second for each NPC that I’ve spawned. When I attempt to move this function into my newNPC() function in the NPC file, I can get it to either move only the first of multiple NPCs or it will move all of the NPCs for only one step.

What’s the trick to attaching a timer.performWithDelay to each NPC that I spawn from my newNPC() function?

Thanks in advance for any help :slight_smile: [import]uid: 105707 topic_id: 28418 reply_id: 116877[/import]

Well…personally I wouldn’t run timer.performWithDelay() in this instance. Instead I would build an enterFrame listener or something along those lines and then have it execute code after a specified amount of time has passed (measuring event.time, I vaguely recall.)

Now, the one cool thing that gets glossed over a bit when you’re creating visual objects is that they are still tables, so there’s actually a really convenient way to attach references:

[code]
local function coolAI()
– do stuff
end

local function spawnNPC(name)
local monster = display.newGroup()
local body = display.newImage(monster, “coolawesome.png”)

monster.name = name
monster.health = 10
monster.brain = coolAI
monster.arm = display.newImage(monster, “coolarm.png”)

return monster
end[/code]

Display objects are still tables, which means you can easily create table references that aren’t just numbers or strings, but references to functions, images, etc. A common mistake when people use spawn functions is to use internal variables instead of table names, which of course makes it much harder later on to tell what’s what. ( npcGroup[2].arm is a lot easier to understand than npcGroup[2][37] right?) [import]uid: 41884 topic_id: 28418 reply_id: 116904[/import]

Hi richard9,

This is great, I especially resonate on your point about display objects being tables. Am I right in saying that pretty much everything one generates with Lua is a table of one sort or another? I’ve been trying to think of things as tables and this illustration reinforces this perspective.

I follow your example, that you can set the coolAI function into the monster.brain container of the monster variable/table.

The trick is, with your monster.brain that is set to the coolAI function, how do you actually “drive” that function?

With a runtime event listener in your main.lua?

Is there a way to set that up so it runs on it’s own once spawned?

If you can help me understand where you would place the enterFrame listener you mention in the context of your sample above, I think that may “unlock” my understanding of how to set things up!

Thanks :slight_smile:
[import]uid: 105707 topic_id: 28418 reply_id: 116907[/import]

Well, yes and no. I mean from an elemental standpoint you have strings (“hello”), numbers (01), and tables ( {} ). A lot of the general use stuff is basically complex enough to be a table in some form. It’s really best to just remember that display objects are tables that work in a different way.

Case in point:

 monster.arm ~= ( monster:insert(monster.arm) ) 

…because displayGroups are not simple tables, they work differently and have a more complex substructure. Probably with scary metatables somewhere. :wink:

State machines are a real mess and I am hardly an expert, but I have the most success right now with an enterframe system. (Note that I have zero experience with coroutines; that link in the other thread looks pretty interesting…) So for the turn-based game I was working on, I basically made a mode check system:

[code]
local mode = “player” – who’s turn is it?
local index = 1 – for iterating through a group
local done = true – a stop sign…

local function turnMachine(event)
if mode == “player” and done == true then
done = false
playerTurn()
elseif mode == “enemy” and done == true then
done = false
enemyTurn(index)
end[/code]

  1. playerTurn() executes code. At the end, it sets mode to “enemy” and done to true.
  2. enemyTurn() executes the same way, but…
    a. at the end of the function, index = index + 1
    b. at the end of the function, only changes mode if index >= enemyGroup.numChildren

If you attach that to an enterframe listener, it will run your machine every frame, but only execute things sequentially since for 90% of your frames neither condition will be true. If you wanted all of the enemies to execute simultaneously (or virtually that) you could just run the entire loop at once.

Then if you want to pad time you can do event.time comparisons (which I believe Corona has a tutorial for somewhere in the blogs…) Like I said though I have no idea how coroutines work so maybe that is a better route. [import]uid: 41884 topic_id: 28418 reply_id: 116912[/import]

Hi richard9,

I appreciate the clarification regarding tables.

Your state machine example is helpful. I should be clear that I’m not currently working on a turn based game. It’s a real time game and I have NPC actors that I’d like to control with scripted logic. So a state machine seems really relevant, but it will be something that doesn’t wait for game turns (although maybe I use turns behind the scenes to control NPC actions?)

Currently I’ve been working on my pathing solution for my NPCs (they’ll also need to be able to attack the player and potentially be idle when unable to path or attack). I am able to get all my NPCs pathing, but I have to use a timer.performWithDelay for each of them and this is done in my main.lua.

This is how I have things set up:

[lua]–tlovNPC.lua
–the lua file I’d like to use for all my NPC logic/spawning/deleting/etc.

local spawnTable = {}

–… some NPC code…

local function newNPC(NPCxcell, NPCycell)
NPC = display.newRect (0, 0, 32, 32)
–more newNPC properties…

return NPC
end

spawnTable.newNPC = newNPC

–… more NPC code…

return spawnTable[/lua]

[lua]–main.lua

local NPC = require(“tlovNPC”)

–…stuff…

local newNPC = NPC.newNPC
local creeper01 = newNPC()
mainGroup:insert(creeper01)

function npcWanderOne(npc)
–all my npc movement logic in here
end

timer.performWithDelay( 1000, function() npcWanderOne(creeper01) end , 0 )[/lua]
What I’m trying to figure out is how to set the NPCs up so that they spawn “activated” (so that I don’t have to use the timer.performWithDelay in my main file)

Is there a way to do this? When I take my npcWanderOne(npc) function and put it into my newNPC() function in the seperate file, then only the first NPC that’s spawned will path. The rest don’t receive that functionality.

The root of my question is is there a way to associate an event listener or timer with each of my NPCs that I spawn from this separate NPC file? I haven’t figured out how to do that. I can use the file to make “instances” of the NPC “class” (if I’m allowed to call it that in lua!). But I don’t know how to set the class up so that each instance takes “action”.

Thanks for your help :slight_smile:

I feel like this is a very solvable issue and I’ve been spinning my wheels all day on it so I welcome anyone’s input! [import]uid: 105707 topic_id: 28418 reply_id: 116925[/import]

  1. Yeah, if you’re running realtime you can basically throw out the stop-and-go aspect of the turn machine. Instead you would check each group individually (player and enemy) with if statements - if they’re busy, don’t do anything. If they’re not busy, do something.

Once you know that a given player or NPC should do *something*…then you can start doing procedure order checks to figure out exactly what. (i.e.: “if near rock then smash rock else move() end”)

The real strength of modes in an enterFrame machine is that you aren’t programming on a per-turn basis like you might with transition.to. For example, I’m moving my characters with translate()…

if mode == "move" then -- Move <speed> pixels to the right.<br>local speed = math.ceil(tileSize/moveSpeed)<br>object:translate(speed, 0)<br><br>if math.fmod(object.x, tileSize) == 0 then<br> mode == "think"<br>end<br>end<br>

I need that to occur an unknown number of times to get from tile 1 to tile 2. So I set the mode to “move” and basically force the app to run the move command every enterFrame until it gets where it needs to be. (In this case, math.fmod returns a remainder; if the remainder is zero, I’m there!)

2. Pretty hazy on how your code works but getting newly spawned enemies into action should be fairly trivial with an enterFrame machine; just make them part of a display group!

local function coolmachine(event)<br>for i = 1, enemyGroup.numChildren do<br> if enemyGroup[i].busy == false then<br> doSomething(enemyGroup[i])<br> end<br>end<br>

Every frame, your app would knock on the door of every enemy to see if it’s ready for a new action. You then feed it the decision making action. [import]uid: 41884 topic_id: 28418 reply_id: 116927[/import]

I like this “enterFrame machine” concept very much…

Where do I set it up? In my main.lua? Is there any way that I can make it part of my NPC lua file to keep my main.lua tidy? I guess I could but then it might be easy to forget to remove the event listener for scene transitions if I’m using the Storyboard API?

With your block of code above, I’m assuming I would set up a listener like this?:
[lua]Runtime:addEventListener(“enterFrame”, coolmachine)[/lua]

I’ve been solving a lot of tricky challenges lately but I’ve had a hard time getting my head around driving my NPC logic.

Thanks for your continued help! :slight_smile: [import]uid: 105707 topic_id: 28418 reply_id: 116956[/import]

You can set it up wherever. The best general approach for Corona projects right now, I’d say, is to use a storyboard approach - build a seperate lua file using the storyboard template that is your scene, and then have main.lua go there upon launching the program, ie:

storyboard.gotoScene("scene1.lua")

Organization is more art than science but I would probably use the Runtime in there so that you can easily unload it whenever exiting the scene for whatever reason. The commands you call from within the runtime can then be either internal or from whatever files you include in.

local function coolmachine() if mode == "funtime" then player.startThinking() -- run the startThinking function from player.lua end end

(There’s a performance hit for doing that though, so a common strategy is to localize it first…)

 local startThinking = player.startThinking -- localized! Now call startThinking() instead of player.startThinking()

I won’t kid you, it’s fairly easy to run into performance issues with enterFrame designs, but I’ve seen more than a few people here recommend it nonetheless. You just may need to pick up something like Corona Profiler (which is really quite good) to check your code if you run into any performance issues on-device. [import]uid: 41884 topic_id: 28418 reply_id: 117011[/import]

I have massive enterFrame code blocks running smoothly at 30 frames per second on an iPhone 3G, so I definitely would not rule out going enterFrame :slight_smile:

Cheers,
Thomas [import]uid: 70134 topic_id: 28418 reply_id: 117028[/import]

I don’t doubt it. But then I’m running a 64x64 Lime map and getting single digit frames on an iPhone 4 with only two sprites onscreen…>_< [import]uid: 41884 topic_id: 28418 reply_id: 117045[/import]

@richard9 - so if I’m following you correctly, you’re saying that you recommend keeping the event listener in the current scene.lua so it’s easier to remember to remove it on the exit scene? Sounds like a good practice to help avoid doubling up listeners.

I like your technique for localizing the player.startThinking() function. How would this work for something like npc.startThinking()? The issue being that I only have one player but potentially have many NPCs. Would I set up a loop to loop through each instance of my npcs and call the npc.startThinking? This question gets back to what I was getting at earlier regarding how to get my functionality to apply to multiple NPCs. I think I’m gathering that I need to use looping control statements to call the function for each instance of the NPC that I’m tracking.

The Corona Profiler looks like an incredibly useful tool. I was looking around the Profiler website and discovered their Cider IDE… which looks unbelievably awesome. Does anyone use that? Clearly I’m not a programmer but a good friend of mine that is once told me “you’re not really programming unless you’re using a debugger”. The Cider debugger seems like that on it’s own might make a night and day difference for me.

Thanks for your continued support richard9 :slight_smile:
@thomas6 - I appreciate you sharing your experience with enterFrame listeners. That’s reassuring to hear since this seems like a good route for me to take.

As a learning point for me, what are some of the types of things you use your enterFrame listeners for? Do you do any sort of state control with them or are they for other activities? I’d love to know more about how you’ve been able to use them.

Thanks for your input :slight_smile:

[import]uid: 105707 topic_id: 28418 reply_id: 117055[/import]

Exactly. You start the listener in enterScene and remove in exitScene, that way everytime you come back it’s on and everytime you leave it’s off. As Corona says you need to remove those listeners manually so storyboard offers a great framework to do so without needing to remember it later.

Localizing is mostly to save time - every step you go outside of local has a minor performance hit. Table localizing - what I said before about attaching functions to a display object - is different. But yes, for your enemies you would need to setup a for loop and then call the AI function for each of them in a row. I use something like this:

for i = 1, enemyGroup.numChildren do if enemyGroup[i].active then enemyTurn(enemyGroup[i]) end end

I hear CIDER is pretty interesting but I would need to kick my habit of using SublimeText2… [import]uid: 41884 topic_id: 28418 reply_id: 117058[/import]