• entries
707
1173
• views
435849

# Animation Systems

103 views

The old animation system was...crappy to say the least. I knew this as I was programming it, but it was working, so I ran with it. As I said, it wasn't very "instance-friendly" and thus had to be done away with. The problem was that it created new sequences on the heap and so I couldn't copy it (well, I could, but I'd have to create more sequences for each copy and copy it's frames; obviously that's not what instancing is about.)

Anyway, the old system was setup like so:
Animation class    Sequence class        Frame class            Offset            Texture            SourceRectangle            // Started with Delay here, but moved            // it to Sequence because I don't feel            // every frame needs a different delay.        Name        Frames        FrameDelay        Loop    Name    Sequences    CurrentSequence    CurrentFrame    LoopOverride // this was very hacked in lol    State    Timer    OnSequenceFinished

To those of you that can already see the problem, I give you a cookie. The problem being that Animation contains both it's data and instance information (CurrentSequence, CurrentFrame, LoopOverride, State, Timer, and OnSequenceFinished.)

So, I took out the memory allocation when allocating sequences, change things up a little bit, and added a fourth class: AnimationController. So, it looks like so now:
Animation class    Sequence class        Frame class            Offset            Texture            SourceRectangle        Name        Frames        FrameDelay        Loop        StaticFrame    Name    SequencesAnimationController class    CurrentAnimation    CurrentSequence    CurrentFrame    State    Flag    Timer

An example usage (note that this is testbed code and thus isn't very pretty lol):
ulong Temp = Kernel->LoadTexture("Anim01", "Heal2.png", 0);ulong Temp2 = Kernel->LoadTexture("Anim02", "Fire2.png", 0);// AddSequence(Name, FrameDelay, Loop, StaticFrameIndex)TestAnim.AddSequence("Anim1", 0.125f, true, -1);TestAnim.AddSequence("Anim2", 0.0625, false, -1);dbMath::Size FrameSize(192, 192);// AddFrame(SequenceName, Offset, Texture, SourceRectangle)TestAnim.AddFrame("Anim1", dbMath::Vector2::Zero, Temp, dbMath::Rectangle(192 * 0, 0, FrameSize));TestAnim.AddFrame("Anim1", dbMath::Vector2::Zero, Temp, dbMath::Rectangle(192 * 1, 0, FrameSize));TestAnim.AddFrame("Anim1", dbMath::Vector2::Zero, Temp, dbMath::Rectangle(192 * 2, 0, FrameSize));TestAnim.AddFrame("Anim1", dbMath::Vector2::Zero, Temp, dbMath::Rectangle(192 * 3, 0, FrameSize));dbMath::Vector2 Offset(-(192.0f / 2.0f), -(192.0f / 2.0f));TestAnim.AddFrame("Anim2", Offset, Temp, dbMath::Rectangle(192 * 2, 192 * 3, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp, dbMath::Rectangle(192 * 1, 192 * 3, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp, dbMath::Rectangle(192 * 0, 192 * 3, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp, dbMath::Rectangle(192 * 4, 192 * 2, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 1, 192 * 0, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 2, 192 * 0, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 3, 192 * 0, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 4, 192 * 0, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 1, 192 * 1, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 2, 192 * 1, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 3, 192 * 1, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 4, 192 * 1, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 1, 192 * 2, FrameSize));TestAnim.AddFrame("Anim2", Offset, Temp2, dbMath::Rectangle(192 * 2, 192 * 2, FrameSize));// SetAnimation(Animation, StartingSequence = "", Playing = false)TestAnimCntrlr.SetAnimation(&TestAnim, "Anim1", true);

After execution, TestAnimCntrlr is set to TestAnim.Anim1 and is playing (supplying false as the last parameter sets the sequence and sets the frame to the sequence's static frame if available or clears it.)

The system makes a couple assumptions:
1) If a sequence's static frame is less than zero, it assumes that you don't want the animation to be visible when it's stopped.
2) If a sequence's static frame is less than zero, the controller is playing, and the animation is looping (or the controller is overriding it with looping), it assumes you want to play the animation and starts from the beginning.
3) If you tell the controller to stop, it assumes you want to display the sequence's static frame (if available.)
4) When you change sequences it assumes you want to display the sequence's static frame.

The idea is that the engine will contain a animation containing the current level's effects and then the game will tell the engine "Play this effect at this location" and viola, instant effects.
Then, each actor type will have an animation associated with them which would contain their walking animations, walking with weapons, etc and then the actor class itself will contain a controller. This will work fine for this game as I don't plan on adding any more than 5 weapons for the main character and most enemies will only have 1 weapon, if any. A more in-depth game like an RPG would need to couple the animation system with a paper-doll system, but I'll worry about that at a later date.

I do need to implement a 'sprite-sheet' sort of thing along side the animation. There are plenty of things that have more than one frame, but don't animate and/or don't have multiple sequences (a mouse cursor, a button, etc.)
 Actually, the current system works fine for the above:
Animation CursorAnim;CursorAnim.AddSequence("Cursor", 0.0f, false, 0);AnimationController CursorCntrlr;CursorCntrlr.SetAnimation(&CursorAnim);// And then, for a quick example in my OnMouseMove()function, with pseudo-code:if(SomeObject.ContainsPoint(X, Y)){    switch(SomeObject.Type)    {    case ObjectTypes::Person:        {            CursorCntrlr.SetFrame(CURSOR_TALK_FRAME);            break;        }    case ObjectTypes::Item:        {            if(SomeObject.IsOwned())                CursorCntrlr.SetFrame(CURSOR_STEAL_FRAME);            else                CursorCntrlr.SetFrame(CURSOR_PICKUP_FRAME);            break;        }    // etc    }}

You wouldn't be using the controller directly, you'd be using AnimatedEntity, so it'd be less code for initialization, but that's the idea. Better yet, this way would also allow for animated cursors if it was wanted (change sequences instead of frames.)

I shouldn't even be worrying about it; Invasion won't have multiple cursors and the GUI will be programmed with a simple skin system, so it's not even a problem, but it is nice to know that the support is there and I don't have to change anything before I rip it out and stick it in a lib (obviously I'll go over it and whatnot, but the overall design shouldn't need to be changed at all.)
[/edit]

I got my todo list all done today, so I'm putting that up top. If I have time tomorrow at work I'm going to write out my goals and milestones, but it might be busy being the day after the 4th and I'm working with a 70-something year old lady; if so, I'll write it Sunday night or Monday afternoon and then pop it up there as well.

I didn't get to the entity system yet, but it's all designed out and is rather simple. Pretty much, I have a base entity class containing the simple data (position, rotation, scaling, tint, blend mode, and visibility) and then two derived classes: Entity and AnimatedEntity, each storing a Sprite and AnimationController respectively. Then there's an EntityManager class that sorts and renders everything. The base entity class itself is a valid class to use, as it contains all the required info for rendering planes.

Off to bed for me. Hopefully I can get more work done over Monday, Tuesday, and Wednesday (my days off next week.)

There are no comments to display.

## Create an account

Register a new account