When I was making HexMatch, I wanted to stay with an abstract, geometrical style, but I also wanted the background to be dynamic and interesting. That made me think of some videos I saw of Loren Bednar's game, Depth Check: Neptune. They had a field of triangles with shifting, swirling colors that evoked the depths of the ocean. Slight variations in saturation gave it character, almost like each triangle was hand-made, in contrast to the uniformity of the triangles themselves. I took a guess at how it might be implemented and got lucky, and after a few hours one Sunday morning I had a background that was even cooler than I'd envisioned.
Here is what we're making:
You can jump right into the sample code. I implemented it in Haxe and OpenFL, but the effect is easy to implement anywhere that triangles can be drawn. Let's start with the high level concept.
The idea is to fill the window with uniform triangles, and then change the color of each triangle in a smooth but random pattern. Depth Check uses isosceles triangles, but since HexMatch was about hexagons I wanted to use equilateral triangles arranged as hexagons. The hexagon page at Red Blob Games has all the math I needed to create the triangle vertices.
The colors of the triangles needed to change smoothly but unpredictably. One easy way to make a sequence of random values that change smoothly is to use Perlin noise. Flash can make colored Perlin noise, but to have more control over the colors I used the noise as an index into a color gradient. By stepping through the noise buffer on each frame, the color of each triangle will move smoothly back and forth through the gradient. Each triangle starts at a different place in the buffer, so they seem to change with different patterns. However, since the pattern does repeat in each triangle in turn, there is the illusion of motion across the window.
First we need to draw triangles in the window. The best way to draw a
bunch of triangles in Flash is with the
drawTriangles method in the
Graphics class. This is similar to the OpenGL call
glDrawElements. The method takes a vertex array, a texture
coordinate array, and an index array. The index array refers to the
other two arrays, and every three entries defines the corners of a
The vertex array is filled using the
hexagon coordinates. The arrays are constructed so that
every triangle is independent, so the index array is just
index[i] = i. The index and vertex arrays are created at initialization and
don't change again.
The texture is the color gradient. It is essentially one-dimensional,
but Flash 11 requires it to be at least two pixels high (don't ask me
why). You can use an image, or create the gradient at runtime by
drawing a rectangle with a gradient fill and drawing it to a
BitmapData. Just be sure that the image wraps horizontally,
otherwise there will occasionally be abrupt color changes.
The Perlin noise is created using the
perlinNoise method of the
BitmapData class. This isn't supported in the HTML5 target of the
OpenFL version I have (1.0.5), so if you need to build for HTML5 save
the noise bitmap to a file and use that instead of creating it at
runtime. After the noise is created, get a vector of the pixels,
extract one color channel, and normalize all the noise so it's between
0 and 1. That way it can be used directly as a texture
coordinate. Play with the parameters of the
perlinNoise method to
get different effects.
The texture coordinate array is filled during each frame by sampling
the Perlin noise for each triangle. The
u coordinate is set to the
noise sample, while the
v coordinate is set to 0.5 to sample the
middle of the texture vertically. On each frame, start at an offset
and sample consecutive noise values for each triangle, wrapping at the
end. Increment the offset at the start of each frame so the triangles
move through the pattern. Going through the triangles consecutively
led to a weird effect where the pattern rotates around a hexagon then
moves to the next one.
triangles[i].u = noise[(offset + i) % noise.length]
Much more interesting patterns emerge if we use a prime number to skip through the triangles.
triangles[(i * prime) % triangles.length] = noise[(offset + i) % noise.length]
It is important that the number is not a factor of the length of the triangle list, and vice versa. If one is a factor of the other, then the index will repeat too early and miss some triangles. That is why the factor should be a prime number larger that the length of the triangle list. Try different prime numbers, they make the overally effect evolve in different ways.
Although I've been writing "each frame," this effect is subtle enough that it can be updated at a low frame rate without breaking the illusion. Updating can even be paused briefly without really standing out. For example, in HexMatch the background is only updated ten times per second, and it pauses when there is any other animation active.
This turned out to be a really cool effect with a lot of potential. Changing the noise parameters, the color gradient, and the prime factor all lead to a wide range of effects. It's also a relatively cheap effect, because it can be updated at a low frame rate and paused when necessary.
Let me know if you implement this on another platform so I can add a link.