Creating the ActionList System

This is the entire process I went through when creating the ActionList system. I recommend reading through Scripting a System and Scripting a Class before reading through this.

To start off, I define a system and a global table to hold all of the actions. _G.ActionListTable = {} function Initialize end function Update(dt) end Next I need a class to hold of the individual actions in a sequence. In the constructor I make a local table to hold the entire sequence. I will also need to save the game object's id to check if it is dead. ActionSequence = {} function ActionSequence:ActionSequence(objectId) if objectId ~= nil then self.objectId = objectId end self.sequence = {} end CreateClass("ActionSequence", ActionSequence) Now that the basic constructor is working, I'm going to tackle the core of the action system, which is the property method. This method needs to take a variable reference, increment, total time, and an easing function. function ActionSequence:Property(ref, incr, totalTime, func) end Now I need to construct an object to store in the sequence table to store all of the variables that were passed in. It also needs to have a function to be called when the Action updates. Here is the code to create such an object. local propTable = {} propTable.type = "Property" propTable.totalTime = totalTime propTable.begin = 0 propTable.ref = ref propTable.incr = incr function propTable.call(percent) propTable.ref:assign(func(propTable.totalTime * percent, propTable.begin, propTable.incr, propTable.totalTime)) end To explain this, first I create a table to hold all of the variables. I need the type to distinguish between different actions. Most of the other properties are simply set to the variables that were passed in. The two that are not are begin and call function. The begin variable is left at zero because we will need to initialize it later.

Onto the function, it takes in a percent as a parameter because we are going to be using it in conjunction with the OverTime function in the global library. Inside of the function I am using the assign function to save a value into the saved variable reference, then calling the easing function with the parameters it wants. (See Easing)

Now that that's out of the way, I insert the table into the sequence list using table.insert(self.sequence, propTable) Here is the whole function: function ActionSequence:Property(ref, incr, totalTime, func) local propTable = {} propTable.type = "Property" propTable.totalTime = totalTime propTable.begin = 0 propTable.ref = ref propTable.incr = incr function propTable.call(percent) propTable.ref:assign(func(propTable.totalTime * percent, propTable.begin, propTable.incr, propTable.totalTime)) end table.insert(self.sequence, propTable) end Now that I can successfully add properties to the sequence, I need a way to update them.

For the update function I will get the sequence at the top of the list, set the begin variable and run it using the OverTime function. function ActionSequence:Update --Get the first sequence local curTable = self.sequence[1] --No need to update if there are no tables if curTable == nil then coroutine.yield else if curTable.type == "Property" then --Set the begin value and run the sequence curTable.begin = Value(curTable.ref) OverTime(curTable.totalTime, curTable.call) end --Pop it off the list table.remove(self.sequence, 1) end end The reason I wait to initialize the begin value is to make sure that I am working off of the latest data from the object. I.E If an object has multiple actions modifying its Transform position, I need the current position of the object before I start changing it.

Now that the update function is done, I need to make a change to the constructor to add the ActionSequence to the global table.

This is the code I will add: local tbl = { Action = self, Update = MakeCoroutine(self.Update) } table.insert(_G.ActionListTable, tbl) This just creates a basic table that stores its self and a co-routine for its update function.

Here is the full constructor: function ActionSequence:ActionSequence(objectId) if objectId ~= nil then self.objectId = objectId end local tbl = { Action = self, Update = MakeCoroutine(self.Update) }  self.sequence = {} table.insert(_G.ActionListTable, tbl) end The final step is to change the system's update function to update all of the ActionSequences. function Update(dt) local remTable = {} for k,v in pairs(ActionListTable) do    if v.Action.objectId ~= nil and Space.Level:GetGameObject(v.Action.objectId) == nil then table.insert(remTable, k)    else v.Update:resume(v.Action) end end for k,v in pairs(remTable) do    table.remove(ActionListTable, k)   end end Here is where I check if the object's Id is still valid, and if it is I resume the ActionSequence's coroutine. At the end of the loop I remove all of the Actions that were no longer valid.