PokeHearth - Generating Textures

Around new-year, I decided to create a collectible card game inspired by the rules of Hearthstone, and the characters of Pokémon, by which I mean the pokémon of Pokémon. Inaptly named PokeHearth, this abomination surfaced a series of questions, for instance - how to make each of the 18 types feel unique and manageable?


This post is about the graphical aspect of this. It will detail some simple ways to generate textures used for the backgrounds of each of these 18 types. Most of them will primarily use Perlin noise. The textures are applied to two different things - the background of the cards, as well as the battlefield.




The Water type uses a stretched Perlin noise map. This value is compared to a threshold that is, er, pretty far from the median, meaning that only 20% or so actually has its colour changed. Especially on the battlefield you might notice that it is not just a binary - some parts are even brighter than others.



The Poison type instead has a symmetrical threshold close to zero, which creates these interesting contour lines, that in other applications would be the coast-lines when generating terrain.



The Normal type uses a larger-scale Perlin noise map with just two channels, creating these soft, lumpy shapes. Just like the Water texture, all values above a threshold are coloured, though this threshold is much closer to zero, meaning that about 40% is coloured.



Psychic type is quite straightforward, only colouring in the peaks of the Perlin noise map, creating a lot of small, white islands. They're meant to look like sparkles, because, you know, Psychic is basically magic. Basically.



The Dragon-type is the first thing that is purely geometric rather than Perlin-based. Well, the battleground still has some Perlin noise in it, but that's the same for all of them.

The geometric function is as follows:
abs((y mod 32)-abs(16*sin(x*10))-sqr(x-65)/100) < 2
Which, magically, spits out that scaly pattern! Okay, to explain a bit, let's break it into parts:
(y mod 32)
Controls the repeating pattern. The distance between segments is 32 pixels.
16*sin(x*10)
Controls the wave itself. Really, it's that simple, just taking the absolute value of a sine-wave creates scales. Who'd have thought! 16 is the amplitude of the wave, and x*10 (in degrees) is the periodicity - it repeats every 180°, that is, eighteen pixels.
sqr(x-65)/100
Controls the curve throughout the card. 65 is the midpoint of the card, so it takes the square of the distance from the middle. It also incidentally cuts off the far sides of it, which was an unintended consequence I decided to save, since I like the connection between dragons and columns, temples and such.
abs(lots of stuff) < 2
Just specifies how thicc the lines should be. Notice that the lines are not equally thick the whole way through - they get thinner as the square of the x-distance inflates the actual coordinates.




So, the Grass type is the first time we start to see a repeat - Poison was made in the same way. This fits nicely considering all the Grass/Poison type pokémon, since dual-type cards are the combination of the two types:
So you can see the vines from the Grass part wrapping out into tendrils in the Poison type. However, looking over the code, the Grass type uses just two channels of noise, while Poison uses three. What difference does that make? Well, some of the lines do not match up between Grass and poison in the above image, and if we go to look at the two side-by-side:


We can see that while Grass has the same amount of lines all over the card, the Poison texture has areas that are almost completely clean, because this lower-frequency noise-channel lifts up the values too far away from the median to have anything drawn.

This difference is not accidental - it was chosen to reflect the difference between the two types - life and death - though this comes up as something quite subjective at the end. One way or another, it is all quite faint, and most probably won't notice too much.



Ground is a repeat of water, mostly. The only difference is that the threshold is lower, that is, there are more highlights in the Ground texture than in the Water texture, that, to compensate, shine less brightly. Like you'd expect, as one is brilliant and the other more dull. It also means that in some places, different "islands" are connected.



Before you get bored - finally, Fire! While the effect is cooler and catches your eye more than the others, the method is actually simpler. No thresholds, no math, just Perlin. The x-coordinate has been stretched, though opposite of in Ground and Water, to create vertical stripes. Added in is a gradient, which is simply the y-coordinate added to the Perlin value. Finally, the colour change controls hue instead of fading the colour, like in the others.



The Bug-type is a bit weird - I never got it to work quite like I wanted to. It was supposed to be some sort of webby thing, I believe? What it does is that it seperates the map into a grid, and each pixel looks at the distance to the centre of its grid cell. Then a Perlin noise value is added on top, and this is used to determine if the pixel is close enough or two far from the centre to be coloured in. Why this leads to horizontal and vertical lines, I have no clue. In that way it does make a web, just a much less organic one than what I desired. Let's just move on.



Rock is like Normal, only that it has more channels in the noise function, and the threshold is higher. In that way, this might actually be the most straightforward texture to create with Perlin noise.



Flying type is like Water and Ground, only that instead of stretching the x-coordinate, the y-coordinate has been squeezed, creating a much larger scale texture. That's it, though.



Since I was a bit annoyed that I could not make the Bug-texture work, I tried again with Fighting instead. At least I got rid of the horizontal and vertical lines. But still, it's not much better, really. I did not have much of an idea of what to do with Fighting, though. Punches, I think that's what I was going for, like the marks left after being punched, bullet-holes but with hands instead. What kind of element is Fighting, anyway?




Yeah, Electricity is much more obvious, anyway. It's not as interesting though, since we have already seen the tendrils from Poison and Grass, and talked about stretching coordinates for Fire, Water, Earth and Flying. Here, the x-coordinate has been squished in the same method used for Poison. I think it works out fine, though. One should consider not just the texture itself, but the final product, the final card, where you might not notice that some of the lightning is actually a circle.



Oh boy, Ice was such a difficult thing to make. Even here, you might notice several of the circles, which should look like snowflakes, are clipped. One might wonder why I did not do the simple thing of just drawing circles. No, I had this idea that all pixels should be independent, that any of them could be drawn in any order, no pre- or post-generation action going on. So it's messy and not great, as can be seen in the battlefield where the gridlines are obvious. Oops.



No, this is the simplest thing you can do, much simpler than rock. Ghost has the simple texture of simply adding the Perlin noise value onto the brightness of the final colour. No threshold or math. And why? Because I did not actually program this. This texture is actually applied to all the different types, though usually, one does not see it. However, Ghost being as dark as Ghost is, it turned out to be sufficient texture in itself. I can't explain why, but I'm not even too torn up about it. I kind of like the simplicity - it is empty, eerie.



The Fairy-type actually has the same effect as Fire - the vertical gradient and the Perlin-controlled colour shift. Apart from different colours, the difference lies in that thisone does not have its x-coordinate stretched. It just goes to show how important the verticality is to create something that looks like flames.



The Steel-type is not purely geometric - it does, in fact, use Perlin noise, only it's 1-D noise, where the x and y-coordinate has been added. This is what controls how thick the diagonal line is. Apart from that, one can make a diagonal line by adding x and y-coordinates together and comparing them to some number. That's how this works. It's not amazing, but it fits Steel very well.


The idea was for Ice to have snowflakes, and Dark to have stars. It... did not work. Thing is, when creating textures like these, one has to consider where and how they are to be used. You want the texture to be there, best to be noticed a bit, but not too much. You don't want it to grab attention from the card, which is busy enough in itself. And small, bright stars would do that. Instead the stars are neither small or big, neither bright or dull, but in a not very interesting middle ground.


Evaluation

Perhaps this should not really be an illustration, but rather a review of what went well and what went wrong. I think if I went back now, I could make some much more interesting textures. But that was never the point. All this work was made in just one day, way back when, and it was sufficient. With that little space, options are limited. If I one day go through with redesigning cards so they are human instead of ant-sized, I might have the space to actually do something interesting.

I don't know if the textures look great. However, I do feel they add a lot to the final product. I do believe that.

Comments