Reducing Draw Calls Using a Simple Texture Packer

When we started making Academia, we didn’t really plan out how are we going to manage our sprites. We just did the quickest way which was to make them individually and load them in the game. All of our game world sprites are stored in StreamingAssets. We load them dynamically when the game is run. This is different from the normal way using an imported texture. We did it this way in preparation for modding support. I envision that modders could then add their own folders and provide them the mechanism to override the base game images.

Ideally, all game sprites should be in a single big texture. This will allow you to use a common material throughout your game objects so that dynamic batching can indeed batch. Now that the game got bigger, it’s harder to put all of our sprites in one single atlas. Our artist wouldn’t agree to this as it’s a lot of work. Additionally, we no longer have the time. We’re releasing our Early Access this September 8.

SomeObjects
A few samples of our sprites. We have folders of these.

While coming up with solutions, I thought what if I could pack the images dynamically instead and use the generated atlas. It should be simple enough to recompute the UVs of the packed sprites. I scrounged the internet on algorithms on how to optimally pack rectangles in a bigger one. Turns out that this is an interesting problem. There are numerous papers about this. It also turned out that I no longer have to roll up my own packer. Unity already made one.

It needs some help, though. I needed something that keeps track of the packed textures. I needed a way to get the same sprite out of the packed one. Time to code!

Here’s the class that abstracts an “entry” of a packed texture:

    public class PackedTextureEntry {

        private readonly Texture2D atlas;
        private readonly Rect uvRect;
        private readonly Rect spriteRect;
        private readonly Sprite sprite;

        public PackedTextureEntry(Texture2D atlas, Rect uvRect) {
            this.atlas = atlas;
            this.uvRect = uvRect;
            this.spriteRect = new Rect(this.uvRect.x * this.atlas.width, this.uvRect.y * this.atlas.height,
                this.uvRect.width * this.atlas.width, this.uvRect.height * this.atlas.height);
            this.sprite = Sprite.Create(this.atlas, this.spriteRect, new Vector2(0.5f, 0.5f), 768);
        }

        public Texture2D Atlas {
            get {
                return atlas;
            }
        }

        public Rect UvRect {
            get {
                return uvRect;
            }
        }

        public Rect SpriteRect {
            get {
                return spriteRect;
            }
        }

        public Sprite Sprite {
            get {
                return sprite;
            }
        }

        public Sprite CreateSprite(Vector2 pivot) {
            return Sprite.Create(this.atlas, this.spriteRect, pivot, 768);
        }

    }

Basically, it’s just a container of the generated atlas and the UV coordinates of a particular sprite entry. The Rect passed in the constructor is a normalized UV (values are zero to one). Sprites, however, are created using pixels. So we need a new Rect for this which is just the UV rect multiplied by the dimensions of the atlas. This class also has a pre-generated Sprite pivoted at the center.

The following class is the texture packer itself:

    public class TexturePacker {

        // Contains the associated names of the added texture so we can easily query its entry after packing
        private List<string> names = new List<string>();

        // This contains the textures to pack
        // Used a list here so we could easily convert to array during packing
        private List<Texture2D> textures = new List<Texture2D>();

        // Keeps track of the packed entries
        private Dictionary<string, PackedTextureEntry> entriesMap = new Dictionary<string, PackedTextureEntry>();

        private Texture2D atlas;

        public TexturePacker() {
        }

        public void Add(string key, Texture2D texture) {
            this.names.Add(key);
            this.textures.Add(texture);
        }

        public void Pack() {
            this.atlas = new Texture2D(2, 2, TextureFormat.ARGB32, false); // Will expand on packing
            Rect[] rects = this.atlas.PackTextures(this.textures.ToArray(), 0, 8192, true);

            // Populate entries
            this.entriesMap.Clear();
            Assertion.Assert(this.names.Count == this.textures.Count);
            for(int i = 0; i < this.names.Count; ++i) {
                this.entriesMap[this.names[i]] = new PackedTextureEntry(this.atlas, rects[i]);
            }

            // Clear to save memory
            // These textures may also be released
            this.textures.Clear();
        }

        public PackedTextureEntry GetEntry(string key) {
            return this.entriesMap[key];
        }

    }

Usage is self explanatory. Create an instance of the packer. Add the textures that you want to pack, each associated with a string key. Call Pack(). Use GetEntry() to get an instance of PackedTextureEntry associated with the sprite. Use PackedTextureEntry.Sprite property to have access of the sprite that is from the packed texture.

TexturePacker packer = new TexturePacker();

// Let's just say you have a library of textures associated by name
foreach(TextureEntry entry in entries) {
    packer.Add(entry.Name, entry.Texture);
}

packer.Pack();

// Get a packed entry and use its sprite
PackedTextureEntry packedEntry = packer.Get("Grass");
spriteRenderer.sprite = packedEntry.Sprite;

And that’s it! It’s really simple but this thing helped in batching by a lot.

YuugeBatch
Yuuuge batch! This is from the frame debugger.

 

Advertisements

Some Utility Code

Every now and then, I’d like to share some of my small utility classes that I’ve made a long time ago but are still being used until now. The following are what I call some of my mini utilities. They can be used in your own Unity projects.

(Note that I’ve removed method description comments so that the code part looks more compact.)

Comment Component

A lot of times have come up where I have an object or prefab that contains a lot of components. Sometimes, I want to be able to note about the values I’ve entered for some component or state why the component is there. Sadly, Unity doesn’t have a feature where you can comment on components (let me know if you know one). However, it’s quite easy enough to roll your own “comment” component.

public class Comment : MonoBehaviour {

    [SerializeField]
    private string text;

    void Awake() {
        DestroyImmediate(this); // auto destroy to save memory
    }

    public string Text {
        get {
	    return text;
	}

        set {
	    this.text = value;
        }
    }

}

// This is the editor script. Place it under an Editor folder.
[CustomEditor(typeof(Comment))]
public class CommentEditor : Editor {

    private Comment targetComponent;

    void OnEnable() {
        this.targetComponent = (Comment)this.target;
    }

    public override void OnInspectorGUI() {
        if(targetComponent.Text == null) {
            // use an empty string so that it won't throw null pointer exception
            targetComponent.Text = "";
        }

        targetComponent.Text = GUILayout.TextArea(targetComponent.Text, GUILayout.Height(100), GUILayout.MinWidth(200));
    }

}

To use, just add the Comment component and type your comment text in the textbox.

CommentComponent

UI Image Sprite Animation

I tried to use the animation timeline for animating sprites in a UI Image. But it didn’t work or maybe I was using it wrong. I got impatient, so I turned to code.

public class ImageAnimation : MonoBehaviour {

    [SerializeField]
    private Image image;

    [SerializeField]
    private float framesPerSecond = 15;

    [SerializeField]
    private Sprite[] sprites; // The sequence of sprites comprising the animation

    private float polledTime;

    private int currentFrameIndex;

    private void Awake() {
        Assertion.AssertNotNull(this.image);
        Assertion.Assert(this.framesPerSecond > 0);

        this.polledTime = 0;

        // Reset
        this.currentFrameIndex = 0;
        SetSprite(this.currentFrameIndex);
    }

    private void Update() {
        this.polledTime += UnityEngine.Time.deltaTime;

        // We didn't cache this so we can see the effect of framesPerSecond on the fly like tweaking it in editor
        float timePerFrame = 1.0f / this.framesPerSecond;

        while(this.polledTime > timePerFrame) {
            this.polledTime -= timePerFrame;

            // Show next frame
            this.currentFrameIndex = (this.currentFrameIndex + 1) % this.sprites.Length;
            SetSprite(this.currentFrameIndex);
        }
    }

    private void SetSprite(int index) {
        this.image.sprite = this.sprites[index];
    }

}

This is very straightforward to use. Just specify an Image target, a frame rate, and the sequence of sprites to show. That’s it. It will play on loop. Nothing fancy. I’ve use this on the game over screen of Academia.

GameOver

SelectionSequence

Political Animals had a common UI where you select an item from a finite set of items using next and previous buttons. I’ve noticed that I’m repeating the same code spread across different UI screens. So I made a generic class for this.

public class SelectionSequence<T> {

    private readonly List<T> items = new List<T>();

    private int currentIndex = 0;

    public SelectionSequence(T[] array) {
        this.items.AddRange(array);
        Reset();
    }

    public void Reset() {
        this.currentIndex = 0;
    }

    public void MoveToNext() {
        this.currentIndex = (currentIndex + 1) % this.items.Count;
    }

    public void MoveToPrevious() {
        int decremented = this.currentIndex - 1;
        this.currentIndex = decremented < 0 ? this.items.Count - 1 : decremented;
    }

    public T Current {
        get {
            return this.items[this.currentIndex];
        }
    }

    public void Select(int index) {
        this.currentIndex = index;
    }

    public void Select(T item) {
        for(int i = 0; i < this.items.Count; ++i) {
            if(this.items[i].Equals(item)) {
                Select(i);
                return;
            }
        }

        throw new Exception("Can't find the item to select: " + item.ToString());
    }

    public int Count {
        get {
            return this.items.Count;
        }
    }

    public T GetAt(int index) {
        return this.items[index];
    }

}

// Usage
// Say you have an array of LanguageOption
LanguageOption[] languages = ...;
SelectionSequence<LanguageOption> langSequence = new SelectionSequence<LanguageOption>(languages);

// When next button is clicked
langSequence.MoveToNext();

// When previous button is clicked
langSequence.MoveToPrevious();

// Do something with current selection
ChangeLanguage(langSequence.Current);

This kind of selection have come up in Academia, too.

SelectionExample

These are just some of my cute little classes. They’re small but they have been very useful. Here’s some programming tip, think of making classes like making a new invention or a device. That way, you will think in terms of narrowing functionality as much as possible and expose only appropriate public methods. This will also make your classes more modular. Over time, you will develop a library of robust and reusable code.

Render Two Sprites in One Shader

Most of the character faces in Academia are generated in a procedural way. Each character has its own combination of face and head. Heads can either be hair or construction hat if they are workers. Both faces and heads are contained in a single texture so Unity may apply batching.

FaceHair
Part of the faces and heads texture

To generate a character, you simply render a face and render its head on top of it.

Characters

This can be done with two sprites. However, there are disadvantages. First, more draw calls. Second, it looks weird when the characters are overlapping because of z fighting. I needed a way to render both the face and head in only one sprite. This reduces draw calls and prevents weird z fighting. Fortunately, this is easy enough to do using a custom shader.

While working on our custom quad mesh where the characters would be displayed on, I realized that vertices can have more than one UV. This means that I can use the first UV for the face and use a second UV to draw the head. These two renders can be done within a single shader.

Shader "Common/TwoUvsLayeredTexture"
{
	Properties
	{
		_Texture("Main Texture", 2D) = "white" {}
	}

	SubShader
	{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		ZWrite Off Lighting Off Cull Off Fog{ Mode Off } Blend SrcAlpha OneMinusSrcAlpha
		LOD 110

		Pass
		{
			CGPROGRAM
			#pragma vertex vert_vct
			#pragma fragment frag_mult 
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"

			sampler2D _Texture;

			struct vin_vct
			{
				float4 vertex : POSITION;
				float4 color : COLOR;
				float2 texcoord0 : TEXCOORD0;
				float2 texcoord1 : TEXCOORD1;
			};

			struct v2f_vct
			{
				float4 vertex : SV_POSITION;
				fixed4 color : COLOR;
				float2 texcoord0 : TEXCOORD0;
				float2 texcoord1 : TEXCOORD1;
			};

			v2f_vct vert_vct(vin_vct v)
			{
				v2f_vct o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.color = v.color;
				o.texcoord0 = v.texcoord0;
				o.texcoord1 = v.texcoord1;
				return o;
			}

			fixed4 frag_mult(v2f_vct i) : SV_Target
			{
				fixed4 col1 = tex2D(_Texture, i.texcoord0) * i.color;
				fixed4 col2 = tex2D(_Texture, i.texcoord1) * i.color;

				fixed4 output;
				output.rgb = (col1.rgb * (1.0f - col2.a)) + (col2.rgb * col2.a);
				output.a = min(col1.a + col2.a, 1.0f);
				return output;
			}

			ENDCG
		}
	}
}

The magic is in frag_mult(v2f_vct i). Actually, I’m not too sure if that’s the proper way to blend two images. I’m not very good with shaders. I arrived with that using trial and error. Let me know if there’s a better way.

[Edit] Some people pointed out that it’s generally not ok to have conditionals in shader code. Thus, I’ve updated the code with a better one. This is why I blog.

A Generic Duration Timer Class

In game development, there are a lot of instances where you would like to do the following:

  • Wait for X seconds
  • Fly for X seconds
  • Fire for X seconds
  • Move forward for X seconds

The common element in these actions is the concept of a time duration. The most common solution that I see in tutorial pages looks like the following:

public class SomeComponent : MonoBehaviour {
    [SerializeField]
    private float durationTime; // the time to wait or go through

    private float polledTime;

    void Awake() {
        this.polledTime = 0;
    }

    void Update() {
        this.polledTime += Time.deltaTime;

        if(this.polledTime >= this.durationTime) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.polledTime / this.durationTime;
        // do something with ratio like interpolation (lerp)
    }
}

This is good enough, but we can do something better. Imagine if you are maintaining more than one duration time. You’d probably do something like this:

public class SomeComponent : MonoBehaviour {
    [SerializeField]
    private float waitDurationTime;

    private float waitPolledTime;

    [SerializeField]
    private float fireDurationTime;

    private float firePolledTime;

    void Update() {
        UpdateWait();
        UpdateFire();
    }
    
    private void UpdateWait() {
    	this.waitPolledTime += Time.deltaTime;

        if(this.waitPolledTime >= this.waitDurationTime) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.waitPolledTime / this.waitDurationTime;
        // do something with ratio
    }
    
    private void UpdateFire() {
    	this.firePolledTime += Time.deltaTime;

        if(this.firePolledTime >= this.fireDurationTime) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.firePolledTime / this.fireDurationTime;
        // do something with ratio
    }
}

We can see here that the floating point arithmetic is repeated for each timed duration domain. A simple solution to manage this is to refactor and create a reusable DurationTimer class.

using UnityEngine;

/**
 * Generic class for implementing timers (specified in seconds)
 */
public class DurationTimer {
    private float polledTime;
    private float durationTime;

    /**
     * Constructor with a specified duration time
     */
    public DurationTimer(float durationTime) {
        Reset(durationTime);
    }

    /**
     * Updates the timer
     */
    public void Update() {
        this.polledTime += Time.deltaTime;
    }

    /**
     * Resets the timer
     */
    public void Reset() {
        this.polledTime = 0;
    }

    /**
     * Resets the timer and assigns a new duration time
     */
    public void Reset(float durationTime) {
        Reset();
        this.durationTime = durationTime;
    }

    /**
     * Returns whether or not the timed duration has elapsed
     */
    public bool HasElapsed() {
        return Comparison.TolerantGreaterThanOrEquals(this.polledTime, this.durationTime);
    }

    /**
     * Returns the ratio of polled time to duration time. Returned value is 0 to 1 only
     */
    public float GetRatio() {
        if(Comparison.TolerantLesserThanOrEquals(this.durationTime, 0)) {
            // bad duration time value
            // if countdownTime is zero, ratio will be infinity (divide by zero)
            // we just return 1.0 here for safety
            return 1.0f;
        }

        float ratio = this.polledTime / this.durationTime;
        return Mathf.Clamp(ratio, 0, 1);
    }

    /**
     * Returns the polled time since it started
     */
    public float GetPolledTime() {
        return this.polledTime;
    }

    /**
     * Forces the timer to end
     */
    public void EndTimer() {
        this.polledTime = this.durationTime;
    }

    /**
     * Returns the durationTime
     */
    public float GetDurationTime() {
        return this.durationTime;
    }

}

What we did here is we simply contained the duration timer variables and routines in a separate class. Take note of the usage of Comparison functions in HasElapsed() and GetRatio(). Read here for the reason why.

Let’s look at the basic usage using of this class:

public class SomeComponent : MonoBehaviour {
    [SerializeField]
    private float duration; // the time to wait or go through

    private DurationTimer timer;

    void Awake() {
        this.timer = new DurationTimer(this.duration);
    }

    void Update() {
        this.timer.Update();

        if(this.timer.HasElapsed()) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.timer.GetRatio();
        // do something with ratio like interpolation (lerp)
    }
}

By using this class, we reduce the clutter of floating point arithmetic (increment, check, compute ratio). We use the functions of the class instead, making it more readable. See how it looks like when it’s used for more than one timed duration:

public class SomeComponent : MonoBehaviour {
    [SerializeField]
    private float waitDuration; // the time to wait or go through

    private DurationTimer waitTimer;
    
    [SerializeField]
    private float fireDuration;
    
    private DurationTimer fireTimer;

    void Awake() {
        this.waitTimer = new DurationTimer(this.waitDuration);
        this.fireTimer = new DurationTimer(this.fireDuration);
    }

    void Update() {
        UpdateWait();
        UpdateFire();
    }
    
    private void UpdateWait() {
    	this.waitTimer.Update();

        if(this.waitTimer.HasElapsed()) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.waitTimer.GetRatio();
        // do something with ratio like interpolation (lerp)
    }
    
    private void UpdateFire() {
    	this.fireTimer.Update();

        if(this.fireTimer.HasElapsed()) {
            // time's up
            // maybe reset or do something since duration is up
            return;
        }

        float ratio = this.fireTimer.GetRatio();
        // do something with ratio like interpolation (lerp)
    }
}

We can also reuse this class if we’re making other frameworks that uses time.

public class WaitAction : FsmAction {
    private DurationTimer timer; // pretty cool huh?
	
    ...
}

Mar 17-22 Updates

New Tutorial Sequence

I’ve shown the game to a lot of people by now. When I’m showing it, I don’t want to specifically point out to the player what he/she should do. I’ve always wanted my games to show itself how to play it. In this aspect, my evaluation so far is not so good. I’ve spent the beginning of this week changing the initial tutorial.

3-22-2014a
Now it says where to tap
Tap the dangerous button when you're ready
Tap the dangerous button when you’re ready

The tutorial sequence looks better to me now. But it means nothing unless actual players verify it.

Tiles that shouldn’t be walked upon

Now that I have the actual tile assets with houses, rivers, and other stuff, it’s also the time to update the tilemap data that some objects are no longer ‘passable’. It’s kind of like setting collision boxes to these tiles but instead of boxes, a number is just assigned to each of these tiles which signifies that units can’t pass through here. Here’s a screenshot from Tiled:

Black means 'no'
Black means ‘no’

For those of you who doesn’t know, Tiled is a free software that is used mainly for tile based games or applications. I’m using a different layer with solid colored tiles that acts as a data file for the game to determine which parts are passable or not. Now that the map has blocked areas like houses and trees, I changed the solid tiles to transparent ones so that I can see through it. All I have to do then is set this layer as the topmost and paint those blocked areas with a special tile (the black one). This special tile is read as ‘unpassable’ in the game.

Unit Upgrade Items

The Upgrade screen is far from complete. For me, it’s actually the most boring thing to do. Maybe because it’s all just UI and data. It’s like working with a web application all over again with MVC patterns and shit. It’s so unexciting but it has to be done. Right now, the ‘unit upgrades’ is work in progress.

Unit upgrades using dummy icons
Unit upgrades using dummy icons

Mind Museum

I also have great news. I was invited to have a game exhibit in Mind Museum! I will showcase Warrior Defense, of course. It’s all happening on March 30. Other Filipino game developers will be there as well.

Working Behaviour Tree From Editor

A lot has happened to Banana Tree and I’m quite happy with its current state. More variable types are now supported in the node inspector. I prioritized the variables that are used in existing actions so I could test them as soon as possible.

While thinking about how action nodes should pass variable values to other nodes, I realized that it’s not effective for nodes to have references of other nodes. Playmaker has this concept of “FSM level” variables. These variables can be referenced by any states in the FSM. Though initially not planned, I think I needed this type of feature in Banana Tree as well for cross node variable communication. I need to have some kind of behaviour-level variables that can be referenced by any action nodes in the behaviour. I’m surprised that the implementation is really not that hard.

Node level and behaviour level variables
I have this action class “TestVariableAction” which is used to test all supported variables. On the right are the behaviour level variables which can be referenced by action level variables.

I use custom classes to add variables to an action node. The editor identifies these properties and renders the appropriate UI controls. Here’s the implementation of TestVariableAction:

    [ActionGroup("Tests")]
    public class TestVariableAction : BntAction {

        /**
         * Constructor
         */
        public TestVariableAction(string name) : base(name) {
        }

        public BntString StringVariable {get; set;}

        public BntString StringTest2 {get; set;}

        public BntGameObject GameObjectTest2 {get; set;}

        public BntFloat FloatVar {get; set;}

        public BntFloat FloatVar2 {get; set;}

        public BntVector3 PositionVar {get; set;}

        public BntVector3 PositionVar2 {get; set;}

        public BntTransform TargetVar {get; set;}

        public BntTransform TargetVar2 {get; set;}

        public BntBool BoolVar {get; set;}

        public BntBool BoolVar2 {get; set;}

        #region implemented abstract members of BntNodeAdapter
        public override BntResult Start() {
            return BntResult.SUCCESS;
        }

        public override BntResult Update() {
            return BntResult.SUCCESS;
        }

        public override BntResult FixedUpdate() {
            return BntResult.SUCCESS;
        }
        #endregion

    }

If you want to do something like this, I suggest you use custom classes instead of directly using string, float, bool, GameObject, etc. Custom class variables adds indirection so you can add some intermediary features to your variables like referencing another variable when its value is requested or add some more information to the variable. It’s also much easier to assign values to it on game runtime since the class variables can be implemented as serialized and you’ll just have to assign it directly to the action node instance properties through reflection.

With this in place, I was able to instantiate a minimal behaviour tree at runtime from serialized data. What it does is it moves an actor back and forth in two positions, Home and Destination.

Minimal working behaviour... Yey!
Minimal working behaviour… Yey!

The editor is almost complete. When I say complete, it means it must be verified which further means that it should be used in a working game. Tomorrow, I’ll replicate the existing enemy behaviour in Warrior Defense using this editor. It’s not that easy because I’ll have to implement lots of action classes. I’m sure there will be some hiccups but I’ll find a way.

Experimental Node Inspector

Today, I’ve been working on the node inspector of a BananaTree node. The inspector displays information about the selected node. It’s most important use, though, is to set variables of a node. Every property in an action that has a public getter and setter would be considered as a variable. This information could be retrieved through reflection. For now, I’m playing with string properties and I’m quite happy with the result. Variables are automatically added to the node data based from the action node class (properties) if they are not yet found. Its value can also be set through the inspector.

Node inspector, woot!
Node inspector, woot!

I stumbled upon a very useful discovery. Serialized generic classes can be used in Unity with a little help. We know that Unity can’t serialize Dictionary. I need something that works like a Dictionary for the named variables but stores the serializable items in a List. Let’s say we have the following class that stores a single string variable:

    [Serializable]
    public class StringVariable {

        [SerializeField]
        private string name;

        [SerializeField]
        private string stringValue;

        /**
         * Default constructor
         */
        public StringVariable() {
        }

        /**
         * Constructor with specified name and value
         */
        public StringVariable(string name, string value) {
            this.name = name;
            this.stringValue = value;
        }

        public string Name {
            get {
                return name;
            }
            set {
                name = value;
            }
        }

        public string Value {
            get {
                return this.stringValue;
            }
            set {
                this.stringValue = value;
            }
        }

    }

A list of StringVariable instances can be specified as:

public class SomeComponent : MonoBehaviour {
    [Serializable]
    private List<StringVariable> stringVariables;
}

I don’t like this because I’ll have to bake functions like GetStringVar(string name) into the component. I don’t just have string variables. I’ll have int, float, GameObject, Vector3 later on. GetIntVar(), GetFloatVar(), GetVector3Var(), …? No way. This is where I tried to make a generic container class that stores items in a list but provides Dictionary like methods. The first step is to provide an interface to mark classes that it has something that returns a name.

    public interface Named {

        /**
         * Returns a name
         */
        string Name {get;}

    }

StringVariable now implements this interface:

    public class StringVariable : Named {
        ...
    }

This is my generic container that maintains items in a list:

    [Serializable]
    public class VariableMap<T> where T : Named {

        [SerializeField]
        private List<T> itemList;

        /**
         * Constructor
         */
        public VariableMap() {
        }

        /**
         * Adds an item
         */
        public void Add(T item) {
            this.itemList.Add(item);
        }

        /**
         * Removes an item
         */
        public void Remove(T item) {
            this.itemList.Remove(item);
        }

        /**
         * Returns the item with the specified name
         */
        public T Get(string name) {
            int count = itemList.Count;
            for(int i = 0; i < count; ++i) {
                if(itemList[i].Name.Equals(name)) {
                    return itemList[i];
                }
            }

            // not found
            return default(T);
        }

        /**
         * Returns whether or not the map contains an item with the specified name
         */
        public bool Contains(string name) {
            int count = itemList.Count;
            for(int i = 0; i < count; ++i) {
                if(itemList[i].Name.Equals(name)) {
                    return true;
                }
            }

            return false;
        }

    }

Everything’s good so far but I had a problem. This does not work (it does not display in Unity inspector):

public class SomeComponent : MonoBehaviour {
    [Serializable]
    private VariableMap<StringVariable> stringVariables;
}

I tried a little magic:

    [Serializable]
    public class StringVariableMap : VariableMap<StringVariable> {
    }

Now this works in Unity:

public class SomeComponent : MonoBehaviour {
    [Serializable]
    private StringVariableMap stringVariables;
}
Serialized generic class!
Serialized generic class!

This is so cool! This has countless possibilities. Its weakness, though, is it implies that I’ll have classes like IntVariableMap, FloatVariableMap, GameObjectVariableMap, etc. But this is a small price to pay. My main goal is to put the code of the custom container in one class and I have done that.

If you have a better idea, please do tell me.