Unity: How Adapters can help you write fewer MonoBehaviours

TL;DR: You don't need to duplicate a bunch of code to do the same things to a Image and a SpriteRenderer, or RectTransform and Transform, etc. You can use a simple pattern called the Adapter Pattern. If you've never used it before, this post explains how.

The Problem: Image vs SpriteRenderer

Lets say you want to make a sprite fade out, maybe its a dead monster or a collectible, but in either case you want it to gracefully depart from the screen rather than blink out of existence. Kinda like this eyeball monster from Beastly:


So that's pretty easy right, one way of doing it is with a MonoBehaviour that modifies the sprites alpha value via SpriteRenderer.color. Your class might look something like this:
public class AlphaFaderSprite : MonoBehaviour {
  public SpriteRenderer spr;
  public float fade_percentage;

  void Update() {
    spr.color = new Color(spr.color.r, spr.color.g, spr.color.b,
        fade_percentage);
  }
}
Now, anyone who's used Unity for more than an hour is now screaming internally at how inefficient the above class is. You'd never write code like this in reality, but for the sake of this tutorial lets ignore the specifics and keep things simple.

It is simple, right?

But what happens when you want to do the same thing to a UI element? Like a UI.Image?

Well that's easy too: we can just do the same thing we did for sprites and build a AlphaFaderUI:
public class AlphaFaderUI : MonoBehaviour {
  public Image img;
  public float fade_percentage;

  void Update() {
    img.color = new Color(img.color.r, img.color.g, img.color.b,
        fade_percentage);
  }
}
Great, but now alarm bells are ringing all up in your brain because you just wrote the same class twice.

The only difference between the two classes is that one works with Image and the other works with SpriteRenderer. Usually, this is where something like Polymorphism would come in handy, but unfortunately Image and SpriteRenderer meet in the class tree at Component, which doesn't provide a color member.

So how can we avoid this duplication? We could use Reflection, or if we were using a later version of C# we might use the dynamic keyword. The answer that I want to talk about is the Adapter Pattern.

The Adapter Pattern

First we define the adapter abstract base class, ColorAdapter, this promises to give us a way of modifying the color. This is what our generic components will use instead of SpriteRenderer or Image
public abstract class ColorAdapter {
  public abstract Color color { get; set; }
}
Next we subclass ColorAdapter for both Image and SpriteRenderer, these each simply delegate color modifications to their "adaptee" member variables.
public class AdaptedImage :  ColorAdapter {
  Image adaptee;

  public AdaptedImage(Image adaptee) {
    this.adaptee = adaptee;
  }

  public override Color color {
    get { return adaptee.color; }
    set { adaptee.color = value; }
  }
}

public class AdaptedSpriteRenderer : ColorAdapter {
  SpriteRenderer adaptee;

  public AdaptedSpriteRenderer(SpriteRenderer adaptee) {
    this.adaptee = adaptee;
  }

  public override Color color {
    get { return adaptee.color; }
    set { adaptee.color = value; }
  }
}
Now, in our alpha fade component we simply define AlphaFader to use a ColorAdapter.
public class AlphaFader : MonoBehaviour {
  public ColorAdapter ca;
  public float fade_percentage;

  void Update() {
    ca.color = new Color(ca.color.r, ca.color.g, ca.color.b,
        fade_percentage);
  }
}
And now we can write Alpha Fading logic once and use it for both component types. Mission accomplished.

Pros and Cons

You might be thinking
"We've ended up writing more lines of boilerplate code than we duplicated in the first place!"
... and you'd be right, but remember that this is a toy example. This class could also be used for any color manipulation (for example: flashing a sprite red when taking damage and "greying out"a shop item to signify that the player has insufficient funds could be the same component). If you've got a more complex series of color transformations the benefits are even greater.

We can add other functionalities to our adapter class, such as access to underlying Sprite or Material for even more flexibility. This might limit the number of Unity Components that can make use of the adapter, but that's only because its an abstract class. ColorAdapter doesn't actually need to be abstract, it can be an interface. Which means that you can create a myriad of different adapter types which something like a AdaptedImage can implement, further increasing coverage. An AdaptedImage could be an IColorAdapter, an ISpriteAdapter and an IMaterialAdapter all at once.

Beyond that, its easy to extend this to work with other components. We can define a Adapted3DRenderer for GetComponent<Renderer>().material.color manipulation and reuse our AlphaFade component for 3D objects, we can define a AdaptedText and fiddle with UI.Text colors.

One catch we do need to watch out for here: Where do we construct the ColorAdapters in the first place? For a simple Image/Sprite adapter it might be okay to have a couple of GetComponent<T>() calls in the Start() method of our alpha fading class:
  ColorAdapter ca;

  public void Start() {
    SpriteRenderer spr = GetComponent<SpriteRenderer>();
    Image img = GetComponent<Image>();

    if (spr != null) {
      ca = new AdaptedSpriteRenderer(spr);
    } else if (img != null) {
      ca = new AdaptedImage(img);
    }
  }
but it's easy to see that this might get annoying or complicated as we expand the range of possible adapters. We could try and do this in some kind of baseclass for classes that use the color adapters, but that would limit our ability to use multiple different types of adapter (e.g. some kind of TransformAdapter for RectTransform and Transform manipulation). Its clear that this would be a band-aid and not a solution.

The good news is that in practice this is (usually) a non-issue. This is because (usually) there will be an attached component which references your ColorAdapter holding component. In our first example, the dying monster, this would be the Monster class, which would be working with AlphaFade, we can pass responsibility for initialization to Monster.Start() instead, where we already know that we're dealing with a SpriteRenderer.

Wrapping Up

So, not bad right? The Adapter pattern is very simple, the type of thing that many of you might have already used without knowing it. I'm highlighting it here because I feel like small patterns like this are often overlooked in favor of more fancy techniques. There are larger versions of this problem where one might be tempted to use Reflection to facilitate a Duck Typing solution but, bar some minor issues, you're probably better off writing a little boiler plate and saving yourself the headache.

~Charles

Blogger Templates for Noobs - Part 1 - Template Structure

I've been curious about blogger templates since I started using the site a half decade ago, but I never took the time to learn how to build them.

Recently I've been making changes to this site and I've been picking up a few things about blogger templates, so I'll be sharing things on this site as I learn them.

Setting Up

I'll assume that you have a blog already, so the first step is to visit the dashboard of your blog and click on the "Template" tab on the left-hand side.



Then, click the "Edit HTML" button to get started.

Site Redesign


The site is currently undergoing a big redesign. For the next couple of days pages will be moving around a lot, so I apologize in advance for the inconvenience!

I'm working on a custom Blogger Template, and since I haven't used the API before i'm sort of learning as I go. It doesn't seem to be too difficult, but its still slowing things down a little bit.

Full launch Soon[tm]

#TAGJam22 - Submissions

Leave a comment on this post with a link to your submission.

Submissions must arrive before:


 (The end of TAGJam + a 12 hour grace period to account for differing time-zones)


The winner will be announced 24 hours after submissions close.

#TAGJam22 Begins! - Themes


Random Theme 1
Beast
Definition: An animal, especially a large or dangerous four-footed one.


Random Theme 2
Overpotent
Definition: Too potent or powerful.


Host Theme
Gyoza
Definition: Japanese pan-fried dumplings, filled with ground meat and vegetables and wrapped in a thin dough.


Remember the Bonus Rule:

Bonus Rule: "Show Me Your Moves!"

Show me something that you're already working on! For more information on this bonus rule 

Because its understood that entries adhering the bonus rule will be at different stages of completion, all entries will be judged based on the following factors, with the first being the most important:

How interesting is the overall idea?
How well are the themes incorporated?
How well is the idea executed?

Your submissions must come in by
26th June @ 23:59 GMT / [25th June @ 16:59 PST]

On your marks.
Get Set.
Go!

#TAGJam22 - Show Me Your Moves! - Begins in 24 Hours!

The Arbitrary Gamejam Comes Home 2: Electric Boogaloo



TAG has just come off a short one-month hiatus, and its kicking off right here. This time its a 7 Day Jam running from:

20th June @ 00:00 GMT
[19th June @ 17:00 PST]
UNTIL
26th June @ 23:59 GMT
[25th June @ 16:59 PST]


This TAG is has a special bonus rule that we're announcing early:

Bonus Rule: "Show Me Your Moves!"

This TAG, I want to see what you're working on! Instead of building a whole new game for the jam, show off something that you're currently working on or have worked on recently.

During the Jam, spend those 7 days creating a special #TAGJam22 build of your project that incorporates the themes. For example:

  • If your game is a Platformer, you could build a short bonus level or powerup that incorporates the TAG themes.
  • If your game is an RPG, you could add a short quest, skill, monster or item which incorporates the TAG themes.
  • If your game is a Racer, you could add a custom TAG vehicle paint job, or a new track.
  • If your game is a Puzzler, you could add a special TAG puzzle or a simple game mode variation.

Remember, these are just suggestions: feel free to incorporate TAG in whatever way you like! You have no obligation to keep whatever you add during the jam in your project forever, this is just for fun!

If you happen to be working on a different Jam during TAG, you can submit that game for TAG as well. Bonus points if you can work in the themes of both jams somehow!

As always, you can disregard the bonus rule and build something totally new. TAG is about freedom!

The themes will be announced when the Jam starts, and a submissions post will come shortly after.

See you all in a few hours!

~Tick

- - -

If you are unfamiliar with TAGJam, take a look at www.thearbitrarygamejam.com. For a rough introduction: The Arbitrary Gamejam is a community gamejam where the host changes every jam, encouraging everyone to give small indies a little extra exposure. TAGJam runs every 2 months and the exact rules of the jam are decided by the host.