Devlog 3: Of Tiles and Arrow Traps


As I mentioned in Devlog 1, I'm planning on adding a number of Lair Building elements to PBM. The first of these is The Arrow Trap, which is obviously a classic device for dungeon crawlers of all stripes. 

Since I'm using the Unity Tilemap system, however, I figured I'd try to create these as prefabs with their own embedded tiles. What a journey that has been...

The first problem I ran into was I wanted to animate a tile on demand. But tiles aren't "animated" the way other assets in a game are. They're basically just ScriptableObjects that are referenced by an efficient but mostly-opaque low-level draw engine. Intercepting animation steps is not, at present, particularly simple.

Unity's existing AnimatedTile (available in the 2D Extras package) allows you to set the rate at which the tile animates. I figured maybe I could set that value out-of-process with some tricksy per-frame magic.

Unfortunately, there isn't really a way to stop the animation via this method, at least not one I could find. Tile animation frame rates are tricky in general, it seems. More on in a minute.

There are a bunch of workarounds for this, from using sprites with the appropriate draw order all the way up to creating custom tile classes,  none of which are particularly clean or satisfying. And I guess I am a bit single-minded in a case like that, which...maybe doesn't always help.

Luckily, the now-beta 2022.2 editor includes some updates to the tile system that make a huge difference when you're trying to control the timing of tile animation. The core of the update is the addition of TileAnimationFlags. These allow you to pause animations,  loop them a single time, and update the physics collider, if you're fancy.  With these and the Tilemap.GetAnimationFrame/GetAnimationFrameCount functions added in earlier editions, we now have the makings of a rudimentary animation control system. 

For my prefab, this translated to a bit of code that looks roughly as follows:

Hierarchy:
- Grid (parent)
-- animated tile (background/draw layer 5)
-- collider tiles (foreground/draw layer 10)
public Tilemap animatingTilemap; //set from editor
bool animating = false;
Vector3Int animatingTilePosition;
Start() 
{
        foreach (Vector3Int position in animatingTilemap.cellBounds.allPositionsWithin)
        {
            animatingTilePosition = animatingTilePosition;
            animatingTilemap.SetTileAnimationFlag(PauseAnimation);
        }
}
OnTriggerEnter2D(Collision2D info)
{
    if(!animating && AppliesTo(info))
    {
         animating = true;
         foreach (Vector3Int position in animatingTilemap.cellBounds.allPositionsWithin)
         {
             animatingTilemap.RemoveTileAnimationFlag(PauseAnimation);
         }
         StartCoroutine(PauseAfterAnimationCycle())
    }
}
PauseAfterAnimationCyle()
{
    while(animatingTilemap.GetAnimationFrame(animatingTilePosition) == 0) 
    {
        yield return 1;
    }
    while(animatingTilemap.GetAnimationFrame(animatingTilePosition) != 0)
    {
        yield return 1;
    }
    animating = false;
    foreach (Vector3Int position in animatingTilemap.cellBounds.allPositionsWithin)
    {
        animatingTilemap.SetTileAnimationFlag(PauseAnimation);
    }
}

You can also add substeps to PauseAfterAnimationCycle if you want to do something event-like (say, spawn an arrow). 

Theoretically you could have a sparse animated Tilemap and in that case my method of assigning animatedTilePosition  in this fragment could cause issues, but in my case I only had one tile to worry about (which is its own, different, problem).  

This is not the only prefab of this type I intend to make, so I'm hoping this will be more generally useful. But even if that doesn't come to pass, I'm always happy when I manage to make a thing work in the way I think it should.

Leave a comment

Log in with itch.io to leave a comment.