Danmaku
Unity twinstick shooter
Summary & Purpose
Danmaku was originally a gamejam title that I felt compelled to finish up as it was simple but also had a lot of promise in terms technical challenge. I’ll discuss the key interesting things as well as provide a brief video playthrough of the level. The game should be on itch.io soon for download.
Discussion points:
- Behavior Tree AI
- Smart pooling bullet objects
- Scriptable Object Event Architecture
- Custom Tools for map creation
- Procedurally Generated Animations
- World Transition Gimmick
Behavior Tree AI
For enemy AI, I wanted more practice with working with behavior trees. The above display the behavior trees I’ve created. A lot of these have simple nodes but there are also a few custom nodes that I made specifically for that enemy or that combat encounter.
Smart Pooling Bullet Objects
Since this game was intended to be a sort of bullet hell game, I wanted to manage the many bullets that were going to be on screen at the same time in an efficient manner. To this end I created a bullet manager to accomplish this.
Anytime a bullet is required by an entity it asks this level-wide manager. The manager then checks to see if a bullet is available in the queue, if it is, it will enable it and set it up as required. If not, it will create a new bullet.
Once a bullet has run its course or made impact, instead of destroying, it notifies the bullet manager, and places itself back into a queue ready to be used for the next time the bullet manager needs a new bullet.
Doing this removes the constant overhead of creating and deleting bullets.
Scriptable Object Event Architecture
This game was made with a strong lean into Scriptable Object Event Architecture. This talk was pretty inspiring to try using events to decouple a lot of the systems talked about here. I found I really enjoyed working with this paradigm, despite it not having as clear a paper trail as traditional dependency injection in unity. The benefits far outweight the cons in my head as a lot of these systems I made are easily portable to other projects. I extended the methods discussed in the talk quite a bit, adding generics and parameters to events, as well as making special Scriptable Object variables to house custom data types and the like.
Custom Map Creation Tools
To make map generation easier, I implemented some map generation tooling which takes png images to create a level. The actual implementation is pretty simple with some unity editor tooling wrapped around it. We go through each pixel in the provided image, and then spaw the block or enemy at the calculated offset. This map is also automatically enrolled in the world shift gimmick discussed later.
public void CreateMap()
{
foreach (Texture2D map in maps)
{
int mapwidth = map.width;
int mapheight = map.height;
int xoffset = -(map.width) / 2;
int zoffset = -(map.height) / 2;
Color[] pixels = map.GetPixels();
float px, pz;
for (int i = 0; i < pixels.Length; i++)
{
px = i % mapwidth * distanceBetweenCells + xoffset;
pz = Mathf.Floor(i / mapwidth) * distanceBetweenCells + zoffset;
if (pixels[i].a == 0)
continue;
if (pixels[i] == Color.white)
{
CreateBlock(blocktype.WHITE, px, pz);
}
if (pixels[i] == Color.black)
{
CreateBlock(blocktype.BLACK, px, pz);
}
if (pixels[i].r > 0 && pixels[i].b == 0)
{
createEnemy(px, pz);
}
}
}
}
Procedually Generated Animations
World Transition Gimmick
This Gimmick was the whole purpose of the game initially, the theme for the gamejam was something like “split decisions”. So the game is based around shifting between these worlds.