如何避免混合不同的精灵动画速度?

时间:2013-02-27 11:04:16

标签: xna xna-4.0

我知道为什么每次调用Update_Animation(Point sheetSize,TimeSpan frameInterval,GameTime gameTime)函数时,精灵动画的速度变得更快。它是否由gameTime引起?怎么解决?感谢。

class Character
{
    #region Field
    // Character file
    System.Xml.Linq.XDocument doc;

    // The texture with animation frames
    Texture2D animationTexture_Stand;
    Texture2D animationTexture_Run;
    Texture2D animationTexture_Hit;

    // The size of single frame inside the animationTexture
    public Point frameSize_Stand;
    public Point frameSize_Run;
    public Point frameSize_Hit;

    // The size and structure of whole frames sheet in animationTexture. The animationTexture could
    // hold animaton sequence organized in multiple rows and multiple columns.
    Point sheetSize_Stand;
    Point sheetSize_Run;
    Point sheetSize_Hit;

    // Amount of time between frames
    TimeSpan frameInterval_Stand;
    TimeSpan frameInterval_Run;
    TimeSpan frameInterval_Hit;
    #endregion

    #region Initialization
    /// <summary>
    /// Constructor of a character class
    /// </summary>
    /// <param name="characterName">the name of the xml file of the character without .xml</param>
    /// <param name="content">ContentManager instance</param>
    public Character(String characterName, ContentManager content)
    {
        doc = System.Xml.Linq.XDocument.Load("Content/" + "Player/"  + characterName + ".xml");

        // Get the first sprite info from the XML definition
        var stand = doc.Root.Element("stand");
        var run = doc.Root.Element("run");
        var hit = doc.Root.Element("hit");

        animationTexture_Stand = content.Load<Texture2D>(stand.Attribute("SheetName").Value);
        animationTexture_Run = content.Load<Texture2D>(run.Attribute("SheetName").Value);
        animationTexture_Hit = content.Load<Texture2D>(hit.Attribute("SheetName").Value);

        frameSize_Stand = new Point();
        frameSize_Stand.X = int.Parse(stand.Attribute("FrameWidth").Value, NumberStyles.Integer);
        frameSize_Stand.Y = int.Parse(stand.Attribute("FrameHeight").Value, NumberStyles.Integer);
        frameSize_Run = new Point();
        frameSize_Run.X = int.Parse(run.Attribute("FrameWidth").Value, NumberStyles.Integer);
        frameSize_Run.Y = int.Parse(run.Attribute("FrameHeight").Value, NumberStyles.Integer);
        frameSize_Hit = new Point();
        frameSize_Hit.X = int.Parse(hit.Attribute("FrameWidth").Value, NumberStyles.Integer);
        frameSize_Hit.Y = int.Parse(hit.Attribute("FrameHeight").Value, NumberStyles.Integer);

        sheetSize_Stand = new Point();
        sheetSize_Stand.X = int.Parse(stand.Attribute("SheetColumns").Value, NumberStyles.Integer);
        sheetSize_Stand.Y = int.Parse(stand.Attribute("SheetRows").Value, NumberStyles.Integer);
        sheetSize_Run = new Point();
        sheetSize_Run.X = int.Parse(run.Attribute("SheetColumns").Value, NumberStyles.Integer);
        sheetSize_Run.Y = int.Parse(run.Attribute("SheetRows").Value, NumberStyles.Integer);
        sheetSize_Hit = new Point();
        sheetSize_Hit.X = int.Parse(hit.Attribute("SheetColumns").Value, NumberStyles.Integer);
        sheetSize_Hit.Y = int.Parse(hit.Attribute("SheetRows").Value, NumberStyles.Integer);

        frameInterval_Stand = TimeSpan.FromSeconds(1.0f / int.Parse(stand.Attribute("Speed").Value, NumberStyles.Integer));
        frameInterval_Run = TimeSpan.FromSeconds(1.0f / int.Parse(run.Attribute("Speed").Value, NumberStyles.Integer));
        frameInterval_Hit = TimeSpan.FromSeconds(1.0f / int.Parse(hit.Attribute("Speed").Value, NumberStyles.Integer));

    }
    #endregion

    #region Update Animation
    TimeSpan nextFrame;
    Point currentFrame;

    public void Update_Animation(Point sheetSize, TimeSpan frameInterval, GameTime gameTime)
    {
        if (nextFrame >= frameInterval)
        {
            currentFrame.X++;

            if (currentFrame.X >= sheetSize.X)
            {
                currentFrame.X = 0;
                currentFrame.Y++;
            }

            if (currentFrame.Y >= sheetSize.Y)
                currentFrame.Y = 0;

            nextFrame = TimeSpan.Zero;
        }
        else
        {
            nextFrame += gameTime.ElapsedGameTime;
        }
    }  
    #endregion

    #region Update Control
    KeyboardState mPreviousKeyboardState;

    String action;
    SpriteEffects effect = SpriteEffects.FlipHorizontally;

    Vector2 feetPosition = new Vector2(0, 450);
    Vector2 mSpeed = Vector2.Zero;
    Vector2 mDirection = Vector2.Zero;
    Vector2 mStartingPosition = Vector2.Zero;

    int CHARACTER_SPEED = 50;
    int MOVE_LEFT = -5;
    int MOVE_RIGHT = 5;
    int MOVE_UP = -5;
    int MOVE_DOWN = 5;


    enum State
    {
        Walking,
        Jumping,
        Hitting,
    }

    State mCurrentState = State.Walking;

    public void UpdateControl(GameTime gameTime)
    {
        KeyboardState aCurrentKeyboardState = Keyboard.GetState();

        feetPosition += mDirection * mSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        UpdateMovement(aCurrentKeyboardState, gameTime);
        UpdateJump(aCurrentKeyboardState, gameTime);

        mPreviousKeyboardState = aCurrentKeyboardState;
    }

    private void UpdateMovement(KeyboardState aCurrentKeyboardState, GameTime gameTime)
    {
        if (mCurrentState == State.Walking)
        {
            action = "stand";
            Update_Animation(sheetSize_Stand, frameInterval_Stand, gameTime);

            mSpeed = Vector2.Zero;
            mDirection = Vector2.Zero;

            if (aCurrentKeyboardState.IsKeyDown(Keys.Left) && !aCurrentKeyboardState.IsKeyDown(Keys.Right))
            {
                action = "run";
                effect = SpriteEffects.None;

                mSpeed.X = CHARACTER_SPEED;
                mDirection.X = MOVE_LEFT;

                Update_Animation(sheetSize_Run, frameInterval_Run, gameTime);
            }
            else if (aCurrentKeyboardState.IsKeyDown(Keys.Right) && !aCurrentKeyboardState.IsKeyDown(Keys.Left))
            {
                action = "run";
                effect = SpriteEffects.FlipHorizontally;

                mSpeed.X = CHARACTER_SPEED;
                mDirection.X = MOVE_RIGHT;

                Update_Animation(sheetSize_Run, frameInterval_Run, gameTime);
            }

            if (aCurrentKeyboardState.IsKeyDown(Keys.Z) && mPreviousKeyboardState.IsKeyUp(Keys.Z))
            {
                mCurrentState = State.Hitting;
            }
        }

        if (mCurrentState == State.Hitting)
        {
            action = "hit";

            Update_Animation(sheetSize_Hit, frameInterval_Hit, gameTime);
            mCurrentState = State.Walking;
        }
    }

    private void UpdateJump(KeyboardState aCurrentKeyboardState, GameTime gameTime)
    {
        if (mCurrentState == State.Walking)
        {
            if (aCurrentKeyboardState.IsKeyDown(Keys.Up) && mPreviousKeyboardState.IsKeyUp(Keys.Up))
                Jump();
        }

        if (mCurrentState == State.Jumping)
        {
            if (mStartingPosition.Y - feetPosition.Y > 150)
                mDirection.Y = MOVE_DOWN;

            if (feetPosition.Y > mStartingPosition.Y)
            {
                feetPosition.Y = mStartingPosition.Y;
                mCurrentState = State.Walking;
                mDirection = Vector2.Zero;
            }
        }
    }

    private void Jump()
    {
        if (mCurrentState != State.Jumping)
        {
            mCurrentState = State.Jumping;
            mStartingPosition = feetPosition;
            mDirection.Y = MOVE_UP;
            mSpeed = new Vector2(CHARACTER_SPEED, CHARACTER_SPEED);
        }
    }

    #endregion

    #region Draw Animation
    Texture2D animationTexture;
    Point frameSize;

    /// <summary>
    /// Rendering of the animation
    /// </summary>
    /// <param name="spriteBatch">SpriteBatch in which current frame will be rendered</param>
    /// <param name="position">The position of the current frame</param>
    /// <param name="scale">Scale factor to apply on the current frame</param>
    /// <param name="spriteEffect">SpriteEffect to apply on the current frame</param>
    public void Draw_Animation(SpriteBatch spriteBatch)
    {
        if (action == "stand")
        {
            Assign_Sprite(animationTexture_Stand, frameSize_Stand);
            Draw_Action(spriteBatch, feetPosition, 1.0f, effect);
        }
        else if (action == "run")
        {
            Assign_Sprite(animationTexture_Run, frameSize_Run);
            Draw_Action(spriteBatch, feetPosition, 1.0f, effect);
        }
        else if (action == "hit")
        {
            Assign_Sprite(animationTexture_Hit, frameSize_Hit);
            Draw_Action(spriteBatch, feetPosition, 1.0f, effect);
        }

    }

    private void Assign_Sprite(Texture2D assignAnimationTexture, Point assignFrameSize)
    {
        animationTexture = assignAnimationTexture;   
        //currentFrame = AssignCurrentFrame;
        frameSize = assignFrameSize;
    }

    private void Draw_Action(SpriteBatch spriteBatch, Vector2 position, float scale, SpriteEffects spriteEffect)
    {
        spriteBatch.Draw(animationTexture, position - new Vector2(0, frameSize.Y), new Rectangle(
            frameSize.X * currentFrame.X,
            frameSize.Y * currentFrame.Y,
            frameSize.X,
            frameSize.Y),
            Color.White, 0f, Vector2.Zero, scale, spriteEffect, 0);
    }
    #endregion
}

1 个答案:

答案 0 :(得分:1)

我不确定这是不是问题,但这段代码关注我:

if (nextFrame >= frameInterval)
{
    //Blah blah

    nextFrame = TimeSpan.Zero;
}

如果gameTime.ElapsedGameTime不常规(如果您的游戏出现性能问题),则此方法将无法正确计时。相反,你应该尝试:

while (nextFrame >= frameInterval)
{
    //Blah blah

    nextFrame -= frameInterval;
}

因此nextFrame会有一些时间“遗留”,所以下一个动画会在正确的时间出现。

但是,由于你的问题不是很清楚,我不确定这是否能真正解决你的问题。