For a change I thought I’d write about a hypothetical, and one of the things I usually do to avoid extra work. Or rather, not just to avoid extra work but to keep it easy to change things on a whim. Let’s create a texture for Piczle Cross clue numbers with the minimum amount of work or texture overhead. Note, this is not how I did it in Piczle Cross Adventure, though I wish I had…
Say we want to make a, let’s say, 90 by 75 grid puzzle. This won’t happen in any future or past Piczle Cross as it just wouldn’t be readable on anything but a massive screen. No, this is a hypothetical. On a row that is 90 blocks long, the maximum clue number you might find is 90 (the minimum one is obviously 0). So theoretically you’d have to make 91 textures to accommodate a puzzle this big. The 0 clue number texture all the way up through 1, 2, 3…etc. to 90.
Creating 91 textures, though there are ways to automate something like this in Photoshop, is quite a task, and takes up quite a lot of texture space as well. Imagine having to load 91 textures of 256×256 pixels, for example. That starts chipping away at performance and memory. Let’s say that during development you decide to change the font of the clue numbers. Now you have to redo those 91 textures. Automation scripts in Photoshop notwithstanding, this is not a very optimal way of dealing with this issue.
Instead, let’s create these “textures” automatically from a more manageable subset of 10 (0 through 9). The only real caveat here is that the font you decide to use has to be fairly “blocky”, i.e. the numbers have to be fairly uniform in width.
What I am planning to do here is to create a texture mask based off of these 10 textures, combining 2 of them for numbers over 10.
The plan here is to use these textures for clues numbers under 10, but for any double digit I take the texture for the first digit, offset it to the left, then the texture for the second digit, offset it to the right, and slap them together.
This is a view of the material. I set 2 textures for 2 digits, though I only use the 2nd one if there is no need for double digits. These are made into parameters so I can drive which number these represent through texture parameter values in the material instance.
Second, I create a single scalar parameter which I call “digits”, which is set to 1, but can be set to 2. If it is set to one, using a few IF gates I simple ignore everything except the 2nd digit texture and make that one the material’s opacity.
If “digits” is set to 2, however, I take both digit 1 and digit 2 textures and combine them into one. I do this by offsetting the 1st one a little to the right, and the 2nd one a little to the right, then adding them together. The offset is done through the TextureCoordinate, adding a little to it through a vector2D to nudge it a little. As I want to do the same to digit 2, but not when I’m only using 1 digit, I added an IF gate before that texture as well. If it is a 2 digit clue then the texture coordinate will be nudged, if not it won’t. After all this the combined new texture will be used as the material’s opacity instead.
You may notice there are a few MaterialParameter values in there as well. Though it is great to drive the individual digits for each instance of this material, others you want to apply to every instance of this material at all times. The colour of the clue number, for example, can be set in a MaterialParameterCollection as a vector parameter. Then you drag the MaterialParameterCollection into the material editor, select that particular vector and hey presto! Now if you want to change the colour of all your clue number textures you only have to adjust the vector parameter in this MaterialParameterCollection, and not set it for each individual instance manually.
I also added an offset parameter this way. Say you want a different font, or allow the player to select from a few different fonts, you may want to adjust how big the offset is between the two digits. Wider font numbers would need a bigger offset, so setting this in the MaterialParameterCollection as a scalar parameter makes it easy to adjust in one place.
Now when I calculate and create these clue numbers in a puzzle, I can simple create a dynamic material instance and set the proper parameters. In this case I created an array of the 0 to 9 textures and simply grab the one that corresponds to the clue number I need to create, if it is a single digit…
…or turn the clue into a bit of text so I can check what the left digit is versus the right digit and set them both in the dynamic material instance. There is probably a different, better way to easily see which digit is what but this definitely works.
These textures can now be set on a static mesh or in your UI widget, depending on the material domain you created it in.
Obviously this could also work for 3 digits (the offset just needs adjusting, and you need to add an “if” check for 3 digits). For me, a lot of the fun I have during game development is figuring out little shortcuts like these to minimise texture or work overhead, to add little variables here and there that can be easily tweaked, changing a whole subset of objects without the need of having to go back and tweak each individually.
There is a probably apocryphal quote of Bill Gates’ which goes: “I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.” I am certainly lazy, but for me finding ways to make hard work easy is honest-to-goodness fun.
NOTE: This post doesn’t cover the idea of manually drawing textures and using nodes like “draw text”. Using a system like the above gives the level of control I personally would demand. E.g. you could, if you wanted, paint some cool, pre-coloured, shaded numbers to use as textures. Also, when using fonts you have to deal with licenses, and some don’t allow for packaging TTFs on some of the cheaper levels of licensing, making nodes like “draw text”, which relies on fonts, something to avoid.