June 19th, 2018 (back)

RAPTR - A Basic Renderer

Today's Product

With a basic renderer, some sprite management, and animation controls in place, we get our first signs of life. Here is Raptr walking about.

The Basics

It's really easy to get caught up with the details of writing any game. You can spend countless hours on a renderer, sound, or physics engine. Hell, writing a simple CVAR system might even eat up a weekend. So, setting short-term goals and writing your code in a way that you can come back and extend off of what you wrote without destroying your architecture is important.

For today I broke up the project into a few components:

  • Renderer
  • Sound
  • Game
  • Sprite
  • I/O
We then define some clear responsibilities. For example, the renderer is only responsible for interacting with surfaces and providing mechanisms for getting data on to the screen. It doesn't load textures, provide interaction, or even have a scenegraph. But, that's okay. We just need to provide a flexible interface that we can later on include things like scenegraphs.

Sprites and Animations

Aseprite provides convenient functionality to export as JSON. I am using the header-only picojson to process and form a PoD representation of the animation itself.

 "frames": [
   {
    "filename": "raptr 0.aseprite",
    "frame": { "x": 0, "y": 0, "w": 64, "h": 32 },
    "rotated": false,
    "trimmed": false,
    "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 32 },
    "sourceSize": { "w": 64, "h": 32 },
    "duration": 500
   },

    ...
 ],
  "meta": {
    "size": {
      "w": 448,
      "h": 32
    },
    "frameTags": [
      {
        "name": "Idle",
        "from": 0,
        "to": 2,
        "direction": "pingpong"
      },
      {
        "name": "Walk Forward",
        "from": 3,
        "to": 6,
        "direction": "forward"
      }
    ]
  }
}
With that said, we can represent a simple animation from above as:
struct AnimationFrame {
  std::string name;
  int32_t x, y, w, h;
  uint32_t duration;
};

enum class AnimationDirection {
  forward,
  backward,
  ping_pong
};

struct Animation {
  std::string name;
  int32_t frame, from, to;
  std::vector<AnimationFrame> frames;
  AnimationDirection direction;
};

using AnimationTable = std::map<std::string, Animation>;
The sprite now has rather scoped responsibilities as well. It just needs to keep track of what animation it is using and the textures to pass to the renderer.