   Sketching out a basic sprite engine.
  This is something I put on the back-burner years ago, but a thread I saw today awoke it in my brain. Having each game object render itself is fine for a small scale project, but in the long run it will make the code less reusable and a much large project to update (if, for instance, I want to change what graphics library I'm using or even what platform I'm on). So, a portable sprite engine seems like a good thing to think and talk about.What I'm looking for is constructive criticism on my thinking here. Am I missing anything? Is anything here unnecessary or needlessly restrictive? What could I do better?My first idea is to have two basic classes: a sprite class, cSprite, and a controller class, cSpriteController. Note that all loading is done by a third class, cLoader, separate from the sprite classes. For purposes of illustration, let's say I'm making Pac-man.cSpriteController- The loader gives this class pointers to all of the image data loaded for the game. All images of the maze, background, pellets, Pac-man himself, the ghosts, and fruit are here.- Any game object will inherit from cSprite and "register" itself with the sprite controller; basically the programmer will have to call an instance of cSpriteController to instantiate an arbitrary sprite, and the sprite controller will keep a queue of the sprites which it will run through each game loop.- Once per game loop, the sprite controller will call upon sprites to update and draw themselves (if they are not occluded) and flip the buffer.cSprite- Game objects which will ever be drawn to the screen will inherit from this class.- Each instance of a cSprite will have pointers to whatever images it's associated with.- Each instance will have some set of "animation sequences", a literal ordering of images/frames to draw, as well as timing information (i.e. how long to hold each frame) and potentially SFX scheduling (when and what to warp, etc).- Either built into cSprite or into children classes (also a part of the sprite engine) can be various generic special effects, like transparency, warping, etc. Possibly some effects schedules should be stored independently of the animation sequence (suppose for instance I want Pac-man to flip between more and less translucent on a rhythm entirely different than his chomping).- All classes that subtype from cSprite will be required to have an update() function which manages required member variables such as: x,y coordinates, which animation sequence is being used, what "level" it is on (should it be above or below other sprites), and whether or not it should be drawn right now regarding it's own internal state. (Occlusion will be handled by the sprite controller; the sprite will handle clipping itself.) Basically, update() will keep the internal state maintained so the sprite controller need only call upon the sprite to update and draw itself.Just this design alone warrants other (bigger) projects, such as a resource file format and a scripting engine...but let's stick with the sprite engine itself for now. (Assume I load all images file-by-file and hand-code animation sequences and behaviors.) I feel sure there's a lot I'm failing to think about here, but this seems to me to be a sensible foundation to start on.What do people think? Thanks in advance for your time.EDIT: I'm aware I'm sort of re-writing a kitchen sink here, but I think it's academically valuable at least as a thought experiment. Also, I can't afford Torque right now.[Edited by - serratemplar on July 27, 2010 3:17:53 PM]
  Try to separate different kinds of 2D graphics for a performance boost and logical separation that can keep you sane. Let's assume you have a game level where the visible region of the back buffer has 80x80 tiles. It would be safe to assume these guys should NOT draw themselves or update, as they are likely static graphics. If they did, you would have 6400 update() calls and you would be pressured to expose functionality that cSprite can take advantage of that tiles do not necessarily need. Sure, tiles are still 2D graphics and are therefore sprites, but it just comes down to making sure you do not bog down the engine with tasks it should not worry about.You would also want to make a fore/background object that guarantees to be placed below or above everything, regardless of how the end-developer messes with the draw order. Backgrounds can also provide scrolling functionality.YoYoGame's Game Maker allowed you to mess around with tiles during runtime, but you had to shuffle through the container that held them, grab some handles and operate on them through an object that WAS dynamic.Just my two cents.Regards, and good luck.   ;  That is a very good point; tiles should be separated into their own routines. Thank you.   ;  *bump*Perhaps people may recommend a common open source sprite engine so I can dissect it? Not a lot of bites on this thread; has me worried it's either uninteresting or presented incorrectly.   ;Quote:Not a lot of bites on this thread; has me worried it's either uninteresting or presented incorrectly.I think sometimes broad architectural questions of this sort can be a little hard to answer (since there's really no one 'right way' to do something like this), but here are a few thoughts:The architecture you describe sounds like it might be reasonable for a very small-scale game, such as a simple arcade or puzzle game. I'm not sure how well it would scale though. Inheritance-based systems like the one you describe seem to be falling out of favor (mostly due to their rigidity, I think). More in favor now are systems based on composition or separate 'components' that can be combined in different ways. In such a system, a 'sprite' would most likely just be a visual component (and only a visual component), with other aspects of the entity (e.g. movement, etc.) handled by other components. Among other things, this approach is more in keeping with the 'single responsibility principle' (in that each class/component has one, and only one, clearly defined roll).Again though, if the system is intended specifically for small-scale arcade/puzzle/etc. games, the system you describe might work just fine.Also, this does sound a bit like reinventing the wheel. It sounds like you have some constraints you're working with here (budget, etc.), but there are quite a few options out there, including free ones. I can't really recommend anything off the top of my head, but if you could provide some more info (language(s) used, target platform(s) etc.), someone could probably offer some suggestions.Finally, minor detail, but I'd recommend dropping the 'c' prefix from your class names.   ;  In fact, last night I just finished the first version of my graphics library wrapped over XNA that does just about what you mentioned although with a slight different implementation as my classes are named and structured a bit differently (I'd share the UML but I don't have access to upload utilities right now).I did what you mentioned as well, I have a certain type of resource file (called a LoadFile) which is parsed to load different objects (2D sprites, 3D models, fonts, etc.).  Each of those objects are given a series of methods that I can generically call (Update() and Render()).Now instead of having a 'Sprite Controller' I have ILevel which is an abstract class (not interface) that contains a list of something called IRhinoGraphicsComponent's.  The IRhinoGraphicsComponent is the highest level and is solely an interface to give any inheriting classes those Update() and Render() methods.  This way I can store everything together and just run through and update/render everything.  The user's create their own inherited versions of ILevel and magically have a level that will fully update and render itself with darn near 1 line of code added by the user.However, zyrolasting has a great point that I have yet to include, the ability to prevent updating something static.   ;Quote:Original post by serratemplarcSpriteController- The loader gives this class pointers to all of the image data loaded for the game. All images of the maze, background, pellets, Pac-man himself, the ghosts, and fruit are here.I'm missing a class for the image data. How about creating an ImageData class that contains the data for a single image, and a dictionary (std::map) that maps filenames (or unique identifiers) to image data?Quote:- Any game object will inherit from cSprite and "register" itself with the sprite controller; basically the programmer will have to call an instance of cSpriteController to instantiate an arbitrary sprite, and the sprite controller will keep a queue of the sprites which it will run through each game loop.So the SpriteController class holds image data (it's a cache) and it manages sprites? I'd separate these two responsibilities into two different classes: ImageCache and SpriteController.Quote:- Once per game loop, the sprite controller will call upon sprites to update and draw themselves (if they are not occluded) and flip the buffer.You may want to make a distinction between static and dynamic sprites. Also, you'll want to keep track of animation data somehow. In addition to image data, a dynamic sprite also needs a reference to some animation data, and it needs to keep track of what sequence and frame it's at. You may need some additional bookkeeping as well (how much time until the next frame, looped playback or not, etc.).Quote:cSprite- Game objects which will ever be drawn to the screen will inherit from this class.Game objects can be represented by one or more sprites, but that doesn't mean they are sprites. I would use Sprites as member variables in game objects instead. It's more flexible.Quote:- Each instance of a cSprite will have pointers to whatever images it's associated with.Good.Quote:- Each instance will have some set of "animation sequences", a literal ordering of images/frames to draw, as well as timing information (i.e. how long to hold each frame) and potentially SFX scheduling (when and what to warp, etc).Animation data can be shared between various sprites. Consider putting this into a separate class, much like the ImageData class I proposed. Loading such data from a text file shouldn't be too difficult.Quote:- Either built into cSprite or into children classes (also a part of the sprite engine) can be various generic special effects, like transparency, warping, etc. Possibly some effects schedules should be stored independently of the animation sequence (suppose for instance I want Pac-man to flip between more and less translucent on a rhythm entirely different than his chomping).Consider creating an Effect class that operates on a certain sprite. This class provides an update function, and stores some effect data. Using inheritance for this is limiting - if both AnimatedSprite and EffectSprite inherit from Sprite, how would you apply such effects on AnimatedSprite? An Effect class takes a Sprite pointer, and can operate on any Sprite class.Quote:- All classes that subtype from cSprite will be required to have an update() function which manages required member variables such as: x,y coordinates, which animation sequence is being used, what "level" it is on (should it be above or below other sprites), and whether or not it should be drawn right now regarding it's own internal state. (Occlusion will be handled by the sprite controller; the sprite will handle clipping itself.) Basically, update() will keep the internal state maintained so the sprite controller need only call upon the sprite to update and draw itself.I would handle rendering order either with a z variable, and let the renderer (SpriteController) deal with it, or I'd create a Layer class that can hold a collection of sprites. Such a Layer class could have a camera modifier, to allow for parallax scrolling. The SpriteController class then would hold a list of layers, rather than sprites directly.I built a 2D framework some time ago, gradually improving it throughout a variety of prototypes and games. Eventually, I wrote Tilemap and SpriteCollection classes, that could be used like any other visual object (they had a position and dimension, and a draw() function). They allowed me to compose levels of freely placed sprites and tilemaps stitched together.   ;  First, thank you everyone for your replies; they've been very helpful in my thinking. I've got some additional questions.Several of you suggested separating mechanisms in a way that seemed counter-intuitive to me. I'm not saying your wrong - in reality, I believe you are right - but I'm not sure I understand it. For instance, the suggestion was made to seperate animation control/state into it's own class, separate from sprite information. Does this mean then I'd want to do something like this?class GameObject:- All game objects are (or inherit from) this class, which will have the ability to call the DrawStuff(better name pending) class to render things to the screen.- has member(s) of class Sprite, with keys/references to various images in the ImageCache.- has member(s) of class AnimationOp(or some more meaningful name); since this information can be shared between other sprites (which may have similar/identical animation behaviors under certain circumstances), should this only be a reference/pointer to a shared AnimationData instance? Should I allow more than one AnimationData member per game object that is an animated sprite? (I'm guessing yes, thinking of things like the 'cheer' animation from various 2d Final Fantasy titles and the like, for  instance.)- has member(s) of class SFXOp which operates on a particular sprite. Perhaps something with inheritance/polymorphism is appropriate here, where there are several children of a base class SFXOp which have different canned effects and can be referenced through SFXOp? Or (I feel this is better) each instance of SFXOp has several canned effects and/or the ability to follow a script given from somewhere else in the system.class Sprite:- I'm not clear on why/how to distinguish between a static sprite (one that is always the same image) and a dynamic sprite (one that may be animated/change entirely in appearance); if I got that distinction correct, only sprites which are animated will need animation updates, but otherwise they both need to be drawn to the screen in the same way.- Sprites will have pointers into the ImageCache to their own image data, and will have location information (where to be drawn, in a Rect, effectively), and z/layer information.This does seem more flexible, but I sense it still needs some tinkering.   ;Quote:Original post by serratemplarFor instance, the suggestion was made to seperate animation control/state into it's own class, separate from sprite information. Does this mean then I'd want to do something like this?You could create an AnimatedSprite class that inherits from Sprite. It's drawable and references image data like a sprite, but it's also updateable, it references some animation data and it holds some animation control state. Obviously, if you want animated sprites to work, you'll have to put references (pointers) to them in a separate updateable list. Alternately, you could let game objects update their animated sprites, though that seems more tedious.Quote:class GameObject:- All game objects are (or inherit from) this class, which will have the ability to call the DrawStuff(better name pending) class to render things to the screen.I wouldn't let game objects actually initiate a draw action. They'll modify their representation (sprites) when they need to, yes, but the actual drawing is better performed in the renderer, the system that 'manages' all sprites. Also, you don't want half of your game objects being rendered at an old position because the camera was moved after they had been rendered already.Quote:- has member(s) of class Sprite, with keys/references to various images in the ImageCache.Yup. Sprites can keep a reference to an Image, regardless of where that Image actually comes from.Quote:- has member(s) of class AnimationOp(or some more meaningful name); since this information can be shared between other sprites (which may have similar/identical animation behaviors under certain circumstances), should this only be a reference/pointer to a shared AnimationData instance? Should I allow more than one AnimationData member per game object that is an animated sprite? (I'm guessing yes, thinking of things like the 'cheer' animation from various 2d Final Fantasy titles and the like, for  instance.)Nope. I'd put this in the AnimatedSprite class. No need to clutter a game object with this. Remember, game objects are not sprites. They can be represented by one or more sprites. Also, the easier a sprite is to use, the better. If you have to create an AnimationOp object for every animated sprite, manually, then your animated sprite is not very user-friendly yet.Quote:- has member(s) of class SFXOp which operates on a particular sprite. Perhaps something with inheritance/polymorphism is appropriate here, where there are several children of a base class SFXOp which have different canned effects and can be referenced through SFXOp? Or (I feel this is better) each instance of SFXOp has several canned effects and/or the ability to follow a script given from somewhere else in the system.One class per distinct effect should work fine. You can then have multiple effect objects operate on a single sprite (as long as they're not conflicting with each other, heh). Letting game objects own these effect objects sounds like a fair approach.Quote:class Sprite:- I'm not clear on why/how to distinguish between a static sprite (one that is always the same image) and a dynamic sprite (one that may be animated/change entirely in appearance); if I got that distinction correct, only sprites which are animated will need animation updates, but otherwise they both need to be drawn to the screen in the same way.Yup. Which is why I suggest letting AnimatedSprite inherit from Sprite. That allows you to treat them like sprites (which they are!) for all intents and purposes, except that they're also updateable (and, of course, they should provide some animation control functionality - so game objects can control animation playback).Quote:- Sprites will have pointers into the ImageCache to their own image data, and will have location information (where to be drawn, in a Rect, effectively), and z/layer information.Sprites just need pointers/references to image data. Whether such image data comes from a cache shouldn't matter. Other than that, sounds good.   ;  Captain P, you are immensely helpful.Where would it be appropriate to keep location (x,y coords) and z-layer info with this model? If not in the sprite class (where I think it should be) then in the game object class?I like the idea of pushing pointers to animated sprite objects (and sprite objects, and other game objects) onto different (I presume circular) queues so they can be updated/drawn; that cleans up checking on them nicely. I think it seems smarter to have two different queues (one for drawing and one for animating/updating) as opposed to putting them all on one queue (i.e. certain sprites may be done animating but may wish to still be drawn for some reason). Make sense?Where should I manage "global" timing information, e.g. the timer(s) involved in maintaining frame rate? (Animation timing data should be kept in the state variables of each animation-sprite instance that needs it, in almost all cases I can imagine.)Thanks again!   