Blazor GameDev – part 7: animations
Blazor GameDev – part 7: animations

Blazor GameDev – part 7: animations

2020, Aug 06    

Hi my fellow gamedevs! Welcome back to part 7 of our Blazor 2d Gamedev series. Today we’re going to step into the marvelous world of animations using spritesheets 🙂

Last time we saw how it’s possible to interact with the game using the mouse. It was fun and easy, but still, all we were rendering was just the Blazor logo. Nice, but not that fancy. Let’s take the game to another level and render something more interesting.

Nice, isn’t it? You can see it in your browser here.

Of course, like many of you, I can barely hold a pen, let alone draw something. I downloaded the sprites here and combined them using a custom tool that I wrote. This tool will take a list of sprite sheets as input and generate a single JSON file that looks like this:

{
  "version": 1,
  "name": "warrior",
  "animations": [
      {
          "name": "Attack1",
          "imageData": "iVBORw0KGgoAAAANSUhEUgAAAAAAABJRU5ErkJggg==",
          "imageMeta": {
              "height": 150,
              "width": 600,
              "type": "png"
          },
          "frameSize": {
              "width": 150,
              "height": 150
          },
          "fps": 12
      }
    ]
}

The animations array, as you might have guessed, contains a list of animations (eg. each single spritesheets) along with some metadata like the size of each frame, FPS, and so on. The image data is encoded as base 64.

The first step is to create a Razor component that loads this nice JSON file and builds a data structure we can later on utilize. It’s basically just boring parsing boilerplate code, but if you’re interested, you can see it here.

At this point, once we have our animations in memory, all we have to do is build a Component that can loop over the frames and render them:

public class AnimatedSpriteRenderComponent 
{
        private int _currFrameIndex = 0;
        private int _currFramePosX = 0;
        private float _lastUpdate = 0f;

        public async ValueTask Render(GameContext game, Canvas2DContext context)
        {
            if (game.GameTime.TotalTime - _lastUpdate > 1000f / Animation.Fps)
            {
                ++_currFrameIndex;
                _lastUpdate = game.GameTime.TotalTime;
                _currFramePosX = (_currFrameIndex % Animation.FramesCount) * Animation.FrameSize.Width;
            }

            await context.DrawImageAsync(Animation.ImageRef, _currFramePosX, 0,
                Animation.FrameSize.Width, Animation.FrameSize.Height,
                _transform.Position.X, _transform.Position.Y,
                Animation.FrameSize.Width, Animation.FrameSize.Height);
        }

        public AnimationsSet.Animation Animation { get; set; }
}

Some code has been omitted for brevity, the full class is available here. So what we do here is quite simple:

  1. initialize the class with some base state (eg. the current frame index and its position in the spritesheet)
  2. at every update step we check if enough time has passed and if so, we update the frame index
  3. call DrawImageAsync specifying a window over the spritesheet, basically selecting only the frame we want to render.

In this super-duper renderer we’re assuming that the frames are all on the same row:

Enough for the day. Next time we’ll see how to switch between animations using the keyboard. Ciao!

Did you like this post? Then