How To: NPCs and loops
In this tutorial, you will learn about modifying NPC's at runtime and how their various properties behave.
The NPC Class contains various methods for modifying NPC values at runtime. There is no real way to get a single NPC, instead you need to get a table of a type of NPC in the level. A table is similar to an array in other languages such as C++. An array is a container of multiple objects accessed by an index.
To get this, we use the NPC.get() method which takes two parameters.
1. The NPC's ID
2. The section (or -1 for whole level).
function onLoop() tableOfGoombas = NPC.get(1, -1); end
This code assigns the variable tableOfGoombas to the table of all the goombas across the whole level. It is wise you get familiar with tables in Lua. For more information, please read this article from the Lua documentation on how tables work. If you're already familiar with arrays from other programming languages, there's one difference you need to know: tables in Lua are indexed starting from 1, not from 0. So, instead of typing
To return the first instance of the Goomba in the array, you would use 1 instead.
Properly utilizing the NPC Class
It is important that you utilize the NPC class from a loop only. Do not store any NPC values between code as this can cause errors, always access a fresh table of NPCs in either your
1. onLoop() function
2. onLoopSection#() function
NPCs are changing constantly in SMBX. One tick an NPC may exist but another it may not. You can't always predict that, so always keep your table fresh and only in the scope of your loop.
One other smart thing to do before iterating in a for loop is to check and make sure there are actually objects in the array. Not doing this will cause errors in your script. The simplest way to do this is with an if statement and the table.getn() function. table.getn() takes an argument of a table and will return the amount of objects in the table. So, if our tableOfGoombas has 3 Goombas, table.getn(tableOfGoombas will return 3. We can use this value as such:
if(table.getn(tableOfGoombas) > 0) then --iterate here.
For loops are the easiest way to access values of your table. When changing NPC AI values, only assigning to one will cause inconsistencies. Using a for loop ensures that the full table is iterated through and all values are changed. For more information on for loops in Lua, this article has it.
For loops execute a series of statements based on steps. Remember back to how tables are indexed, and you access these values via table[int]. This makes for loops the logical way to modify multiple NPCs. For a simple example, we will kill all Goombas in the level.
function onLoop() tableOfGoombas = NPC.get(1, -1); if(table.getn(tableOfGoombas) > 0) then for i=1,table.getn(tableOfGoombas) do tableOfGoombas[i]:kill(); end end end
This will iterate through, 1 to table.getn(tableOfGoombas), setting i to the current step value. Hence, why we can access the NPC using tableOfGoombas[i]. Using this ensures that not only all Goombas are killed, but that we're not accessing any out of range values. Also, as added safety, we only execute the loop if there's more than 0 objects in the table. By doing this, we prevent errors as state above. Also, because the table is constantly updated by being in the onLoop(), we can ensure that any freshly spawned Goombas are also insta-killed.
For each loops
While Lua doesn't have a true for each loop, one can be simulated by changing around some of the parameters.
A table contains two values: an index and an object. As an example, a table of 3 Goombas may look something like this:
Recall that for loops take two arguments, a minimum and a maximum so to speak. You can also change these values to represent index-object inside of the table, by using the in keyword in the for loop. For example,
for _,i in pairs(tableOfGoombas) do i:kill(); end
This is the same exact code as above (minus our safety net), the difference is that i is the actual instance of the object instead of the index number in the table. (UNCONFIRMED, MUST TEST) _ in this instance of that index number as shown in our table above. We assign it to _ because we don't care about it in this case. We also use the pairs function to return the table represented as pairs. The pairs in this case are index, object.