已经找到更好的方法来处理动画,这篇文章只是闭门造车的负面例子🐱‍🏍

大致步骤

动画播放的本质就是多张图片间的快速切换。每张图片都有单独的持续时间,即可根据播放进度选择一张图片显示出来。如果播放进度大于1(播放时间超过所有帧加起来的总时间)则从头来过。

1、创建 AnimationFrame

首先创建 AnimationFrame.cs 文件

一个 AnimationFrame 就是动画中的其中一帧。这一帧可以是一整张包含所有帧的图片中的某个位置(Rectangle),也可以是单独的一张贴图(Texture2D),此处我选择后者(因为素材绘画的原因)。

using Microsoft.Xna.Framework.Graphics;
using System;

namespace CatFishing_Windows.Models
{
    class AnimationFrame
    {
        //帧贴图
        public Texture2D FrameTexture { get; set; }

        //帧持续时间
        public TimeSpan Duration { get; set; }
    }
}

2、创建 Animation

创建 Animation.cs 文件, Animation 中主要应该包含一下几项:

1、包含该动画所有帧的 List<Animation>

public List<AnimationFrame> Frames = new List<AnimationFrame>();

2、记录该动画从播放到现在的时间 TimeIntoAnimation

TimeSpan TimeIntoAnimation;

3、枚举每一帧的持续时间,累加获得动画持续的总时间 Duration

TimeSpan Duration
{
    get
    {
        double totalSeconds = 0;
        foreach (var frame in Frames)
        {
            totalSeconds += frame.Duration.TotalSeconds;
        }

        return TimeSpan.FromSeconds(totalSeconds);
    }
}

4、向 Animation() 添加帧的方法

public void AddFrame(Texture2D frame, double duration)
{
    AnimationFrame animationFrame = new AnimationFrame()
    {
        FrameTexture = frame,
        Duration = TimeSpan.FromSeconds(duration)
    };
    Frames.Add(animationFrame);
}

其中, frame 为添加帧的贴图, duration 为该帧的预计持续时间。 duration 之所以为 double 是为了让 .Add() 在调用时看起来没有那么冗长,将 TimeSpan.FromSeconds() 在传入 .Add() 后进行。

5、计算 TimeIntoAnimation 的方法 Update() 。这个方法在game.cs内的 update() 处调用,每次 update() 时将 gameTime.ElapsedGameTime.TotalSeconds 累加进 TimeIntoAnimation 。

gameTime.ElapsedGameTime 代表这次 Update() 距离上次 Update() 所耗的时间。

public void Update(GameTime gameTime)
{
    double secondsIntoAnimation =
        TimeIntoAnimation.TotalSeconds + gameTime.ElapsedGameTime.TotalSeconds;

    //用已播放的时间%动画总时间
    //若 已播放时间大于总时间 则余数是多出来的秒数,记录余数进入下一次循环
    //若 已播放时间小于总时间 则余数为 已播放时间
    double remainder = secondsIntoAnimation % Duration.TotalSeconds;

    TimeIntoAnimation = TimeSpan.FromSeconds(remainder);
}

6、获取当前时间的对应帧。逐帧累加时间,即可获知当前 TimeIntoAnimation 应该对应的帧。

public Texture2D CurrentTexture
{
    get
    {
        AnimationFrame currentFrame = null;

        // 逐帧查找对应的帧
        TimeSpan accumulatedTime = new TimeSpan();
        foreach (var frame in Frames)
        {
            if (accumulatedTime + frame.Duration >= TimeIntoAnimation)
            {
                currentFrame = frame;
                break;
            }
            else accumulatedTime += frame.Duration;
        }

        //如果无法找到帧 则返回最后一帧
        if (currentFrame == null)
            currentFrame = Frames.LastOrDefault();

        return currentFrame.FrameTexture;
    }
}

3、简单使用

1、创建新的 Animation ,并添加帧。

Animation a1 = new Animation();
a1.AddFrame(Content.Load<Texture2D>("png0"), 0.5);
a1.AddFrame(Content.Load<Texture2D>("png1"), 0.5);
a1.AddFrame(Content.Load<Texture2D>("png2"), 0.5);

2、在 game.cs 中的 Update() 调用 Animation 的 Update() 事件

a1.Update(gameTime);

3、在 game.cs 中的 Draw() 绘制 Animation 的当前帧

spriteBatch.Draw(a1.CurrentTexture, new Vector2(0,0), Color.White);

发表评论

邮箱地址不会被公开。 必填项已用*标注