万镜之下 2026-02-15 16:27 采纳率: 6.3%
浏览 3

unity的命令模式


using UnityEngine;

public class Move : BehavioCommand
{
    Rigidbody2D rigidbody2D;
    float speed;
    Vector2 direction;
    public Move(Rigidbody2D rigidbody2D,float speed,Vector2 direction)
    {
        this.rigidbody2D = rigidbody2D;
        this.speed = speed;
        this.direction = direction;
    }
    public void Execute()
    {
        rigidbody2D.MovePosition(rigidbody2D.position + direction * speed * Time.fixedDeltaTime);
    }
}

这是自己写的移动命令(昨天晚上听你的建议),可是如果放在刷新函数,每0.2秒就new一次是不是太浪费资源。或者说是new在外面,刷新函数直接execute。到底该怎么用

  • 写回答

1条回答 默认 最新

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

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

    你提出的问题非常关键,命令模式在Unity中使用时的性能优化和资源管理是一个需要认真考虑的部分。


    一、问题分析

    你目前的代码是这样的:

    public class Move : BehavioCommand
    {
        Rigidbody2D rigidbody2D;
        float speed;
        Vector2 direction;
    
        public Move(Rigidbody2D rigidbody2D, float speed, Vector2 direction)
        {
            this.rigidbody2D = rigidbody2D;
            this.speed = speed;
            this.direction = direction;
        }
    
        public void Execute()
        {
            rigidbody2D.MovePosition(rigidbody2D.position + direction * speed * Time.fixedDeltaTime);
        }
    }
    

    你在每帧或每隔一段时间(比如0.2秒)调用 new Move(...),这确实会带来性能开销,因为每次创建对象都会触发垃圾回收(GC),影响游戏性能。


    二、解决方案

    1. 命令对象复用(池化)

    避免频繁创建新对象,可以采用“命令池”方式,预先生成一批命令对象并重复使用。

    ✅ 推荐做法:将命令对象预分配,复用执行

    你可以使用一个 List<Move> 或者一个对象池来管理这些命令。

    示例代码(简化版):
    public class CommandPool
    {
        private List<Move> commandPool = new List<Move>();
    
        public Move GetCommand(Rigidbody2D rb, float speed, Vector2 dir)
        {
            Move cmd = null;
    
            // 尝试从池中获取空闲命令
            for (int i = 0; i < commandPool.Count; i++)
            {
                if (!commandPool[i].IsUsed)
                {
                    cmd = commandPool[i];
                    cmd.IsUsed = true;
                    break;
                }
            }
    
            if (cmd == null)
            {
                // 没有可用命令,新建
                cmd = new Move(rb, speed, dir);
                commandPool.Add(cmd);
            }
    
            return cmd;
        }
    
        public void ReleaseCommand(Move cmd)
        {
            cmd.IsUsed = false;
        }
    }
    
    // 在Move类中添加标记
    public class Move : BehavioCommand
    {
        public bool IsUsed { get; set; } = false;
        // 其余代码不变
    }
    

    这样,你就可以在刷新函数中直接调用 GetCommand 获取命令,而不是每次都 new


    2. 避免在 Update 中频繁创建命令对象

    如果你的逻辑是:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            new Move(rb, speed, direction).Execute();
        }
    }
    

    那么这就是不推荐的做法。建议将命令对象创建放在初始化阶段,或者通过对象池管理。


    3. 使用委托/Action 来替代部分命令模式

    如果你只是想实现简单的“延迟执行”,也可以考虑使用 ActionCoroutine 来代替命令模式,避免频繁创建对象。

    ✅ 示例:使用 Action

    public class PlayerController : MonoBehaviour
    {
        public Rigidbody2D rb;
        public float moveSpeed = 5f;
    
        public void MovePlayer(Vector2 direction)
        {
            StartCoroutine(MoveWithDelay(direction));
        }
    
        private IEnumerator MoveWithDelay(Vector2 direction)
        {
            float time = 0f;
            while (time < 1f) // 移动1秒
            {
                rb.MovePosition(rb.position + direction * moveSpeed * Time.deltaTime);
                time += Time.deltaTime;
                yield return null;
            }
        }
    }
    

    这种方式更轻量,适合简单逻辑。


    三、总结

    | 问题 | 解决方案 | |------|----------| | 频繁创建命令对象导致性能问题 | 使用命令池(Command Pool)复用对象 | | 在Update中new命令对象 | 避免在Update中创建,改用对象池或Action | | 是否应该将命令对象放在外面 | 是的,应提前初始化并复用 |


    四、推荐优化后的代码结构

    1. 定义命令池

    public class CommandPool
    {
        private List<Move> pool = new List<Move>();
    
        public Move GetCommand(Rigidbody2D rb, float speed, Vector2 dir)
        {
            Move cmd = null;
    
            for (int i = 0; i < pool.Count; i++)
            {
                if (!pool[i].IsUsed)
                {
                    cmd = pool[i];
                    cmd.IsUsed = true;
                    break;
                }
            }
    
            if (cmd == null)
            {
                cmd = new Move(rb, speed, dir);
                pool.Add(cmd);
            }
    
            return cmd;
        }
    
        public void ReleaseCommand(Move cmd)
        {
            cmd.IsUsed = false;
        }
    }
    

    2. 修改 Move 类

    public class Move : BehavioCommand
    {
        public bool IsUsed { get; set; } = false;
        Rigidbody2D rigidbody2D;
        float speed;
        Vector2 direction;
    
        public Move(Rigidbody2D rigidbody2D, float speed, Vector2 direction)
        {
            this.rigidbody2D = rigidbody2D;
            this.speed = speed;
            this.direction = direction;
        }
    
        public void Execute()
        {
            rigidbody2D.MovePosition(rigidbody2D.position + direction * speed * Time.fixedDeltaTime);
        }
    }
    

    3. 使用示例

    public class PlayerController : MonoBehaviour
    {
        public Rigidbody2D rb;
        public float moveSpeed = 5f;
        private CommandPool commandPool = new CommandPool();
    
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Move moveCmd = commandPool.GetCommand(rb, moveSpeed, Vector2.right);
                moveCmd.Execute();
                // 如果不再需要,可以释放
                // commandPool.ReleaseCommand(moveCmd);
            }
        }
    }
    

    五、结语

    命令模式的核心是解耦和可扩展性,但不能忽略性能。合理使用对象池、减少不必要的对象创建,是提升Unity性能的关键。

    如果你能按照上述方式优化,性能会显著提升,同时保持代码结构清晰易维护。

    评论

报告相同问题?

问题事件

  • 创建了问题 2月15日