1242798397750262165Orange_lambda.svg.med

C# in Unity3D – Dynamic Methods with Lambda Expressions

I come from a .NET application developer background, and it’s the growing popularity of Unity3D and its C# support that’s really let me make the leap from previous web developer career into the cooler-to-mention-at-parties world of game development. Something I often run into when working with C# in Unity is that other developers might not be aware of some of the more advanced language features that C# brings to the table. I thought I’d start a regular series highlighting some of the language tools you may not know exist.

If you’re a C# guru, then carry on soldier!

Nothing to see here.

The first C# language feature I’m going to talk about is Lambda support, because events and delegates are for chumps.

Lambda? Like, from Half Life? Not really. Lambda expressions are a  language feature that cleanly wrap up in-line functionality, meaning you don’t need to worry about defining delegates, or worry about attaching and unattaching events for callbacks. The flexibility goes much further than just an alternative for managing events.

At their core, they look like this:

(arguments) => expression

Ok, that doesn’t tell you very much, let me give you an example.

using System;

void someMethod()
{
  Action sayHello = () => { Debug.Log("Hello"); };
  sayHello();

  Action<int> sendToLog = (arg) => { Debug.Log(arg); };
  sendToLog(5);
  sendToLog(-10);
  sendToLog(42);
}

Lambda expressions let you dynamically bind functionality to variables.

The Action generic type is how you define the signature of your expression. The generic parameters define the argument types, for example Action will be a method that takes no arguments, and Action<bool> will mean the expression has a single boolean argument, while Action<string, int?> will be an expression that has a string and a nullable integer. Closely related to Action<> is Func<>, which works pretty much the same, except is has a return value. The last type used in the the generic is that return type. For example, Func<int, bool> will have one integer argument, and return a boolean. You can have any number of arguments (and only one return value in the case of a Func<>).

If you wanted to pass multiple arguments to an expression, it looks more like this

  Action<int, string, bool> sendToLog = 
    (value, description, doLog) => 
  {
    if (doLog) Debug.Log("Logging out " 
      + value + " and " 
      + description); 
  };

Also, you cannot use var when instantiating an Action<> or a Func<>, because there is no way for the compiler to infer the types used from a Lambda expression.

You can use this in a variety of situations – one I often use them for us one-shot callbacks. Say you want to pass a callback to a method to notify when a process save is complete, instead of using delegates and events, you can do this:

var mySaveService = new SaveService();
mySaveService.doSave(success => 
  {
    if (success) Debug.Log("Hooray!");
    else Debug.Log("Error :( ");
  });

Instead of subscribing to an event, which we may need to detach later, we can pass in a one-shot method that will be disposed of when complete. The signature of the save method would look like:

void doSave(Action<bool> callback);

As another example, let’s look at how we could implement a simple filter method using a Func<> that returns a boolean.

void doFilter(IEnumerable<int> list, Func<int, bool> filter)
{
  var filteredList = new List<int>();
  foreach (var value in list)
  {
    if (filter(value)) filteredList.add(value);
  }

  return filteredList;
}

var evenNumbers = doFilter(someListOfNumbers, 
    (value) => { return (value % 2) == 0; });
var oddNumbers = doFilter(someListOfNumbers, 
    (value) => { return (value % 2) == 1; });

So by changing the expression we pass to the filter, we can get different different results. But, things get even cooler, because inline expressions can even bind variables from outside their scope. Let’s look at the save example I gave earlier. Say we want to pass in a save start time, then output it to the logs when we’re done. You don’t need to pass the filename into the dynamic expression, C# can take care of that for you.

void write(string filename)
{
  var mySaveService = new SaveService();
  var startTime = DateTime.Now;
  mySaveService.doSave(filename, success => 
    {
      if (success) 
         Debug.Log("Hooray! We saved " 
                    + filename + " starting at " 
                    + startTime + " ending at " 
                    + DateTime.Now);
      else Debug.Log("Error :( ");
    });
}

Even though the write() function will return, any variables within its scope that are passed used in the expression will hang around until it’s disposed of too.

Just to give a quick example in unity, say you wanted to have a quick and easy way to perform a task after n seconds have passed. With a simple wrapper around unity’s co-routine feature, we can implement it using and Action.

private IEnumerator waitThenCallback(float time, Action callback)
{
   yield return new WaitForSeconds(time);
   callback();
}

void Start()
{
  splashScreen.show();

  StartCoroutine(waitThenCallback(5, () => 
         { Debug.Log("Five seconds have passed!"); }));
  StartCoroutine(waitThenCallback(10, () => 
         { Debug.Log("Ten seconds have passed!"); }));
  StartCoroutine(waitThenCallback(20, () => 
  {
    Debug.Log("Twenty seconds have passed!"); 
    splashScreen.hide();
  }));
}

So there is a very brief taste of how you can use Lambda expressions in C#, and also in Unity3D. Actions can be a quick shortcut to callbacks, instead of using the standard event system, and functions allow you to create powerful helper methods.


Fatal error: Call to undefined function the_post_navigation() in /home/blockypi/blockypixel.com/wp-content/themes/voltata/single.php on line 19