自研游戏引擎动画系统

主要参考Unity引擎的动画系统的实现,但是Unity引擎的动画系统是一个复杂的系统,涉及到多个组件和类的协作。但我们可以用C#来实现其基本的动画系统逻辑。以下是一个简化的示例,展示了如何用C#实现一个基本的动画系统,包括骨骼动画、帧动画和Tween动画的基本逻辑。

1. 基本结构

我们将创建几个类来自定义引擎的动画系统:

  • AnimationClip:表示一个动画剪辑。
  • SkeletonAnimation:表示骨骼动画。
  • FrameAnimation:表示帧动画。
  • Tween:表示插值动画。
  • AnimationEvent:表示动画事件。

2. 代码实现

以下是一个简单的C#代码示例,自定义引擎的动画系统:

using System;
using System.Collections.Generic;

public class AnimationEvent
{
    public string Name { get; set; }
    public float Time { get; set; }
    public Action Callback { get; set; }

    public AnimationEvent(string name, float time, Action callback)
    {
        Name = name;
        Time = time;
        Callback = callback;
    }
}

public class AnimationClip
{
    public List<KeyFrame> KeyFrames { get; private set; } = new List<KeyFrame>();
    public List<AnimationEvent> Events { get; private set; } = new List<AnimationEvent>();

    public void AddKeyFrame(float time, float value)
    {
        KeyFrames.Add(new KeyFrame(time, value));
    }

    public void AddEvent(string name, float time, Action callback)
    {
        Events.Add(new AnimationEvent(name, time, callback));
    }
}

public class KeyFrame
{
    public float Time { get; set; }
    public float Value { get; set; }

    public KeyFrame(float time, float value)
    {
        Time = time;
        Value = value;
    }
}

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private float currentTime;

    public void Play(AnimationClip clip)
    {
        currentClip = clip;
        currentTime = 0f;
    }

    public void Update(float deltaTime)
    {
        if (currentClip == null) return;

        currentTime += deltaTime;

        // 处理关键帧插值
        for (int i = 0; i < currentClip.KeyFrames.Count - 1; i++)
        {
            var startFrame = currentClip.KeyFrames[i];
            var endFrame = currentClip.KeyFrames[i + 1];

            if (currentTime >= startFrame.Time && currentTime < endFrame.Time)
            {
                float t = (currentTime - startFrame.Time) / (endFrame.Time - startFrame.Time);
                float value = Mathf.Lerp(startFrame.Value, endFrame.Value, t);
                Console.WriteLine($"Current Value: {value}");

                // 检查并触发事件
                foreach (var animEvent in currentClip.Events)
                {
                    if (animEvent.Time >= startFrame.Time && animEvent.Time <= endFrame.Time)
                    {
                        animEvent.Callback.Invoke();
                    }
                }
                break;
            }
        }
    }
}

public class FrameAnimation
{
    private List<string> frames;
    private int currentFrame;
    private float frameDuration;
    private float elapsedTime;

    public FrameAnimation(List<string> frames, float frameDuration)
    {
        this.frames = frames;
        this.frameDuration = frameDuration;
        currentFrame = 0;
    }

    public void Update(float deltaTime)
    {
        elapsedTime += deltaTime;
        if (elapsedTime >= frameDuration)
        {
            currentFrame = (currentFrame + 1) % frames.Count;
            Console.WriteLine($"Current Frame: {frames[currentFrame]}");
            elapsedTime = 0f;
        }
    }
}

public static class Mathf
{
    public static float Lerp(float a, float b, float t)
    {
        return a + (b - a) * t;
    }
}

// 示例用法
public class Program
{
    public static void Main(string[] args)
    {
        // 创建一个骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip = new AnimationClip();
        animationClip.AddKeyFrame(0f, 0f);
        animationClip.AddKeyFrame(1f, 1f);
        animationClip.AddEvent("OnComplete", 1f, () => Console.WriteLine("Animation Complete!"));
        skeletonAnimation.Play(animationClip);

        // 更新动画
        for (float time = 0; time <= 1.5f; time += 0.1f)
        {
            skeletonAnimation.Update(0.1f);
        }

        // 创建一个帧动画
        var frameAnimation = new FrameAnimation(new List<string> { "frame1", "frame2", "frame3" }, 0.5f);
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            frameAnimation.Update(0.1f);
        }
    }
}

3. 代码说明

  • AnimationEvent:表示动画事件,包含事件名称、触发时间和回调函数。
  • AnimationClip:表示动画剪辑,包含关键帧和事件的列表。
  • KeyFrame:表示单个关键帧,包含时间和对应的值。
  • SkeletonAnimation:表示骨骼动画,负责播放动画剪辑并更新当前时间,处理关键帧插值和事件触发。
  • FrameAnimation:表示帧动画,负责逐帧播放动画。
  • Mathf:提供简单的数学函数,例如线性插值。

4. 总结

以上代码是自定义引擎动画系统的一个简化实现,展示了如何使用C#实现基本的动画逻辑。实际的商业引擎动画系统要复杂得多,涉及更多的功能和优化,但这个示例可以帮助你理解动画系统的基本工作原理。

接下来我们可以进一步扩展之前的C#动画系统模拟,增加一些功能和细节,以更好地实现自研引擎的动画系统。我们可以考虑以下几个方面:

  1. 动画混合:实现多个动画的混合播放。
  2. 动画状态机:管理不同的动画状态和切换。
  3. Tween 动画:实现更复杂的插值动画。
  4. 事件回调:支持在动画播放过程中触发不同的事件。

1. 动画混合

我们可以在 SkeletonAnimation 类中添加一个方法来支持动画混合。这里我们将实现一个简单的线性混合。

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private AnimationClip nextClip;
    private float currentTime;
    private float blendTime;

    public void Play(AnimationClip clip, float blendTime = 0f)
    {
        if (currentClip != null)
        {
            nextClip = clip;
            this.blendTime = blendTime;
        }
        else
        {
            currentClip = clip;
            currentTime = 0f;
        }
    }

    public void Update(float deltaTime)
    {
        if (currentClip == null) return;

        currentTime += deltaTime;

        // 处理关键帧插值
        for (int i = 0; i < currentClip.KeyFrames.Count - 1; i++)
        {
            var startFrame = currentClip.KeyFrames[i];
            var endFrame = currentClip.KeyFrames[i + 1];

            if (currentTime >= startFrame.Time && currentTime < endFrame.Time)
            {
                float t = (currentTime - startFrame.Time) / (endFrame.Time - startFrame.Time);
                float value = Mathf.Lerp(startFrame.Value, endFrame.Value, t);
                Console.WriteLine($"Current Value: {value}");

                // 检查并触发事件
                foreach (var animEvent in currentClip.Events)
                {
                    if (animEvent.Time >= startFrame.Time && animEvent.Time <= endFrame.Time)
                    {
                        animEvent.Callback.Invoke();
                    }
                }
                break;
            }
        }

        // 混合动画
        if (nextClip != null)
        {
            float blendFactor = Mathf.Clamp01(currentTime / blendTime);
            // 这里可以实现混合逻辑
            // 例如,使用当前动画和下一个动画的关键帧进行插值
            // 这部分可以根据具体需求进行扩展
        }
    }
}

2. 动画状态机

我们可以创建一个简单的动画状态机来管理不同的动画状态和切换。

public class AnimationStateMachine
{
    private Dictionary<string, AnimationClip> animations = new Dictionary<string, AnimationClip>();
    private SkeletonAnimation skeletonAnimation;
    private string currentState;

    public AnimationStateMachine(SkeletonAnimation skeletonAnimation)
    {
        this.skeletonAnimation = skeletonAnimation;
    }

    public void AddAnimation(string state, AnimationClip clip)
    {
        animations[state] = clip;
    }

    public void Play(string state)
    {
        if (currentState != state)
        {
            currentState = state;
            skeletonAnimation.Play(animations[state]);
        }
    }

    public void Update(float deltaTime)
    {
        skeletonAnimation.Update(deltaTime);
    }
}

3. Tween 动画

我们可以实现一个简单的 Tween 动画类,支持不同的插值方式。

public class Tween
{
    public static void To(object target, float duration, Action<float> onUpdate, Action onComplete = null)
    {
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += 0.1f; // 假设每次调用更新0.1秒
            float t = Mathf.Clamp01(elapsed / duration);
            onUpdate(t);

            // 这里可以添加延迟逻辑
        }

        onComplete?.Invoke();
    }
}

4. 事件回调

在动画中添加事件回调的逻辑已经在之前的代码中实现。可以在关键帧中添加事件,并在播放时触发。

5. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip1 = new AnimationClip();
        animationClip1.AddKeyFrame(0f, 0f);
        animationClip1.AddKeyFrame(1f, 1f);
        animationClip1.AddEvent("OnComplete", 1f, () => Console.WriteLine("Animation 1 Complete!"));

        var animationClip2 = new AnimationClip();
        animationClip2.AddKeyFrame(0f, 1f);
        animationClip2.AddKeyFrame(1f, 0f);
        animationClip2.AddEvent("OnComplete", 1f, () => Console.WriteLine("Animation 2 Complete!"));

        var stateMachine = new AnimationStateMachine(skeletonAnimation);
        stateMachine.AddAnimation("Idle", animationClip1);
        stateMachine.AddAnimation("Run", animationClip2);

        // 播放动画
        stateMachine.Play("Idle");

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }

        // 切换到另一个动画
        stateMachine.Play("Run");

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }
    }
}

6. 总结

通过以上扩展,我们实现了一个更为复杂的动画系统,支持动画混合、状态机管理、Tween 动画等功能。这些功能使得动画系统更加灵活和强大,能够满足更复杂的游戏需求。

我们可以继续扩展这个动画系统,增加更多的功能和细节。以下是一些可以考虑的扩展:

  1. 支持不同的插值方式:实现不同的插值方法(如缓动、弹性等)。
  2. 动画层:支持多个动画层的叠加。
  3. 动画速度控制:允许动态调整动画播放速度。
  4. 事件系统:实现更复杂的事件系统,支持事件监听和广播。
  5. 动画回调:在动画开始、结束和中断时提供回调。

1. 支持不同的插值方式

我们可以扩展 Mathf 类,添加不同的插值方法,例如线性插值、缓动插值等。

public static class Mathf
{
    public static float Lerp(float a, float b, float t)
    {
        return a + (b - a) * t;
    }

    public static float EaseInOut(float t)
    {
        return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }

    public static float Bounce(float t)
    {
        if (t < (1 / 2.75f))
        {
            return 7.5625f * t * t;
        }
        else if (t < (2 / 2.75f))
        {
            t -= (1.5f / 2.75f);
            return 7.5625f * t * t + .75f;
        }
        else if (t < (2.5 / 2.75))
        {
            t -= (2.25f / 2.75f);
            return 7.5625f * t * t + .9375f;
        }
        else
        {
            t -= (2.625f / 2.75f);
            return 7.5625f * t * t + .984375f;
        }
    }
}

2. 动画层

我们可以实现一个简单的动画层系统,允许多个动画同时播放并叠加。

public class AnimationLayer
{
    public string Name { get; private set; }
    public SkeletonAnimation Animation { get; private set; }
    public float Weight { get; set; }

    public AnimationLayer(string name, SkeletonAnimation animation, float weight = 1f)
    {
        Name = name;
        Animation = animation;
        Weight = weight;
    }

    public void Update(float deltaTime)
    {
        Animation.Update(deltaTime);
    }
}

public class AnimationController
{
    private List<AnimationLayer> layers = new List<AnimationLayer>();

    public void AddLayer(AnimationLayer layer)
    {
        layers.Add(layer);
    }

    public void Update(float deltaTime)
    {
        foreach (var layer in layers)
        {
            layer.Update(deltaTime);
        }
    }
}

3. 动画速度控制

我们可以在 SkeletonAnimation 类中添加一个属性来控制动画的播放速度。

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private float currentTime;
    private float speed = 1f; // 动画速度

    public float Speed
    {
        get => speed;
        set => speed = Math.Max(0f, value); // 确保速度不为负
    }

    public void Update(float deltaTime)
    {
        if (currentClip == null) return;

        currentTime += deltaTime * speed; // 根据速度调整时间

        // 处理关键帧插值
        // ...(与之前相同的逻辑)
    }
}

4. 事件系统

我们可以实现一个简单的事件系统,支持事件的监听和广播。

public class EventManager
{
    private Dictionary<string, Action> eventDictionary = new Dictionary<string, Action>();

    public void Subscribe(string eventName, Action listener)
    {
        if (!eventDictionary.ContainsKey(eventName))
        {
            eventDictionary[eventName] = null;
        }
        eventDictionary[eventName] += listener;
    }

    public void Unsubscribe(string eventName, Action listener)
    {
        if (eventDictionary.ContainsKey(eventName))
        {
            eventDictionary[eventName] -= listener;
        }
    }

    public void Publish(string eventName)
    {
        if (eventDictionary.ContainsKey(eventName))
        {
            eventDictionary[eventName]?.Invoke();
        }
    }
}

5. 动画回调

我们可以在 AnimationClip 中添加回调,以便在动画开始、结束和中断时触发。

public class AnimationClip
{
    public List<KeyFrame> KeyFrames { get; private set; } = new List<KeyFrame>();
    public List<AnimationEvent> Events { get; private set; } = new List<AnimationEvent>();
    public Action OnStart { get; set; }
    public Action OnEnd { get; set; }

    public void Play()
    {
        OnStart?.Invoke();
    }

    public void Complete()
    {
        OnEnd?.Invoke();
    }
}

6. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建事件管理器
        var eventManager = new EventManager();

        // 创建骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip1 = new AnimationClip();
        animationClip1.AddKeyFrame(0f, 0f);
        animationClip1.AddKeyFrame(1f, 1f);
        animationClip1.OnStart = () => Console.WriteLine("Animation 1 Started!");
        animationClip1.OnEnd = () => Console.WriteLine("Animation 1 Ended!");

        var animationClip2 = new AnimationClip();
        animationClip2.AddKeyFrame(0f, 1f);
        animationClip2.AddKeyFrame(1f, 0f);
        animationClip2.OnStart = () => Console.WriteLine("Animation 2 Started!");
        animationClip2.OnEnd = () => Console.WriteLine("Animation 2 Ended!");

        var stateMachine = new AnimationStateMachine(skeletonAnimation);
        stateMachine.AddAnimation("Idle", animationClip1);
        stateMachine.AddAnimation("Run", animationClip2);

        // 播放动画
        stateMachine.Play("Idle");

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }

        // 切换到另一个动画
        stateMachine.Play("Run");

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }
    }
}

7. 总结

通过以上扩展,我们实现了一个更为复杂的动画系统,支持不同的插值方式、动画层、动画速度控制、事件系统和动画回调。这些功能使得动画系统更加灵活和强大,能够满足更复杂的游戏需求。

我们可以继续扩展这个动画系统,增加更多的功能和细节。以下是一些可以考虑的扩展:

  1. 动画循环:支持动画的循环播放。
  2. 动画反向播放:支持动画的反向播放。
  3. 混合动画的权重控制:允许动态调整混合动画的权重。
  4. 动画过渡效果:实现动画之间的过渡效果。
  5. 状态机的条件切换:在状态机中添加条件切换功能。

1. 动画循环

我们可以在 SkeletonAnimation 类中添加一个属性来控制动画是否循环播放。

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private float currentTime;
    private float speed = 1f; // 动画速度
    private bool loop = false; // 是否循环播放

    public bool Loop
    {
        get => loop;
        set => loop = value;
    }

    public void Update(float deltaTime)
    {
        if (currentClip == null) return;

        currentTime += deltaTime * speed; // 根据速度调整时间

        if (loop)
        {
            currentTime %= currentClip.Duration; // 循环播放
        }

        // 处理关键帧插值
        // ...(与之前相同的逻辑)
    }
}

2. 动画反向播放

我们可以在 SkeletonAnimation 类中添加一个属性来控制动画的播放方向。

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private float currentTime;
    private float speed = 1f; // 动画速度
    private bool loop = false; // 是否循环播放
    private bool reverse = false; // 是否反向播放

    public bool Reverse
    {
        get => reverse;
        set => reverse = value;
    }

    public void Update(float deltaTime)
    {
        if (currentClip == null) return;

        currentTime += (reverse ? -1 : 1) * deltaTime * speed; // 根据方向调整时间

        if (loop)
        {
            currentTime = (currentTime + currentClip.Duration) % currentClip.Duration; // 循环播放
        }

        // 处理关键帧插值
        // ...(与之前相同的逻辑)
    }
}

3. 混合动画的权重控制

我们可以在 AnimationLayer 中添加一个方法来动态调整混合动画的权重。

public class AnimationLayer
{
    public string Name { get; private set; }
    public SkeletonAnimation Animation { get; private set; }
    public float Weight { get; set; }

    public AnimationLayer(string name, SkeletonAnimation animation, float weight = 1f)
    {
        Name = name;
        Animation = animation;
        Weight = weight;
    }

    public void Update(float deltaTime)
    {
        Animation.Update(deltaTime);
    }

    public void SetWeight(float weight)
    {
        Weight = Mathf.Clamp01(weight); // 确保权重在0到1之间
    }
}

4. 动画过渡效果

我们可以在 AnimationController 中实现动画之间的过渡效果。

public class AnimationController
{
    private List<AnimationLayer> layers = new List<AnimationLayer>();
    private float transitionDuration = 0.5f; // 过渡持续时间
    private float transitionTime = 0f; // 当前过渡时间

    public void AddLayer(AnimationLayer layer)
    {
        layers.Add(layer);
    }

    public void TransitionTo(string newState)
    {
        // 这里可以实现从当前状态到新状态的过渡逻辑
        transitionTime = 0f; // 重置过渡时间
        // 其他逻辑...
    }

    public void Update(float deltaTime)
    {
        transitionTime += deltaTime;
        float t = Mathf.Clamp01(transitionTime / transitionDuration);

        foreach (var layer in layers)
        {
            layer.Update(deltaTime);
            // 这里可以根据权重和过渡时间调整动画的混合
        }
    }
}

5. 状态机的条件切换

我们可以在 AnimationStateMachine 中添加条件切换功能,以便根据特定条件切换动画状态。

public class AnimationStateMachine
{
    private Dictionary<string, AnimationClip> animations = new Dictionary<string, AnimationClip>();
    private SkeletonAnimation skeletonAnimation;
    private string currentState;

    public AnimationStateMachine(SkeletonAnimation skeletonAnimation)
    {
        this.skeletonAnimation = skeletonAnimation;
    }

    public void AddAnimation(string state, AnimationClip clip)
    {
        animations[state] = clip;
    }

    public void Play(string state)
    {
        if (currentState != state)
        {
            currentState = state;
            skeletonAnimation.Play(animations[state]);
        }
    }

    public void Update(float deltaTime)
    {
        skeletonAnimation.Update(deltaTime);
        // 这里可以添加条件切换逻辑
    }
}

6. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip1 = new AnimationClip();
        animationClip1.AddKeyFrame(0f, 0f);
        animationClip1.AddKeyFrame(1f, 1f);
        animationClip1.Loop = true; // 设置为循环播放

        var animationClip2 = new AnimationClip();
        animationClip2.AddKeyFrame(0f, 1f);
        animationClip2.AddKeyFrame(1f, 0f);
        animationClip2.Reverse = true; // 设置为反向播放

        var stateMachine = new AnimationStateMachine(skeletonAnimation);
        stateMachine.AddAnimation("Idle", animationClip1);
        stateMachine.AddAnimation("Run", animationClip2);

        // 播放动画
        stateMachine.Play("Idle");

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }

        // 切换到另一个动画
        stateMachine.Play("Run");

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }
    }
}

7. 总结

通过以上扩展,我们实现了一个更为复杂的动画系统,支持动画循环、反向播放、混合动画的权重控制、动画过渡效果和状态机的条件切换。这些功能使得动画系统更加灵活和强大,能够满足更复杂的游戏需求。

我们可以继续扩展这个动画系统,增加更多的功能和细节。以下是一些可以考虑的扩展:

  1. 动画事件:在特定时间点触发事件。
  2. 动画混合:支持不同动画之间的平滑过渡。
  3. 状态机的条件切换:根据条件自动切换动画状态。
  4. 动画参数:允许通过参数控制动画状态。
  5. 动画的暂停与恢复:支持动画的暂停和恢复功能。

1. 动画事件

我们可以在 AnimationClip 中添加事件系统,以便在特定时间点触发事件。

public class AnimationEvent
{
    public float Time { get; set; }
    public Action Callback { get; set; }

    public AnimationEvent(float time, Action callback)
    {
        Time = time;
        Callback = callback;
    }
}

public class AnimationClip
{
    public List<KeyFrame> KeyFrames { get; private set; } = new List<KeyFrame>();
    public List<AnimationEvent> Events { get; private set; } = new List<AnimationEvent>();
    public Action OnStart { get; set; }
    public Action OnEnd { get; set; }

    public void AddEvent(float time, Action callback)
    {
        Events.Add(new AnimationEvent(time, callback));
    }

    public void Play()
    {
        OnStart?.Invoke();
    }

    public void Complete()
    {
        OnEnd?.Invoke();
    }

    public void Update(float currentTime)
    {
        foreach (var animationEvent in Events)
        {
            if (Math.Abs(currentTime - animationEvent.Time) < 0.01f)
            {
                animationEvent.Callback?.Invoke();
            }
        }
    }
}

2. 动画混合

我们可以在 AnimationController 中实现动画混合的功能,以便在不同动画之间进行平滑过渡。

public class AnimationController
{
    private List<AnimationLayer> layers = new List<AnimationLayer>();
    private float transitionDuration = 0.5f; // 过渡持续时间
    private float transitionTime = 0f; // 当前过渡时间
    private string currentState;

    public void AddLayer(AnimationLayer layer)
    {
        layers.Add(layer);
    }

    public void TransitionTo(string newState)
    {
        if (currentState != newState)
        {
            transitionTime = 0f; // 重置过渡时间
            currentState = newState;
            // 其他逻辑...
        }
    }

    public void Update(float deltaTime)
    {
        transitionTime += deltaTime;
        float t = Mathf.Clamp01(transitionTime / transitionDuration);

        foreach (var layer in layers)
        {
            layer.Update(deltaTime);
            // 这里可以根据权重和过渡时间调整动画的混合
        }
    }
}

3. 状态机的条件切换

我们可以在 AnimationStateMachine 中添加条件切换功能,以便根据特定条件切换动画状态。

public class AnimationStateMachine
{
    private Dictionary<string, AnimationClip> animations = new Dictionary<string, AnimationClip>();
    private SkeletonAnimation skeletonAnimation;
    private string currentState;

    public AnimationStateMachine(SkeletonAnimation skeletonAnimation)
    {
        this.skeletonAnimation = skeletonAnimation;
    }

    public void AddAnimation(string state, AnimationClip clip)
    {
        animations[state] = clip;
    }

    public void Play(string state)
    {
        if (currentState != state)
        {
            currentState = state;
            skeletonAnimation.Play(animations[state]);
        }
    }

    public void Update(float deltaTime)
    {
        skeletonAnimation.Update(deltaTime);
        // 这里可以添加条件切换逻辑
    }
}

4. 动画参数

我们可以在 AnimationStateMachine 中添加参数,以便通过参数控制动画状态。

public class AnimationStateMachine
{
    private Dictionary<string, AnimationClip> animations = new Dictionary<string, AnimationClip>();
    private SkeletonAnimation skeletonAnimation;
    private string currentState;
    private Dictionary<string, float> parameters = new Dictionary<string, float>();

    public AnimationStateMachine(SkeletonAnimation skeletonAnimation)
    {
        this.skeletonAnimation = skeletonAnimation;
    }

    public void AddAnimation(string state, AnimationClip clip)
    {
        animations[state] = clip;
    }

    public void SetParameter(string name, float value)
    {
        parameters[name] = value;
    }

    public void Update(float deltaTime)
    {
        // 根据参数决定状态切换
        if (parameters.TryGetValue("Speed", out float speed) && speed > 0)
        {
            Play("Run");
        }
        else
        {
            Play("Idle");
        }

        skeletonAnimation.Update(deltaTime);
    }
}

5. 动画的暂停与恢复

我们可以在 SkeletonAnimation 中添加暂停和恢复的功能。

public class SkeletonAnimation
{
    private AnimationClip currentClip;
    private float currentTime;
    private float speed = 1f; // 动画速度
    private bool isPaused = false; // 是否暂停

    public void Pause()
    {
        isPaused = true;
    }

    public void Resume()
    {
        isPaused = false;
    }

    public void Update(float deltaTime)
    {
        if (isPaused || currentClip == null) return;

        currentTime += deltaTime * speed; // 根据速度调整时间

        // 处理关键帧插值
        // ...(与之前相同的逻辑)
    }
}

6. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip1 = new AnimationClip();
        animationClip1.AddKeyFrame(0f, 0f);
        animationClip1.AddKeyFrame(1f, 1f);
        animationClip1.AddEvent(0.5f, () => Console.WriteLine("Halfway through Animation 1!"));
        animationClip1.OnStart = () => Console.WriteLine("Animation 1 Started!");
        animationClip1.OnEnd = () => Console.WriteLine("Animation 1 Ended!");

        var animationClip2 = new AnimationClip();
        animationClip2.AddKeyFrame(0f, 1f);
        animationClip2.AddKeyFrame(1f, 0f);
        animationClip2.OnStart = () => Console.WriteLine("Animation 2 Started!");
        animationClip2.OnEnd = () => Console.WriteLine("Animation 2 Ended!");

        var stateMachine = new AnimationStateMachine(skeletonAnimation);
        stateMachine.AddAnimation("Idle", animationClip1);
        stateMachine.AddAnimation("Run", animationClip2);

        // 设置参数
        stateMachine.SetParameter("Speed", 0); // Idle状态

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }

        // 切换到另一个动画
        stateMachine.SetParameter("Speed", 1); // Run状态
        stateMachine.Update(0.1f);

        // 暂停动画
        skeletonAnimation.Pause();
        Console.WriteLine("Animation Paused");

        // 恢复动画
        skeletonAnimation.Resume();
        Console.WriteLine("Animation Resumed");

        // 更新动画
        for (float time = 0; time <= 2f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
        }
    }
}

7. 总结

通过以上扩展,我们实现了一个更为复杂的动画系统,支持动画事件、动画混合、状态机的条件切换、动画参数控制以及动画的暂停与恢复。这些功能使得动画系统更加灵活和强大,能够满足更复杂的游戏需求。

我们可以继续深入探讨动画系统的更多功能和实现细节。以下是一些可以进一步扩展的方面:

8. 动画层(Animation Layers)

动画层允许我们在同一角色上叠加多个动画。例如,角色可以同时进行行走和挥手的动画。我们可以为每个动画层设置权重,以控制它们的影响程度。

public class AnimationLayer
{
    public string Name { get; private set; }
    public AnimationClip Clip { get; private set; }
    public float Weight { get; set; } = 1.0f; // 默认权重为1.0
    public bool IsActive { get; set; } = true;

    public AnimationLayer(string name, AnimationClip clip)
    {
        Name = name;
        Clip = clip;
    }

    public void Update(float deltaTime)
    {
        if (IsActive)
        {
            Clip.Update(deltaTime);
        }
    }
}

9. 混合动画(Blend Animations)

我们可以实现一个简单的混合动画功能,以便在多个动画层之间进行平滑过渡。

public class AnimationController
{
    private List<AnimationLayer> layers = new List<AnimationLayer>();
    private float transitionDuration = 0.5f;
    private float transitionTime = 0f;
    private string currentState;

    public void AddLayer(AnimationLayer layer)
    {
        layers.Add(layer);
    }

    public void Update(float deltaTime)
    {
        transitionTime += deltaTime;
        float t = Mathf.Clamp01(transitionTime / transitionDuration);

        foreach (var layer in layers)
        {
            layer.Update(deltaTime);
            // 这里可以根据权重和过渡时间调整动画的混合
            if (layer.IsActive)
            {
                // 处理混合逻辑
                // 例如:根据权重调整当前动画的影响
            }
        }
    }
}

10. 动画反向播放

我们可以为动画添加反向播放的功能,以便在需要时可以反向播放动画。

public class AnimationClip
{
    public List<KeyFrame> KeyFrames { get; private set; } = new List<KeyFrame>();
    public bool IsPlaying { get; private set; } = false;
    public bool IsReversed { get; set; } = false; // 是否反向播放

    public void Play()
    {
        IsPlaying = true;
    }

    public void Stop()
    {
        IsPlaying = false;
    }

    public void Update(float deltaTime)
    {
        if (!IsPlaying) return;

        // 根据播放方向更新当前时间
        if (IsReversed)
        {
            // 反向播放逻辑
        }
        else
        {
            // 正向播放逻辑
        }
    }
}

11. 动画循环与重复

我们可以为动画添加循环和重复的功能,以便在需要时可以重复播放动画。

public class AnimationClip
{
    public bool Loop { get; set; } = false; // 是否循环播放
    private float currentTime;

    public void Update(float deltaTime)
    {
        if (!IsPlaying) return;

        currentTime += deltaTime;

        if (Loop)
        {
            // 如果循环,重置时间
            if (currentTime >= GetClipLength())
            {
                currentTime = 0f;
            }
        }
        else
        {
            // 非循环播放逻辑
        }
    }

    private float GetClipLength()
    {
        // 返回动画片段的总时长
        return KeyFrames.Last().Time; // 假设最后一个关键帧的时间为动画时长
    }
}

12. 动画过渡(Transitions)

我们可以实现动画过渡的功能,以便在不同动画之间进行平滑过渡。

public class AnimationTransition
{
    public string FromState { get; private set; }
    public string ToState { get; private set; }
    public float Duration { get; private set; }

    public AnimationTransition(string fromState, string toState, float duration)
    {
        FromState = fromState;
        ToState = toState;
        Duration = duration;
    }
}

public class AnimationStateMachine
{
    private List<AnimationTransition> transitions = new List<AnimationTransition>();

    public void AddTransition(string fromState, string toState, float duration)
    {
        transitions.Add(new AnimationTransition(fromState, toState, duration));
    }

    public void Update(float deltaTime)
    {
        // 根据条件检查是否需要进行状态切换
        foreach (var transition in transitions)
        {
            // 检查条件并执行过渡
        }
    }
}

13. 事件系统

我们可以实现一个事件系统,以便在动画状态变化时触发事件。

public class AnimationEventSystem
{
    private Dictionary<string, Action> eventCallbacks = new Dictionary<string, Action>();

    public void RegisterEvent(string eventName, Action callback)
    {
        eventCallbacks[eventName] = callback;
    }

    public void TriggerEvent(string eventName)
    {
        if (eventCallbacks.TryGetValue(eventName, out var callback))
        {
            callback?.Invoke();
        }
    }
}

14. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建骨骼动画
        var skeletonAnimation = new SkeletonAnimation();
        var animationClip1 = new AnimationClip();
        animationClip1.AddKeyFrame(0f, 0f);
        animationClip1.AddKeyFrame(1f, 1f);
        animationClip1.Loop = true; // 设置为循环播放

        var animationClip2 = new AnimationClip();
        animationClip2.AddKeyFrame(0f, 1f);
        animationClip2.AddKeyFrame(1f, 0f);

        var layer1 = new AnimationLayer("BaseLayer", animationClip1);
        var layer2 = new AnimationLayer("UpperBodyLayer", animationClip2);

        var controller = new AnimationController();
        controller.AddLayer(layer1);
        controller.AddLayer(layer2);

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            controller.Update(0.1f);
        }

        // 切换动画状态
        layer1.IsActive = false; // 停止基础层动画
        layer2.IsActive = true; // 启动上半身动画
        controller.Update(0.1f);
    }
}

15. 总结

通过以上扩展,我们实现了一个更为复杂和灵活的动画系统,支持动画层、混合动画、反向播放、循环与重复、动画过渡以及事件系统。这些功能使得动画系统能够更好地适应各种游戏需求,提供更丰富的动画表现。

我们可以继续深入探讨动画系统的其他方面,特别是一些高级功能和优化策略。以下是一些可以进一步扩展的主题:

16. 动画事件(Animation Events)

动画事件允许我们在特定的时间点触发自定义事件。这对于在动画播放过程中执行特定操作(如发出声音、生成粒子效果等)非常有用。

public class AnimationEvent
{
    public float Time { get; private set; }
    public Action Callback { get; private set; }

    public AnimationEvent(float time, Action callback)
    {
        Time = time;
        Callback = callback;
    }
}

public class AnimationClip
{
    public List<AnimationEvent> Events { get; private set; } = new  List<AnimationEvent>();
    private float currentTime;

    public void AddEvent(float time, Action callback)
    {
        Events.Add(new AnimationEvent(time, callback));
    }

    public void Update(float deltaTime)
    {
        if (!IsPlaying) return;

        currentTime += deltaTime;

        // 检查并触发事件
        foreach (var animationEvent in Events)
        {
            if (currentTime >= animationEvent.Time)
            {
                animationEvent.Callback.Invoke();
                Events.Remove(animationEvent); // 触发后移除事件
                break; // 退出循环以避免修改集合
            }
        }
    }
}

17. 动画混合树(Animation Blend Trees)

混合树允许我们根据输入(如角色速度、方向等)动态选择和混合多个动画。这对于实现自然的角色运动非常重要。

public class BlendTree
{
    private List<AnimationClip> clips = new List<AnimationClip>();
    private List<float> weights = new List<float>();

    public void AddClip(AnimationClip clip, float weight)
    {
        clips.Add(clip);
        weights.Add(weight);
    }

    public void Update(float deltaTime)
    {
        float totalWeight = weights.Sum();
        foreach (var clip in clips)
        {
            float blendWeight = weights[clips.IndexOf(clip)] / totalWeight;
            clip.Update(deltaTime * blendWeight); // 根据权重更新动画
        }
    }
}

18. 状态机(State Machine)

状态机可以帮助我们管理角色的不同动画状态(如行走、跑步、跳跃等),并在状态之间进行切换。

public class AnimationState
{
    public string Name { get; private set; }
    public AnimationClip Clip { get; private set; }

    public AnimationState(string name, AnimationClip clip)
    {
        Name = name;
        Clip = clip;
    }
}

public class AnimationStateMachine
{
    private Dictionary<string, AnimationState> states = new Dictionary<string, AnimationState>();
    private AnimationState currentState;

    public void AddState(AnimationState state)
    {
        states[state.Name] = state;
    }

    public void ChangeState(string newStateName)
    {
        if (states.TryGetValue(newStateName, out var newState))
        {
            currentState?.Clip.Stop(); // 停止当前动画
            currentState = newState;
            currentState.Clip.Play(); // 播放新动画
        }
    }

    public void Update(float deltaTime)
    {
        currentState?.Clip.Update(deltaTime);
    }
}

19. 动画压缩与优化

在实际应用中,动画数据可能会占用大量内存。我们可以实现动画压缩和优化技术,以减少内存占用并提高性能。

  • 关键帧压缩:只存储变化的关键帧,减少冗余数据。
  • 插值技术:使用插值算法(如线性插值、球面线性插值等)在关键帧之间平滑过渡。
  • LOD(Level of Detail):根据角色与摄像机的距离选择不同精度的动画。

20. 多线程与异步加载

为了提高性能,我们可以使用多线程或异步加载技术来处理动画数据的加载和计算。

public class AnimationLoader
{
    public async Task<AnimationClip> LoadAnimationAsync(string path)
    {
        return await Task.Run(() =>
        {
            // 模拟加载动画的过程
            Thread.Sleep(1000); // 假设加载需要1秒
            return new AnimationClip(); // 返回加载的动画
        });
    }
}

21. 调试与可视化

为动画系统添加调试和可视化工具,可以帮助开发者更好地理解动画的状态和行为。

  • 动画预览窗口:在编辑器中显示当前动画的预览。
  • 调试信息:输出当前动画状态、时间、权重等信息到控制台。

22. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建动画状态机
        var stateMachine = new AnimationStateMachine();

        // 创建动画片段
        var walkClip = new AnimationClip();
        walkClip.AddKeyFrame(0f, 0f);
        walkClip.AddKeyFrame(1f, 1f);
        walkClip.Loop = true;

        var runClip = new AnimationClip();
        runClip.AddKeyFrame(0f, 1f);
        runClip.AddKeyFrame(1f, 0f);
        runClip.Loop = true;

        // 添加状态
        stateMachine.AddState(new AnimationState("Walk", walkClip));
        stateMachine.AddState(new AnimationState("Run", runClip));

        // 切换状态
        stateMachine.ChangeState("Walk");

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
            if (time == 2f) // 2秒后切换到跑步状态
            {
                stateMachine.ChangeState("Run");
            }
        }
    }
}

23. 总结

通过以上扩展,我们实现了一个更为复杂和灵活的动画系统,支持动画事件、混合树、状态机、动画压缩与优化、多线程加载等功能。这些功能使得动画系统能够更好地适应各种游戏需求,提供更丰富的动画表现。

我们可以继续深入探讨动画系统的其他高级功能和实现细节。以下是一些可以进一步扩展的主题:

24. 动画混合与过渡

在复杂的动画系统中,动画混合和过渡是非常重要的功能。我们可以实现更复杂的混合逻辑,以便在不同动画之间进行平滑过渡。

24.1 混合权重

我们可以为每个动画层设置混合权重,以便在播放多个动画时控制它们的影响程度。

public class AnimationLayer
{
    public string Name { get; private set; }
    public AnimationClip Clip { get; private set; }
    public float Weight { get; set; } = 1.0f; // 默认权重为1.0
    public bool IsActive { get; set; } = true;

    public AnimationLayer(string name, AnimationClip clip)
    {
        Name = name;
        Clip = clip;
    }

    public void Update(float deltaTime)
    {
        if (IsActive)
        {
            Clip.Update(deltaTime * Weight); // 根据权重更新动画
        }
    }
}
24.2 动画过渡

我们可以实现动画过渡的功能,以便在不同动画之间进行平滑过渡。

public class AnimationTransition
{
    public string FromState { get; private set; }
    public string ToState { get; private set; }
    public float Duration { get; private set; }
    private float transitionTime;

    public AnimationTransition(string fromState, string toState, float duration)
    {
        FromState = fromState;
        ToState = toState;
        Duration = duration;
    }

    public void Update(float deltaTime)
    {
        transitionTime += deltaTime;
        float t = Mathf.Clamp01(transitionTime / Duration);
        // 这里可以实现混合逻辑
    }
}

25. 动画反向播放与暂停

我们可以为动画添加反向播放和暂停的功能,以便在需要时可以反向播放动画或暂停动画。

public class AnimationClip
{
    public bool IsPlaying { get; private set; } = false;
    public bool IsReversed { get; set; } = false; // 是否反向播放
    private float currentTime;

    public void Play()
    {
        IsPlaying = true;
    }

    public void Stop()
    {
        IsPlaying = false;
    }

    public void Pause()
    {
        IsPlaying = false; // 暂停播放
    }

    public void Resume()
    {
        IsPlaying = true; // 恢复播放
    }

    public void Update(float deltaTime)
    {
        if (!IsPlaying) return;

        // 根据播放方向更新当前时间
        if (IsReversed)
        {
            currentTime -= deltaTime;
            if (currentTime < 0) currentTime = 0; // 确保时间不小于0
        }
        else
        {
            currentTime += deltaTime;
            // 这里可以添加动画结束的逻辑
        }
    }
}

26. 动画层次结构

在复杂的角色动画中,可能需要实现动画的层次结构,例如角色的上半身和下半身可以独立动画。

public class CharacterAnimation
{
    private AnimationLayer upperBodyLayer;
    private AnimationLayer lowerBodyLayer;

    public CharacterAnimation(AnimationLayer upperBody, AnimationLayer lowerBody)
    {
        upperBodyLayer = upperBody;
        lowerBodyLayer = lowerBody;
    }

    public void Update(float deltaTime)
    {
        upperBodyLayer.Update(deltaTime);
        lowerBodyLayer.Update(deltaTime);
    }
}

27. 动画状态机的条件与触发器

在状态机中,我们可以添加条件和触发器,以便在特定条件下切换状态。

public class AnimationStateMachine
{
    private Dictionary<string, AnimationState> states = new Dictionary<string, AnimationState>();
    private AnimationState currentState;

    public void AddState(AnimationState state)
    {
        states[state.Name] = state;
    }

    public void ChangeState(string newStateName)
    {
        if (states.TryGetValue(newStateName, out var newState))
        {
            currentState?.Clip.Stop(); // 停止当前动画
            currentState = newState;
            currentState.Clip.Play(); // 播放新动画
        }
    }

    public void Update(float deltaTime)
    {
        currentState?.Clip.Update(deltaTime);
    }

    public void CheckTransitions()
    {
        // 检查条件并执行状态切换
        // 例如:如果角色速度超过某个阈值,则切换到跑步状态
    }
}

28. 动画的事件系统

我们可以实现一个事件系统,以便在动画状态变化时触发事件。

public class AnimationEventSystem
{
    private Dictionary<string, Action> eventCallbacks = new Dictionary<string, Action>();

    public void RegisterEvent(string eventName, Action callback)
    {
        eventCallbacks[eventName] = callback;
    }

    public void TriggerEvent(string eventName)
    {
        if (eventCallbacks.TryGetValue(eventName, out var callback))
        {
            callback?.Invoke();
        }
    }
}

29. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建动画状态机
        var stateMachine = new AnimationStateMachine();

        // 创建动画片段
        var walkClip = new AnimationClip();
        walkClip.AddKeyFrame(0f, 0f);
        walkClip.AddKeyFrame(1f, 1f);
        walkClip.Loop = true;

        var runClip = new AnimationClip();
        runClip.AddKeyFrame(0f, 1f);
        runClip.AddKeyFrame(1f, 0f);
        runClip.Loop = true;

        // 添加状态
        stateMachine.AddState(new AnimationState("Walk", walkClip));
        stateMachine.AddState(new AnimationState("Run", runClip));

        // 切换状态
        stateMachine.ChangeState("Walk");

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
            if (time == 2f) // 2秒后切换到跑步状态
            {
                stateMachine.ChangeState("Run");
            }
        }
    }
}

30. 总结

通过以上扩展,我们实现了一个更为复杂和灵活的动画系统,支持动画混合与过渡、反向播放与暂停、动画层次结构、状态机的条件与触发器、动画事件系统等功能。这些功能使得动画系统能够更好地适应各种游戏需求,提供更丰富的动画表现。

我们可以继续深入探讨动画系统的其他方面,特别是一些更高级的功能和实现细节。以下是一些可以进一步扩展的主题:

31. 动画的物理交互

在许多游戏中,角色的动画需要与物理系统进行交互。我们可以实现一个简单的物理交互系统,使得动画可以根据物理状态进行调整。

31.1 动画与刚体的结合

我们可以将动画与刚体结合,使得角色在播放动画时能够受到物理影响。

public class RigidBody
{
    public Vector3 Velocity { get; set; }
    public Vector3 Position { get; set; }

    public void ApplyForce(Vector3 force)
    {
        Velocity += force; // 简单的力学模型
    }

    public void Update(float deltaTime)
    {
        Position += Velocity * deltaTime; // 更新位置
    }
}

public class Character
{
    public RigidBody RigidBody { get; private set; }
    public CharacterAnimation Animation { get; private set; }

    public Character()
    {
        RigidBody = new RigidBody();
        Animation = new CharacterAnimation(new AnimationLayer("UpperBody", new AnimationClip()), new AnimationLayer("LowerBody", new AnimationClip()));
    }

    public void Update(float deltaTime)
    {
        RigidBody.Update(deltaTime);
        Animation.Update(deltaTime);
    }
}

32. 动画的反向运动学(IK)

反向运动学是一种用于计算关节位置的技术,以便在动画中实现更自然的运动。我们可以实现一个简单的IK系统。

32.1 基本的IK实现

我们可以创建一个简单的IK系统,使得角色的手或脚能够根据目标位置进行调整。

public class IKSystem
{
    public void SolveIK(Transform endEffector, Vector3 targetPosition)
    {
        // 简单的IK求解逻辑
        Vector3 direction = (targetPosition - endEffector.Position).normalized;
        endEffector.Position = targetPosition; // 将末端执行器移动到目标位置
    }
}

33. 动画的动态调整

在游戏中,角色的动画可能需要根据游戏状态进行动态调整。我们可以实现一个系统,根据角色的状态(如受伤、疲劳等)调整动画。

public class CharacterState
{
    public bool IsInjured { get; set; }
    public bool IsTired { get; set; }
}

public class AnimationAdjuster
{
    public void AdjustAnimation(CharacterState state, CharacterAnimation animation)
    {
        if (state.IsInjured)
        {
            animation.UpperBodyLayer.Weight = 0.5f; // 减少上半身动画的权重
        }
        else
        {
            animation.UpperBodyLayer.Weight = 1.0f; // 恢复权重
        }

        if (state.IsTired)
        {
            animation.LowerBodyLayer.Weight = 0.5f; // 减少下半身动画的权重
        }
        else
        {
            animation.LowerBodyLayer.Weight = 1.0f; // 恢复权重
        }
    }
}

34. 动画的声音同步

在动画播放时,可能需要同步播放声音效果。我们可以实现一个声音同步系统。

public class SoundManager
{
    public void PlaySound(string soundName)
    {
        // 播放声音的逻辑
        Console.WriteLine($"Playing sound: {soundName}");
    }
}

public class AnimationEventSystem
{
    private SoundManager soundManager;

    public AnimationEventSystem(SoundManager soundManager)
    {
        this.soundManager = soundManager;
    }

    public void TriggerSoundEvent(string eventName)
    {
        if (eventName == "Footstep")
        {
            soundManager.PlaySound("footstep.wav");
        }
    }
}

35. 动画的网络同步

在多人游戏中,动画需要在网络上进行同步。我们可以实现一个简单的网络同步系统。

public class NetworkManager
{
    public void SendAnimationState(string playerId, string animationState)
    {
        // 发送动画状态到服务器
        Console.WriteLine($"Sending animation state for {playerId}: {animationState}");
    }

    public void ReceiveAnimationState(string playerId, string animationState)
    {
        // 接收动画状态并更新本地动画
        Console.WriteLine($"Received animation state for {playerId}: {animationState}");
    }
}

36. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建角色
        var character = new Character();
        var animationAdjuster = new AnimationAdjuster();
        var soundManager = new SoundManager();
        var eventSystem = new AnimationEventSystem(soundManager);

        // 更新角色状态
        var state = new CharacterState { IsInjured = false, IsTired = true };
        animationAdjuster.AdjustAnimation(state, character.Animation);

        // 更新角色
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            character.Update(0.1f);
            if (time == 2f) // 2秒后触发脚步声事件
            {
                eventSystem.TriggerSoundEvent("Footstep");
            }
        }
    }
}

37. 总结

通过以上扩展,我们实现了一个更为复杂和灵活的动画系统,支持物理交互、反向运动学、动态调整、声音同步和网络同步等功能。这些功能使得动画系统能够更好地适应各种游戏需求,提供更丰富的动画表现。

我们可以继续深入探讨动画系统的其他方面,特别是一些更高级的功能和实现细节。以下是一些可以进一步扩展的主题:

38. 动画的状态机优化

在复杂的动画系统中,状态机的性能和可维护性是非常重要的。我们可以实现一些优化策略,以提高状态机的效率。

38.1 使用哈希表存储状态

使用哈希表(字典)来存储状态,可以提高状态查找的效率。

public class OptimizedAnimationStateMachine
{
    private Dictionary<string, AnimationState> states = new Dictionary<string, AnimationState>();
    private AnimationState currentState;

    public void AddState(AnimationState state)
    {
        states[state.Name] = state;
    }

    public void ChangeState(string newStateName)
    {
        if (states.TryGetValue(newStateName, out var newState))
        {
            if (currentState != newState)
            {
                currentState?.Clip.Stop(); // 停止当前动画
                currentState = newState;
                currentState.Clip.Play(); // 播放新动画
            }
        }
    }

    public void Update(float deltaTime)
    {
        currentState?.Clip.Update(deltaTime);
    }
}

39. 动画的事件系统增强

我们可以增强事件系统,使其支持参数化事件,以便在动画中传递更多信息。

39.1 参数化事件
public class AnimationEventSystem
{
    private Dictionary<string, Action<object[]>> eventCallbacks = new Dictionary<string, Action<object[]>>();

    public void RegisterEvent(string eventName, Action<object[]> callback)
    {
        eventCallbacks[eventName] = callback;
    }

    public void TriggerEvent(string eventName, params object[] parameters)
    {
        if (eventCallbacks.TryGetValue(eventName, out var callback))
        {
            callback?.Invoke(parameters);
        }
    }
}

40. 动画的层级与组合

在复杂的角色动画中,可能需要实现动画的层级与组合,以便更好地管理和复用动画。

40.1 动画组合

我们可以创建一个组合动画类,以便将多个动画组合在一起。

public class CompositeAnimation
{
    private List<AnimationClip> clips = new List<AnimationClip>();

    public void AddClip(AnimationClip clip)
    {
        clips.Add(clip);
    }

    public void Update(float deltaTime)
    {
        foreach (var clip in clips)
        {
            clip.Update(deltaTime);
        }
    }
}

41. 动画的时间缩放

在某些情况下,可能需要根据游戏状态动态调整动画的播放速度。我们可以实现一个时间缩放功能。

public class AnimationClip
{
    public float Speed { get; set; } = 1.0f; // 默认速度为1.0
    private float currentTime;

    public void Update(float deltaTime)
    {
        currentTime += deltaTime * Speed; // 根据速度更新当前时间
        // 这里可以添加动画结束的逻辑
    }
}

42. 动画的调试工具

在开发过程中,调试动画系统是非常重要的。我们可以实现一些调试工具,以便更好地监控动画状态。

42.1 动画状态监控
public class AnimationDebugger
{
    public void LogCurrentState(OptimizedAnimationStateMachine stateMachine)
    {
        Console.WriteLine($"Current Animation State: {stateMachine.CurrentState?.Name}");
    }

    public void LogAnimationProgress(AnimationClip clip)
    {
        Console.WriteLine($"Current Animation Time: {clip.CurrentTime}");
    }
}

43. 示例用法

以下是如何使用这些扩展的示例:

public class Program
{
    public static void Main(string[] args)
    {
        // 创建优化的动画状态机
        var stateMachine = new OptimizedAnimationStateMachine();

        // 创建动画片段
        var walkClip = new AnimationClip();
        walkClip.AddKeyFrame(0f, 0f);
        walkClip.AddKeyFrame(1f, 1f);
        walkClip.Loop = true;

        var runClip = new AnimationClip();
        runClip.AddKeyFrame(0f, 1f);
        runClip.AddKeyFrame(1f, 0f);
        runClip.Loop = true;

        // 添加状态
        stateMachine.AddState(new AnimationState("Walk", walkClip));
        stateMachine.AddState(new AnimationState("Run", runClip));

        // 切换状态
        stateMachine.ChangeState("Walk");

        // 创建调试工具
        var debugger = new AnimationDebugger();

        // 更新动画
        for (float time = 0; time <= 5f; time += 0.1f)
        {
            stateMachine.Update(0.1f);
            debugger.LogCurrentState(stateMachine);
            debugger.LogAnimationProgress(walkClip);

            if (time == 2f) // 2秒后切换到跑步状态
            {
                stateMachine.ChangeState("Run");
            }
        }
    }
}

44. 总结

通过以上扩展,我们实现了一个更为复杂和灵活的动画系统,支持状态机优化、事件系统增强、动画组合、时间缩放和调试工具等功能。这些功能使得动画系统能够更好地适应各种游戏需求,提供更丰富的动画表现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值