Sprites, Frames, Tiles, and Dimensions - Sprite Redesign

Started by
2 comments, last by SeanMiddleditch 8 years, 3 months ago

I recently brought my idea of how to define Sprites into question when I considered whether sprite frames should be allowed to have different sizes. As a result of this I came up with a few questions to help me decide what the correct design should be. I am hoping to have some light shed on the answers to these questions.

1) My games generally separate the idea of tilesets from all other sprites, so should I design a semantic difference between the two ideas? ie. Is it worth designing a TileSet class to handle tiles which have the benefit of being statically sized, and then a separate Sprite class for that which may have frames of different sizes?

2.a) Should a sprite be allowed to contain frames of differing sizes?

2.b) Should each frame be a different object (eg. class Sprite; class SpriteFrame)? Thus allowing dimensions to be owned by a frame instead of by a sprite.

3) Should sprites draw anchored from the center or a corner?

The problem is I intend on re-using this code for multiple projects and thus cannot afford to simply fit my current needs as I must also consider my future needs. Seeing as I don't want to refactor code that is currently being refactored after redesigning this code later on to fit the needs of a new project.

Hearing about common design practices for this sort of thing is great, but I would also appreciate if I knew them with regards to the ~3 questions first. This is mainly because I never feel the need to stick to common practices for the sake of being common or understood. I always feel the design should feel right for the intended usage. The benefit of redesigning the wheel allows one to view problems otherwise lost in time, and thus understand why common designs are common. Sometimes, I'm sure, you can improve the common design after fully knowing why it is the common design, at least for your own purposes. I am sure nobody here is a blind follower; I just happen to be a hands-on/visual learner.

Advertisement

At the lowest level I'd expect a simple Texture or TextureSlice concept (which may be an object, or a simple struct, or even just some conceptual data that's implicit in a set of separate arrays... don't try to shove conceptual thing into a distinct object, that's an abuse of oop).

TileSets would be a collection of texture slices with additional data, e.g. possibly collision data and tiling data. They would allow mapping a tile id efficiently to a texture slice. They might also contain some simple animation data.

Sprites are indeed better broken into sprites and frames conceptually. A sprite is the metadata that determines the frames, and possibly multiple tracks of frames (because you might have more than one animation). Or you might have each sprite be a single animation (or unanimated) and have a separate higher-level animated sprite controller that swaps between sprites for different animations.

I highly recommend the distinct sprite frame though as each frame of an animation has a lot of potentially different data. The sprite might be different visual sizes. It might have different hit boxes for physics. It might have different attachment points for setting items/equipment or spawning effects.

Regarding the origin, I generally also make that data (usually from a corner, since it's more accurate than "center" given that your sprites can have both odd and even dimensions). Some sprites or individual frames may want the origin to be the middle-bottom (under the feet), some the bottom-left corner, some the upper-right corner, etc. Define the "true" origin as a corner (usually the bottom- or top-left) and then provide an offset data with each sprite/frame.

And again, don't think of these as objects. Instead of a Sprite having an array of SpriteFrames, maybe the Sprite just has an array of texture slices, an array of origin offsets, an array of hitboxes, etc. A conceptual SpriteFrame without actually having a new object/class. This is especially useful for languages like Java where each object has an unreasonable amount of overhead due to every object being heap-allocated (C# does too if you use 'class' but not if you use 'struct', while C++ gives you total control).

Sean Middleditch – Game Systems Engineer – Join my team!

At the lowest level I'd expect a simple Texture or TextureSlice concept (which may be an object, or a simple struct, or even just some conceptual data that's implicit in a set of separate arrays... don't try to shove conceptual thing into a distinct object, that's an abuse of oop).

What would a texture slice be composed of? My gut says it would be the data I push to the VBO for a sprite and all its frames (ie. x,y,z,u,v). Is this right?

eg.


for ( m_FrameIndex = 0; m_FrameIndex < m_Frames; ++m_FrameIndex )
{
Data >> x;
Data >> y;
///Bottom Left
m_Vertices[( m_FrameIndex * 20 ) + 0] = ( m_PixelWidth - width ) / 2.0f;
m_Vertices[( m_FrameIndex * 20 ) + 1] = ( m_PixelHeight - height ) / 2.0f;
m_Vertices[( m_FrameIndex * 20 ) + 2] = 0.0f;
m_Vertices[( m_FrameIndex * 20 ) + 3] = (float)x / (float)Tex_width;
m_Vertices[( m_FrameIndex * 20 ) + 4] = 1.0f - ( ( (float)y + (float)height ) / (float)Tex_height );


What would a texture slice be composed of? My gut says it would be the data I push to the VBO for a sprite and all its frames

The minimum data really is just a rectangular region of a texture.

struct TexSlice {
  GpuTextureId texture;
  Rectangle dimensions;
};

That's it. A slice does not have multiple frames because a texture slice is not itself an animation. A slice may or may not correspond to a frame of an animation. You can reuse the texture slice concept to represent glyphs in a bitmap font, individual icons in a UI asset, frames of sprite animations, and so on.

I wouldn't write all that code to calculate texture offsets, either. That intimately binds your art assets to your code. Artists are not going to be rewriting a bunch of math every time they change something in a character animation. Let the artists specify where the frames of animation are in the source asset using a text file, or import the animation from one of the common sprite animation tools that are around these days. Make it all data driven.

Sean Middleditch – Game Systems Engineer – Join my team!

This topic is closed to new replies.

Advertisement