From zero to 10,000 NFTs. A Unity developer learning Solana.

Third Time Dev
6 min readSep 25, 2021

--

For the past few months, I’ve been experimenting with each of the ‘popular’ blockchains out there, trying to learn how they work and advantages and disadvantages of each. Backstory being I am a long-time game maker and game designer, working primarily in the land of Unity C# since 2013 or so.

After digging deep into the documentation and communities, I decided it’d be worth trying to make a collection on the Solana chain. There are so many great things that each of the chains offer, but I liked how early things were in the Solana ecosystem and how positive many of the enthusiasts and members were. I also liked that it was really cheap for the end users…I didn’t like the cost the consumers / players had to eat on Ethereum for gas fees as they transferred or bought and sold items.

So, after deciding on Solana, I figured I’d make my very own NFT drop. And after stumbling upon an amazing tutorial by Levi Cook https://hackmd.io/@levicook/HJcDneEWF on how to make a large collection of assets, I figured what the hey why not do my own PFP / Avatar collection!

Digging into how many of these worked, I found there’s often a set of pre-defined metadata that is used to determine both a) what the avatars look like and b) how rare the items are.

Step 1 then was to figure out what it looked like. Over the course of a week or two, I first brainstormed some ideas on silly names that were horse-themed. “Confused Colts”, “Puzzled Ponies”, “Stunned Steeds”, etc. Eventually after bouncing it off of a few people, “Stylish Studs” came around. Winner winner.

Next I made the call on what categories I’d need for the swappable parts. I settled on these for the categories, which can also be referred to as “traits”.

  • Background
  • Face
  • Outfit
  • Head
  • Coat

I then went to work in excel of defining how many variants of each trait I would need. This is totally up to the creator. Do you want lots of faces and just a few heads? Or do you want more backgrounds and just a few outfits? And how do you want to break apart the distribution and rarity? I came up with 20+ face ideas, 50+ head ideas, 60+ outfit ideas, etc. And then I’d use some excel formula magic to distribute a rough percentage of how “rare” I’d want something to be. For example a unicorn horn or gold crown I thought should obviously be rare, whereas a plain green cap or headband would be common.

After defining the variants of the traits, I made a single PNG for every variant inside of named folders that matched the traits. I put this entire folder structure into the Resources folder in my Unity project so I could load them up at runtime. (Disclaimer: I know that loading out of the Resources folder is often discouraged. But this isn’t for a working game, it’s just to generate stuff. I chose speed of development over the runtime optimization)

After these PNG’s were all ready, I jumped into Unity to figure out how I’d make an “image combiner”. I had made image combination scripts many times on command line using the Image Magic package (http://www.imagemagick.org/script/download.php#unix), but I decided on using Unity specifically because I wanted to be able to preview things as they were generated and make adjustments.

This was really straightforward. If you don’t use Unity UI (UGUI) often maybe this seems intimidating, but it’s not!

  1. Make a UGUI Canvas with a Panel anchored to the edges.
  2. Make 5 children. Add a “RawImage” component to each child. Anchor / stretch all to the edge.
  3. Set the Game view to the canvas size you’re going to draw the images at (if you’re me — 3000px by 3000px)
  4. After importing all of your PNG’s to the Resources folder, select all those PNG’s and make sure they’re uncompressed with mipmaps disabled
  5. Get ready to code!
The scene is setup with a canvas and 5 RawImage components on children

There are a TON of ways to generate N new images based on all these inputs. In my case of having 150+ layers (or variants on the traits), there are actually millions of possible combinations. So I settled in on generating only 10,000 of them, since that seemed to be commonplace at the time. Disclaimer: this is a very hacky way to do this - done for speed and ease of implementation. It is not very efficient and not a model of solid computer science techniques. 😌

The very first thing to do is of course make a new script. If you’re me, you call it “WeirdGenerator.cs”. Then, you add a new object to the hierarchy, and add your new WeirdGenerator script to that object so you can access it.

Next up was to start the randomization. The way I accomplished this was to start with a hardcoded static Dictionary<string,int> of the traits with their rarities, ensuring they all added up to 10,000. This came from my original excel file. For example, for the “Coats”:

static public Dictionary<string,int> Coats = new Dictionary<string,int>{
{"Dark Bay", 10},
{"Dun", 1238},
{"Black", 2014},
{"Chestnut", 2251},
{"Bay", 4477},
};

I made 5 dictionaries obviously, 1 for each trait.

I made a Generate() function at first that I could easily call when I hit the space bar (hacky way — Input.GetKeyUp() in the Update() loop). First thing I do in my generate function is convert that dictionary into a “bucket” — using a List<string> — and then scramble / shuffled it.

List<string> ListFromDictionary(Dictionary<string, int> dict)
{
List<string> retList = new List<string>();
foreach (var kvp in dict)
{
for (int i = 0; i < kvp.Value; i++)
{
retList.Add(kvp.Key);
}
}

System.Random rng = new System.Random();
var shuffled = retList.OrderBy(a => rng.Next()).ToList();
return shuffled;
}

So now I know that I have conceptually 5 buckets that I can iterate over in a loop and grab items out of them.

for (int i = 0; i < 10000; i++)
{
var bg = backgrounds[i];
var face = faces[i];
var outfit = outfits[i];
var head = heads[i];
var coat = coats[i];
string uniqueId = $"{bg}_{face}_{outfit}_{head}_{coat}";
Debug.Log(uniqueId);

Run this and see 10,000 prints blow past! Woo!

Since we now have some base level randomization working, it’s time to hook into the RawImage components! This is super simple Unity stuff…create 5 serialized fields in the script, save it, then drag each layer from the hierarchy over to the Generator script.

[SerializeField] private RawImage ImageBackground;
[SerializeField] private RawImage ImageCoat;
[SerializeField] private RawImage ImageOutfit;
[SerializeField] private RawImage ImageHead;
[SerializeField] private RawImage ImageFace;

With these links made from script to the RawImages, I just made sure my PNG’s match the names in the dictionary, and I can load them up and stick them in the RawImage!

ImageBackground.texture = Resources.Load<Texture2D>(imgPath + bg);
ImageFace.texture = Resources.Load<Texture2D>(imgPath + face);
ImageOutfit.texture = Resources.Load<Texture2D>(imgPath + outfit);
ImageHead.texture = Resources.Load<Texture2D>(imgPath + head);
ImageCoat.texture = Resources.Load<Texture2D>(imgPath + coat);

To capture a screenshot, I just use Unity’s built in screenshot utility — ScreenCapture.CaptureScreenshot() (inside of an IEnumerator with a delay) so I could view them as they were getting generated!

yield return new WaitForEndOfFrame();
string fileName = $"{i}.png";
ScreenCapture.CaptureScreenshot(Path.Combine("../export", fileName));
yield return new WaitForSeconds(.6f);
Load an image into each layer, snap a screenshot!

You should now have 10,000 images generated!

However, if you’re like me, you’ll realize after this great moment of relief and accomplishment that there’s a flaw in the above logic, which is that it will create a whole bunch of images that are duplicates of each other. And that’s no good — these are supposed to be 1 of 1 unique NFT’s!

So, in my next medium article, I’ll talk about how to address that, along with how to create the appropriate metadata json files along with each image to prepare these assets for becoming NFTs!

--

--

Third Time Dev
Third Time Dev

Written by Third Time Dev

Game development studio with many years in AAA, mobile, and F2P. Crypto enthusiasts. Big fans of Unity.

Responses (2)