Floating Rocks Liveblog

Airships
2014-05-04 16:07
After a bunch of maintenance releases, it's time to start working on new features for Airships. The first thing I'll be working on is floating rocks as aerial obstacles. I very much enjoy doing detailed dev writeups, so I'm going to go one further this time and live-blog this, adding to this article as I go along.

So revisit the page every once in a while, and there'll be new stuff! And if you have any questions, ask via twitter or the comment form below and I'll put answers inline.

The basic idea

To make fights more varied, I want to replace the current completely flat terrain with one that is variable and destructible. In engine terms, this means replacing the current giant rectangular physics object that is the ground with a more complex ones that consists of a grid of tiles, much like the tiles on an airship.

Actually getting this to work all the way requires a fair number of changes, especially to the AI, which should be prevented from flying into hills it doesn't know are there. So to try out the new data structures and algorithms needed, I'll start with creating flying rocks as obstacles. These will be in addition to rather than as a replacement of the ground.

Some quick graphics

The landscape and floating rocks are going to be composed of soil (with optional grass on), rock, suspendium ore (which is what makes the rocks float) and bedrock (which will be the bottom layer of the landscape, to prevent ships from falling through into the void). So to start out, I've added some quick graphics for those to the sprite sheet:

Starting with structures

Unlike airships, which are ultimately a list of modules and tiles, bits of landscape are much simpler creatures, so I'm going to model them as 2D grids of blocks:

package com.zarkonnen.airships;

public enum LandBlockType {
    AIR(false, 0, 0, 0, null),
    GRASS(true, 10, 200, 0, Appearance.app().frame(0, 17, 1, 2)),
    SOIL(true, 10, 200, 0, Appearance.app(0, 18)),
    ROCK(true, 50, 800, 0, Appearance.app(1, 18)),
    SUSPENDIUM_ORE(true, 40, 600, 2000, Appearance.app(2, 18)),
    BEDROCK(true, 0, 1000, 0, Appearance.app(3, 18))
    ;
    public final boolean solid;
    public final int hp;
    public final int weight;
    public final int liftGenerated;
    public final Appearance app;

    private LandBlockType(
        boolean solid,
        int hp,
        int weight,
        int liftGenerated,
        Appearance app)
    { /* snip boring bits */ }
}
public strictfp class LandFormation extends Body {
    private final LandBlockType[][] grid;
    
    // Body requires a whole bunch of things like the size, weight
    // and air friction, which we can all derive from looping over
    // the grid.
    // ...
}

Wait, why are those rocks floating?

While I'm doing the slightly boring work of implementing all the code for calculating the weight and size of these rocks on the basis of the grid, here's a quick in-universe explanation: In the game, airships are held aloft by crystals of "suspendium", which, when charged, tend to float. The floating rocks have large suspendium deposits in them, which tend to pick up charge from the atmosphere, floating on their own.

Random generation

Having written code that should hopefully give these rocks the right physics characteristics, and written a quick loop to draw them to screen, one detail remains before I can start integrating them into the game: they need to be generated somehow.

Now I could probably create some very clever perlin-noise based solution for this, but at this point this would be overkill. There are a bunch of very specific properties the rocks need to satisfy:

  • They need to be mostly suspendium ore. The ore doesn't provide very much lift, so only if there's enough of it will the rocks actually float!
  • There should be no air pockets.
  • They should have a slightly irregular shape, but fill out the grid pretty well.
  • Soil should be on top, rock on bottom!

So for now, I'm going for a very ad-hoc solution where I'm filling the bottom half of the grid with a mix of suspendium ore and normal stone, and then "raining" multiple layers of ore, stone, and soil onto the rock. We'll see whether that produces useful results as soon as I've hooked this up!

The next step, then, is to integrate these rocks into the combat code. This means adding a list of rocks to the combat, adding them to the physics system, and drawing them...

The first floating rocks

Having hooked the rocks into the combat system, I've quickly added some code to generate some rocks. After a bunch of fiddling with the random generation parameters, the result's starting to look quite good:

OK, so it's been about two and a half hours, and I've got something vaguely working. There's still a lot missing:

  • Collisions between rocks and airships, and rocks and other rocks.
  • Generating these rocks (and eventually the landscape) in single and multiplayer combat.
  • Explaining to the AI about these new obstacles.

Time for a break, though! It's Sunday night here, so I'm going to have some dinner and then go to bed. The next step - collisions - will be covered tomorrow morning.

Collisions

Right, we're back! So what I've got so far is rocks that float in midair, using the default collision code based on bounding boxes. Now, I want to improve this code by actually looking at the individual blocks of the floating rock and calculating damage to them, destroying them if the impact is severe enough. This means adding the following collision scenarios:

  • Rock on rock
  • Rock on airship
  • Rock on generic rectangular bounding box

Which will be a bit tricky, but it's essentially the same code as in airships, with a grid the same size (if stored differently).

So I'm starting out by stubbing out the new functions I will need for calculating e.g. which blocks of a rock are overlapping with an airship. I've also just realised that I do actually need to store damage values for the blocks. I thought I could just remove them when they got destroyed, but it's not safe to modify the geometry of physics bodies while collisions are being evaluated!

I ended up having to write some gnarly code to re-crop the grid as necessary to make sure the bounding box correctly matched the actual rock, but once I got that working, the code I've adapted from airship/airship collisions worked fine! It's somewhat gnarly in that it has to compare each in one body with each tile in the other, which can be very time-consuming, but I made sure to write the loops in an efficient way, so hopefully it won't be too much a problem. And here it is, the first airship-on-rock collision:

Next up? Some cosmetic improvements...

In case you can't spot the difference, I've added a bunch of variations to the block graphics to prevent an overly obvious tiling look. Still not too happy with how the suspendium deposits look - may have to "blur" them a bit somehow. Dense and sparse deposits maybe...

Making the ground a big rock

Finally, with the floating rocks working, I want to replace the currently flat ground with a "rock", which will allow for things like ships crashing into the ground and destroying it. To do this, for starters, I just generate a really big rock, put it where the ground should be, and remove the previous ground entity.

OK, so this pretty much works:

However, there's definitely some changes to be made when it comes to collision damage! This is what happens when I ground the ship:

Right, that's better:

The problem was that the system counted the entire mass of the ground for purposes of calculating collision energy. Which, given the ground is squishy, doesn't really work, so I capped it.

So there we go: the static ground's been replaced, at least in-engine, with deformable terrain. What comes next is really quite a lot of bookkeeping and UI integration to store and edit this terrain information. But that's out of the scope of this blog. For now - thank you for reading!