Digital koi breeding12 Jun 2022
Recently, I launched my game Koi Farm. Koi Farm is a koi breeding and collecting game, in which the player breeds koi patterns by carefully selecting traits in the parents. This article explains how I implemented crossbreeding and mutation in koi patterns.
Koi Farm is open source, so the algorithms discussed here can be seen in their "natural environment" on GitHub.
Procedural mutable koi patterns
The fish in Koi Farm have many mutable properties, like shape, size and color, but this article mostly goes into generating the patterns on the fish bodies. Most koi patterns contain spots of varying shapes and sizes.
The koi pattern generator needs to achieve the following main goals:
- There should be a nearly infinite number of possible patterns.
- The patterns should have plenty of variance.
- The patterns must be able to mutate in small steps.
The first goal is obvious: the koi are procedurally generated, every fish must be unique. The second goal is important to make the game fun; if all patterns are unique but still look very similar to each other, breeding koi gets boring quickly. Finally, mutation of patterns should happen in small steps. This is crucial for the gameplay. Specific koi patterns desired by the player can be bred by choosing parent koi that look close to the ideal outcome, because their offspring has a chance to arrive there by mutating. For example, if the player wants to breed a "Tancho" pattern (white with a round spot on the head), parents with a single red blob on the back can be bred for several generations until the spot becomes rounder and moves forward. If mutation happens in large steps, or if it would be completely random, there is no way to tell how the offspring of two fish may turn out. In that case, the entire selective breeding gameplay mechanic doesn't work.
To create spots, I chose a method based on slicing noise. The following steps generate a texture with random spots:
- Generate a 3d noise, in this case Cubic Noise was used. This type of noise is less uniformly distributed than for example Perlin noise, which is good for variance.
- Construct a 3d plane that intersects the generated noise. The plane has the same size as the texture the generated spots are written to.
- For every pixel on the plane corresponding to a pixel on the texture, the 3d noise is read. If the noise value is higher than a given threshold, this pixel is a spot. If it is lower, it's not a spot.
The application on the right demonstrates the spot generator. The 3d noise is always the same, but the location and rotation of the 3d plane intersecting it can be changed to change the pattern. The scale of the plane determines the size of the spots; a very small plane contains only a small portion of the noise, while a scaled up plane contains a larger area and therefore more variation.
The randomize button randomizes the pattern by randomizing every plane parameter. The mutate button mutates the pattern by only changing the parameters a little. Because the plane is "nudged" through the 3d noise by small increments, the new mutated pattern will look similar to the pattern that was generated before, while mutating many times can completely change the pattern to every possible pattern this algorithm can create.
Rendering the fish
After generating a pattern, the koi can be rendered. The example application has two other modes besides Texture.
- Shaped shows a cutout of the spots texture in the shape of a fish. The shape has several parameters, and it also mutates when the mutate button is pressed.
- Animated shows the cutout with animation, which resembles the way the fish will look like in the game.
There is no advanced 3d lighting in Koi Farm, but shading is approximated by making fish bodies darker near the edges. Eyes are rendered on top of the texture. In Koi Farm, several fins are added as well, but they are not shown in this example.
Crossbreeding involves more than just mutating. To "mix" the properties of two parents, child patterns are created by taking the two 3d planes that sliced the parent patterns, and choosing a plane that lies between them. While child planes tend to lie close to either parent, they may be anywhere between. This causes most offspring to look like one of their parents, but if parents are sufficiently different, they can use a plane that doesn't contain any pattern seen on the parent. Finally, after a new 3d plane is created for the child pattern, mutation is applied to add more variance.
If the parents contain different colors and different patterns, the crossbreeding methods are a bit more involved, but that's outside the scope of this article.
In Koi Farm, I've used some additional techniques to make the patterns more interesting:
- Multiple patterns can be layered, so koi can have more than two colors. When mutating, colors may change and patterns may be added, swapped or removed.
- In addition to spots, there are also stripes, ridges and brindle patterns. They are generated in somewhat similar ways.
- Patterns can be stretched to change the shapes further.
- Koi have many more mutable properties than just their pattern. Properties like behaviour, fins, fertility, size and grow speed also mutate.
The koi pattern generator made the main gameplay mechanic in Koi Farm possible thanks to its ability to mutate and mix procedurally generated patterns. The gameplay was further extended by adding a koi collecting element to the game, which allows player to store their koi as collectible cards, as shown on Figure 2.