Essay - Published: 2022.05.01 | 3d | art | unity |
DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

Generative character NFT collections like Bored Ape Yacht Club and CryptoPunks have traded for millions of dollars on leading marketplaces. In this post, I'm going to show you how to create your own 3D generative character collections using Unity and C#.
Before we can generate our character collections, we first need to understand how computer generation works and why it's useful.
The art of computer generation is typically about combining a finite number of elements in many ways such that you can produce a large number of potential outputs. By doing so, we can leverage the power of computers to produce outputs that would be impossible (or at least extremely arduous) to make by hand.
Computer Generation = Finite elements * Many combinations => Large opportunity space
An example of this from real life might be lego characters.

With just a few different heads, torsos, and legs to play with we can actually create a very large number of "different" characters.
Thus we can see how a system that combines a few elements can lead to a very large opportunity space of outcomes. This is what we're going to leverage to build our own generative collections with Unity and C#.
Now that we understand how generation works, we've got to get all of the components together to start building our character collections.
Since we're building in 3D, we'll need to collect the different parts we want to use in our characters.
For example, if we were building those lego characters we'd need:
Finding these parts is out of the scope of this post and the ones you choose will be largely dependent on the kind of characters you want to build. That said, I recommend checking these sites out for free / cheap, good quality 3D models:
I'll be using the POLYGON Modular Fantasy Heroes pack I downloaded from the Unity Asset Store awhile back for another project. It's a good fit because for this because:
Once you've got your components picked out, we can continue onto generation.
At this point, we understand how generation works and we've got all the individual character pieces together that we'd like to build a character collection from. Now we've just got to put them together.
We can do this in a few steps:

In Unity, I created a basic scene with:
Then the code itself:
I've provided my full C# source code below:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class GenerativeCharacterCollection : MonoBehaviour
{
// Colors
private List<Color> Colors = new List<Color> {
new Color32(0, 0, 0, 255), // black
new Color32(255, 255, 255, 255), // white
new Color32(188, 0, 45, 255), // red
};
// Positions
[SerializeField]
private Vector3 EyeBrowPositionRight { get; set; } = new Vector3(-0.07f, 0.1f, 0.16f);
[SerializeField]
private Vector3 EyeBrowSize { get; set; } = new Vector3(0.005f, 0.005f, 0.005f);
[SerializeField]
private Vector3 HeadCoveringPosition { get; set; } = new Vector3(0, 0.25f, 0);
[SerializeField]
private Vector3 HeadPosition { get; set; } = new Vector3(0, 0, 0);
// Component Lists
private List<GameObject> AllEyeBrows;
private const string AllEyeBrowsObjectName = "AllEyeBrows";
private List<GameObject> AllHeadCoverings;
private const string AllHeadCoveringsObjectName = "AllHeadCoverings";
private List<GameObject> AllHeads;
private const string AllHeadsObjectName = "AllHeads";
private System.Random _random;
void Awake() {
this._random = new System.Random();
BuildComponentLists();
}
// Start is called before the first frame update
void Start()
{
GenerateCharacter();
Debug.Log("Generate Character");
}
private void GenerateCharacter()
{
GenerateHead();
GenerateHeadCovering();
GenerateEyeBrows();
}
private void GenerateEyeBrows()
{
// Right eyebrow
GenerateEyeBrowHelper(
EyeBrowPositionRight,
new Vector3(0, 0, 0) // no rotation
);
// Left eyebrow
GenerateEyeBrowHelper(
new Vector3(
EyeBrowPositionRight.x * -1,
EyeBrowPositionRight.y,
EyeBrowPositionRight.z
), // mirror horizontal
new Vector3(0, 180, 0) // 180 horizontal
);
}
private void GenerateEyeBrowHelper(
Vector3 position,
Vector3 rotation) {
var randomEyeBrow = this.AllEyeBrows[
this._random.Next(this.AllEyeBrows.Count)
];
randomEyeBrow.transform.localPosition = position;
randomEyeBrow.transform.localScale = this.EyeBrowSize;
randomEyeBrow.transform.Rotate(
rotation,
Space.Self
);
randomEyeBrow.SetActive(true);
SetColorOnGameObject(
randomEyeBrow,
GetRandomColor()
);
}
private void GenerateHead()
{
var randomIndex = this._random.Next(this.AllHeads.Count);
var randomHead = this.AllHeads[
randomIndex
];
randomHead.transform.localPosition = HeadPosition;
randomHead.SetActive(true);
SetColorOnGameObject(
randomHead,
GetRandomColor()
);
}
private void GenerateHeadCovering()
{
var randomHeadCovering = this.AllHeadCoverings[
this._random.Next(this.AllHeadCoverings.Count)
];
randomHeadCovering.transform.localPosition = HeadCoveringPosition;
randomHeadCovering.SetActive(true);
SetColorOnGameObject(
randomHeadCovering,
GetRandomColor()
);
}
private Color GetRandomColor()
{
var randomIndex = this._random.Next(
this.Colors.Count
);
return this.Colors[
randomIndex
];
}
private void SetColorOnGameObject(
GameObject go,
Color color
)
{
var goRenderer = go.GetComponent<Renderer>();
goRenderer.material.SetColor("_Color", color);
}
private void BuildComponentLists()
{
var headsFolder = GameObject.Find(AllHeadsObjectName);
this.AllHeads = GetAllChildrenGameObjects(headsFolder);
var headCoveringsFolder = GameObject.Find(AllHeadCoveringsObjectName);
this.AllHeadCoverings = GetAllChildrenGameObjects(headCoveringsFolder);
var allEyeBrowsFolder = GameObject.Find(AllEyeBrowsObjectName);
this.AllEyeBrows = GetAllChildrenGameObjects(allEyeBrowsFolder);
}
private List<GameObject> GetAllChildrenGameObjects(GameObject targetRoot)
{
return targetRoot.transform
.GetComponentsInChildren<Transform>(includeInactive: true)
.Select(t => t.gameObject)
.Where(g => g != targetRoot)
.ToList();
}
}
That's how I generate 3D character collections in Unity!
For more on Unity, checkout:
The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.