-
Notifications
You must be signed in to change notification settings - Fork 23
ActionTask: Executing an Action After a Delay or at an Interval
ActionTasks are another great example of the flexibility of ZestKit. It's not just about tweening! ActionTasks let you pass in an Action that will be called at different intervals depending on how you set it up. The ActionTask class contains a few helpful static constructor methods will automatically cache the ActionTasks for easy reuse. All of the ITweenable methods apply here so you can pause/resume/stop the ActionTask at any time.
When you pass around an Action with Mono/.NET in most cases there is an allocation penalty to pay. Let's take a look at a common use pattern for Actions and see where the sneaky allocation occurs:
public class MyClass
{
void methodThatTakesAnAction( Action action )
{}
void someRandomMethod()
{}
void Start()
{
// the following two lines will result in an allocation
methodThatTakesAnAction( someRandomMethod );
methodThatTakesAnAction( () => { someRandomMethod(); } );
}
}With a tiny bit of trickery we can avoid the allocation. This trick is what the ActionTask uses behind the scenes. Take note that you can still use the pattern above with an ActionTask. There is nothing stopping you from doing so. ActionTask just provides an alternate pattern that you can follow to avoid the allocation.
Let's take the example above and make it allocation free using an ActionTask. We'll illustrate calling a method after a delay just once and also calling a method every 2 seconds.
public class MyClass
{
void methodThatTakesAnAction( Action action )
{}
void someRandomMethod()
{}
void Start()
{
// the Action will be called after a 1 second delay
ActionTask.afterDelay( 1f, this, task =>
{
var myClassReference = task.context as MyClass;
myClassReference.someRandomMethod();
} );
// the Action will be called every 2 seconds
ActionTask.every( 2f, this, task =>
{
var myClassReference = task.context as MyClass;
myClassReference.someRandomMethod();
} );
}
}If you didn't spot it, the way ActionTask avoids the allocation is by allowing you to set a context. Context can be any object but in most cases it will be the class containing the code. The context is then available in the task.context property. You can cast it back to the proper type and use the reference to do whatever you want.
The trick to avoid the allocation is to not have any references in the Action (which is a closure). If you have any references a new object will need to be created with copies of all the references in the Action. That is the situation we have in the first example. There is a reference to this in the Action and .NET needs to copy the reference so that when the Action is called it is preserved. By using the ActionTask's context property we avoid the reference since it is being passed in as a parameter.