Unity Aspect Oriented Programming

Unity 16/May/2023 Tue

Recently, a customer asked how to feed profiler counters without actually adding 'intrusive code'.

So, the first questions is, what does 'intrusive code' means? They refered to the act of adding more code to the working source files. For reference, we frequently run into this scenario when:

  1. Printing debug information to the console. Such as for tracking the value of a variable or the triggering of events. Traditional Debug.Log examples.
  2. Enclosing sections of code with profiling markers for measuring the time, calls, hierarchy and other metadata. Traditional ProfilerMarker implementation.

And less frequently, but stil a common example with profiler counters. Documentation here.

A simple firing script would look like this:

void Fire()
{
    Sound.Play();
    projectile = Instantiate(Bullet);
    projectile.velocity = projectile.transform.forward * bulletSpeed;
}

In case, we want to measure it with the built-in profiler and keep track of the number of bullets, and print to console when this method is invoked, the method would look like below:

static readonly ProfilerMarker s_CustomMarker = new ProfilerMarker("Gun.Fire");
void Fire()
{
    s_CustomMarker.Begin();
    Sound.Play();
    projectile = Instantiate(Bullet);
    projectile.velocity = projectile.transform.forward * bulletSpeed;
    CustomProfilerModule.BulletCount.value += 1;
    Debug.Log("Firing the gun");
    s_CustomMarker.End();
}

In my opinion, this makes the code feel and look less cleaner. Also, we are combining tasks in a single place, thus, adding complexity to it.

After googling for this type of inquiry, I found there's something called Aspect-Oriented Programming (AOP) and I also found a github user that created a framework for working with this paradigm in Unity. github.com/H1M4W4R1/Unity-Aspect-Oriented-Programming

I'm not going into the details of this framework, as H1M4W4R1 the important bits of it in the repo README.md.

This framework allowed me to achieve the goal of profiling and debugging code with just decorating the target method as below:

[BulletCounter]
[AddProfilerMarker]
void Fire()
{
    Sound.Play();
    projectile = Instantiate(Bullet);
    projectile.velocity = projectile.transform.forward * bulletSpeed;
}

If you'd like to explore the test I ran, please visit the following repository: github.com/seprab/Unity-AOP-proof

Things that I'd like to do:

  • Validate how it behaves in real projects.
    • What's the perfomance impact when iterating in the editor?
    • Compatibility with different Unity versions and target platforms.
    • Compatibility with asmdefs and dlls.
  • How to debug it?
  • Conditional code processing.
  • And I guess there might be unkown limitations, such as with Fast Enter Play Mode, backend compiling configuration, code stripping, etc.

Tags: