Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I see a lot of tutorials explaining how to do 2D Sprite Animation with sprite sheets, based on the user's input (i.e. Move Left, Move Right ) etc.. etc..

I have already incorporated an AnimationManager in my game to handle these types of animations.

What I would like to do however is to play animations that have a set start and end position with a given speed. I'm trying to think of how I'd like to do it and perhaps leverage some of the already written Animation code I have.

Things that I feel stumped about:

  1. Should I use a timer? How will I increment the Sprite's position based on the speed otherwise? It makes logical sense to perhaps put it in the Update() however then I would need to keep track of whether the sprite is animating and the desired final Position?
  2. Clearly the speed of the animation (FPS) should depend on the distance I'd like to travel so that the animation runs throughout the length of any distance?
  3. Who should handle the moving of the position and animation if it's dependant on a distance? My animationManager or the sprite himself?

I couldn't seem to find any demo/example code online, so any feedback or links I'd greatly appreciate!

I'll post my AnimationManager so far to give a fill of what I'm working with:

    public class AnimationManager
{
    #region Member Variables
    public delegate void AnimationComplete();
    public event AnimationComplete AnimationFinished;

    private Dictionary<String, Animation> _animations = null;
    private Animation _currentAnimation;
    private int _frameIndex;
    private float _elapsedTime;
    private AnimatedSprite _sprite;
    #endregion

    #region Initializers
    public AnimationManager(AnimatedSprite sprite)
    {
        _animations = new Dictionary<string, Animation>();
        _frameIndex = 0;
        _elapsedTime = 0f;
        _currentAnimation = null;
        _sprite = sprite;
    }
    #endregion

    #region Public Methods

    public void Update(GameTime gameTime)
    {
        if (_currentAnimation == null)
            return;

        _elapsedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
        if (_elapsedTime > _currentAnimation.TimePerFrame)
        {
            _frameIndex++;

            if (_frameIndex > _currentAnimation.NumberOfFrames - 1 && !_currentAnimation.IsLooping)
            {
                _frameIndex--; 
                OnAnimationComplete();
            }

            _frameIndex = _frameIndex % _currentAnimation.NumberOfFrames;
            _elapsedTime -= _currentAnimation.TimePerFrame;
        }
    }

    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(_currentAnimation.Texture, _sprite.Position, _currentAnimation.GetFrame(_frameIndex).toRectangle(), Color.White, _sprite.Rotation, _currentAnimation.Origin, _sprite.Scale, SpriteEffects.None, _sprite.Layer);
    }

    public void PlayAnimation(String key)
    {
        Animation anim = _animations[key];
        PlayAnimation(anim);
    }

    public void AddAnimation(Animation anim, String key)
    {
        _animations[key] = anim;
    }

    public Animation GetAnimation(String key)
    {
        return _animations[key];
    }

    #endregion

    #region Private/Protected Methods

    protected virtual void PlayAnimation(Animation anim)
    {
        if (_currentAnimation == anim)
            return;

        _currentAnimation = anim;
        _frameIndex = 0;
    }

    // Invoke the Changed event; called whenever list changes
    protected virtual void OnAnimationComplete()
    {
        if (AnimationFinished != null)
            AnimationFinished();
    }

    #endregion
}
    public class Animation
{
        #region Fields
    protected String _name;
    protected List<Frame> _frames;
    protected Texture2D _spriteSheet;
    protected float _framesPerSec;
    protected bool _looping;
    protected int _width;
    protected int _height;
    protected Vector2 _origin;
    protected Animation _nextAnimation = null;
    #endregion


    #region Properties

    public Animation NextAnimation
    {
        get { return _nextAnimation; }
        set { _nextAnimation = value; }
    }

    public float TimePerFrame
    {
        get { return 1.0f / (float)_framesPerSec; }
    }

    public Vector2 Origin
    {
        set { _origin = value; }
        get { return _origin; }
    }

    public Texture2D Texture
    {
        get { return _spriteSheet; }
    }

    public int Width
    {
        get { return _width; }
    }

    public int Height
    {
        get { return _height; }
    }

    public String Name
    {
        get { return _name; }
    }

    public float Speed
    {
        get { return _framesPerSec; }
        set { _framesPerSec = value; }
    }

    public bool IsLooping
    {
        get { return _looping; }
    }

    public int NumberOfFrames
    {
        get { return _frames.Count ; }
    }
    #endregion


    #region Initialization

    public Animation(String name,  Texture2D spriteSheet, bool looping, float speed, int width, int height)
    {
        _name = name;
        _frames = new List<Frame>();
        _spriteSheet = spriteSheet;
        _framesPerSec = speed;
        _looping = looping;
        _width = width;
        _height = height;
        _origin = Vector2.Zero;
    }

    #endregion

    public void AddFrame(Vector2 pos)
    {
        _frames.Add(new Frame(pos, _width, _height));
    }

    public Animation.Frame GetFrame(int frameIndex)
    {
        return _frames[frameIndex];
    }


}
share|improve this question
    
I did find this article however it didn't really explain anything. –  Setheron Sep 2 '11 at 15:39

2 Answers 2

up vote 1 down vote accepted

(Total Distance / Distance per Second) should tell you how many seconds it will take to get from where you are, to where you want to be. Call it totalSeconds

totalSeconds / Animation FPS tells us how long each frame should be displayed for. Call it frameTime

Now you just need to have a TimeSpan that accumulates the delta times:

timeSinceLastFrame = timeSinceLastFrame.AddSeceonds(gameTime.ElapsedGameTime.TotalSeconds);

(or something like that). If the timeSinceLastFrame is greater than the 'frameTime', show the next frame and subtract the frame time from the timeSinceLastFrame:

timeSinceLastFrame = timeSinceLastStageChange.Subtract(new TimeSpan(0, 0, 0, 0, frameTime));

Subtracting the frame time in this way makes sure that we don't lose small pieces of our timing mechanism. And that's it. This should move the frame forward through a fixed number of frames over a set world distance.

share|improve this answer
    
What about actually moving the Sprite? Do I put that in a Timer? I rather put a timer that just checks the end condition rather than bloat up my Update method? –  Setheron Sep 2 '11 at 17:01
    
@Setheron, in a 2D overhead game, it's usually done with a movement rate (distance per second), direction (angle), and a delta time (how much time has passed since the last frame), like so: pastebin.com/ZcAsFSpx For changing movement speeds, this will still work, but the formulas I gave above will need to be recalculated regularly –  John McDonald Sep 2 '11 at 17:13
  1. Look into interpolation. It is a fancy word for moving something from A to B over a specified time. Take a look at this post. He explains it in detail, gives the code, and tell you how to use it. I use this anytime I need to move something from A to B.
  2. If you want to only have the animation loop 1 time through the movement, then yes adjust the FPS depending on your distance of travel. Keep in mind that the animation will play slower for longer distances. If you are having the sprite walk then it might look funny. Most of the time the animation should just be played at a constant FPS. Once you reach your target position you can stop it
  3. Regarding who should make him move, it kind of depends on your architecture. I like to design it so the sprite keeps track of the input and moves himself (assuming it is a player controlled character. In your case I would skip the input check and just run the interpolator.

So how can you do it? It is hard to recommend something when I don't have the whole picture. Going by what I do know though what if you tried this.

class AnimatedSprite
{
    Vector2 position;

    public AnimatedSprite(Vector2 posA, Vector2 posB, float timeToTake)
    {
        this.position = posA;
        Interpolator.Create(posA.X, posB.X, timeToTake, i=>this.position.X=i.value,null);
    }

    //somewhere (whether here or in your main loop..doesnt matter) call update on the interpolator class
    void Update(GameTime gameTime)
    {
        Interpolator.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
    }  
}

Now you just add the sprite to the AnimationManager, and do what ever it needs.

It might be important to point out that the interpolator class can only increment one float. So if you wanted to animated over X and Y you would create two interpolators - one for each one. You still only need to call Update() one time though. If you wanted you can even extend the code to interpolate over multiple points. Allowing you to move the sprite along a curve.


It looks like he updated the code. As far as I can tell the latest version is here

share|improve this answer
    
Okay cool, I guess that is something similar to what I have done. My problem is however is that my animation represents a moving animation (jumping from point A to B, so I'm stil learning on the right way on designing it so that the distance matches the animation!) –  Setheron Sep 2 '11 at 18:52
    
Ok, in that case I assume you know how far you want to move it, right? So just calculate the FPS based off of that. –  Joe Sep 2 '11 at 19:15
    
yep that's what I did. I ended up just using Delegates/Events that the animation fires as well to tell the sprite to turn of his speed (therefore he'll stop animating). –  Setheron Sep 2 '11 at 19:42
    
Now I have that other issue (You commented on as well) with my textures being no good :( –  Setheron Sep 2 '11 at 19:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.