万镜之下 2026-02-15 15:28 采纳率: 6.3%
浏览 2

unity的移动问题



using UnityEngine;
 
public class IdleCharacterState : CharacterState
{
    public IdleCharacterState(Character character) : base(character)
    {
        MonoManager.Instance.AddUpdateEvent(Update);
        NewActions.Instance.Player.Move.Enable();
    }
    //进入状态
    public override void Enter()
    {
        Character.animtorSystem.PlayAnimation("idle");
    }
    //持续状态
    public override void Update()
    {
        if(NewActions.Instance.Player.Move.ReadValue<Vector2>()!=Vector2.zero)
        {
            Character.state=new MoveCharacterState(Character);
            MonoManager.Instance.RemoveUpdateEvent(Update);
        }
    }
    //退出状态
    public override void Exit()
    {
 
    }
}
 
 
using UnityEngine;
using Animancer;
using System.Collections.Generic;
using System;
 
 
[Serializable]
public class AnimtorSystem 
{
    private List<ClipTransition> animationClips;
    private AnimancerComponent animancerComponent;
    public AnimtorSystem(GameObject gameObject,List<ClipTransition> animationClips1)
    {
        animationClips=animationClips1;
        animancerComponent=gameObject.GetComponent<AnimancerComponent>(); 
    }
    //播放动画
    public void PlayAnimation(string animationName)
    {
        ClipTransition animationClip=animationClips.Find(x=>x.Name==animationName);
        if(animationClip!=null)
        {
            animancerComponent.Play(animationClip);
        }   
    }
}
 
 
using Unity.VisualScripting;
using UnityEngine;
using System.Collections.Generic;
using Animancer;
public abstract class Character:MonoBehaviour
{
    //属性系统
    public Attribute attribute;
    //状态系统
    public CharacterState state;
    //动画系统
    [SerializeField] public AnimtorSystem animtorSystem;
    public List<ClipTransition> animationClips=new List<ClipTransition>();
    public void Init()
    {
        attribute=new Attribute(100,100,100,1.0f,this);
        //初始化动画系统
        animtorSystem=new AnimtorSystem(gameObject,animationClips);
    }
    protected virtual void Awake()
    {
        Init();
    }
 
}
using UnityEngine;
 
public class MoveCharacterState : CharacterState
{
    float speed;
    Rigidbody2D rigidbody2D;
    public MoveCharacterState(Character character) : base(character)
    {
        NewActions.Instance.Player.Move.Enable();
        speed=Character.attribute.speed;
        rigidbody2D=Character.GetComponent<Rigidbody2D>();
        MonoManager.Instance.AddUpdateEvent(Update);
    }
    //进入状态
    public override void Enter()
    {
        Character.animtorSystem.PlayAnimation("move");
    }
    //持续状态
    public override void Update()
    {
        if (NewActions.Instance.Player.Move.ReadValue<Vector2>()==Vector2.zero)
        {
            Character.state=new IdleCharacterState(Character);
            MonoManager.Instance.RemoveUpdateEvent(Update);
        }
        Move.Move_fun(NewActions.Instance.Player.Move.ReadValue<Vector2>(),speed,rigidbody2D);
    }
    //退出状态
    public override void Exit()
    {
 
    }
}
 
using UnityEngine;
 
public class player : Character
{
 
    protected override void Awake()
    {
        base.Awake();
        state=new IdleCharacterState(this);
    }
  
}

第一这个有移除事件的方法
2有时候掉帧严重
3动画不流畅

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-02-15 15:29
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    你提出的问题主要集中在以下三点:

    1. 移除事件的方法不完善(可能未正确管理事件监听)
    2. 游戏掉帧严重(性能问题)
    3. 动画不流畅(动画状态切换或播放逻辑问题)

    ✅ 一、移除事件方法的改进

    在当前代码中,MonoManager.Instance.RemoveUpdateEvent(Update); 被调用,但没有明确说明 MonoManager 的实现方式。如果 MonoManager 没有正确记录所有注册的更新事件,那么可能导致事件无法被正确移除,从而造成内存泄漏或重复执行。

    ✅ 解决方案:

    1. 确保 MonoManager 正确管理事件

    public class MonoManager : MonoBehaviour
    {
        private static MonoManager _instance;
        public static MonoManager Instance => _instance;
    
        private List<Action> updateEvents = new List<Action>();
    
        void Awake()
        {
            if (_instance != null && _instance != this)
            {
                Destroy(this);
            }
            else
            {
                _instance = this;
            }
        }
    
        public void AddUpdateEvent(Action action)
        {
            updateEvents.Add(action);
        }
    
        public void RemoveUpdateEvent(Action action)
        {
            updateEvents.Remove(action);
        }
    
        private void Update()
        {
            foreach (var action in updateEvents)
            {
                action?.Invoke();
            }
        }
    }
    

    重点:确保 AddUpdateEventRemoveUpdateEvent 都能正确地添加和移除事件。


    ✅ 二、掉帧严重的优化建议

    掉帧通常由以下几个原因引起:

    • 大量的事件监听或频繁的协程调用
    • 动画播放频率过高
    • 未使用 FixedUpdate 处理物理移动
    • 未限制帧率或未使用 Time.fixedDeltaTime

    ✅ 解决方案:

    1. 使用 FixedUpdate 处理物理相关操作

    将移动逻辑移到 FixedUpdate 中,而不是 Update

    // 修改 MoveCharacterState 类中的 Update 方法为 FixedUpdate
    public override void FixedUpdate()
    {
        Vector2 moveInput = NewActions.Instance.Player.Move.ReadValue<Vector2>();
        if (moveInput == Vector2.zero)
        {
            Character.state = new IdleCharacterState(Character);
            MonoManager.Instance.RemoveUpdateEvent(Update);
        }
        Move.Move_fun(moveInput, speed, rigidbody2D);
    }
    

    重点:物理相关的操作应放在 FixedUpdate 中以提高性能。

    2. 限制帧率

    void Start()
    {
        Application.targetFrameRate = 60; // 设置目标帧率为 60
    }
    

    重点:设置合适的帧率可以避免不必要的资源消耗。

    3. 减少事件监听数量

    如果你的 MonoManager 注册了大量事件,可能会导致性能下降。可以考虑只在必要时注册事件。


    ✅ 三、动画不流畅的解决方案

    动画不流畅通常与以下几点有关:

    • 动画状态切换过于频繁
    • 动画播放未使用 Animancer 的过渡功能
    • 动画播放逻辑没有等待动画结束就切换

    ✅ 解决方案:

    1. 使用 Animancer 的 CrossFade 过渡

    目前你的 PlayAnimation 方法直接播放新动画,这会导致动画突变。建议使用 CrossFade 来平滑过渡。

    修改 AnimtorSystemPlayAnimation 方法:

    public void PlayAnimation(string animationName, float fadeDuration = 0.2f)
    {
        ClipTransition animationClip = animationClips.Find(x => x.Name == animationName);
        if (animationClip != null)
        {
            animancerComponent.CrossFade(animationClip, fadeDuration);
        }
    }
    

    重点:使用 CrossFade 可以让动画过渡更自然,减少卡顿感。

    2. 在动画结束后触发状态切换

    如果你在 Update 中判断输入并切换状态,可能会导致动画播放不完整。可以改用 Animancer 的事件来控制状态切换。

    例如,在 IdleCharacterStateEnter() 中注册动画完成事件:

    public override void Enter()
    {
        Character.animtorSystem.PlayAnimation("idle", 0.5f);
        animancerComponent.Events.OnAnimationEnd.AddListener(OnAnimationEnd);
    }
    
    private void OnAnimationEnd(AnimancerState state)
    {
        // 动画播放完成后触发某些逻辑
    }
    

    重点:使用动画事件可以更精确地控制状态切换时机。


    ✅ 四、总结优化点

    | 问题 | 解决方案 | |------|----------| | 移除事件方法不完善 | 确保 MonoManager 正确记录和移除事件 | | 掉帧严重 | 使用 FixedUpdate 处理物理移动,限制帧率 | | 动画不流畅 | 使用 Animancer.CrossFade 平滑过渡动画,使用动画事件控制状态切换 |


    ✅ 五、修改后的关键代码示例

    1. MoveCharacterState 修改为使用 FixedUpdate

    public override void FixedUpdate()
    {
        Vector2 moveInput = NewActions.Instance.Player.Move.ReadValue<Vector2>();
        if (moveInput == Vector2.zero)
        {
            Character.state = new IdleCharacterState(Character);
            MonoManager.Instance.RemoveUpdateEvent(Update);
        }
        Move.Move_fun(moveInput, speed, rigidbody2D);
    }
    

    2. AnimtorSystem 改为支持 CrossFade

    public void PlayAnimation(string animationName, float fadeDuration = 0.2f)
    {
        ClipTransition animationClip = animationClips.Find(x => x.Name == animationName);
        if (animationClip != null)
        {
            animancerComponent.CrossFade(animationClip, fadeDuration);
        }
    }
    

    3. IdleCharacterState 添加动画完成事件

    public override void Enter()
    {
        Character.animtorSystem.PlayAnimation("idle", 0.5f);
        animancerComponent.Events.OnAnimationEnd.AddListener(OnAnimationEnd);
    }
    
    private void OnAnimationEnd(AnimancerState state)
    {
        // 动画播放完成后触发逻辑
    }
    

    如需进一步分析性能瓶颈,建议使用 Unity 的 Profiler 工具 查看 CPU 和 GPU 的占用情况,以便更有针对性地优化。

    评论

报告相同问题?

问题事件

  • 创建了问题 2月15日