32.帧更新优化

32.性能优化-CPU-脚本-Update


32.1 知识点

知识回顾

知识回顾一:移除空的 Update 函数

若在脚本中定义了生命周期函数但未写任何逻辑,它们仍会被 Unity 按帧调用,带来额外开销。因此要避免写空的生命周期函数,尤其是每帧调用的:UpdateLateUpdateFixedUpdateOnGUI

知识回顾二:需要在 Update 中频繁使用的组件应该缓存

在 Unity 中反复对同一对象调用 GetComponent 是常见错误;对常用组件应用泛型方式获取一次并缓存,后续直接使用字段。应避免在 Update 中调用:GetComponent、Find 等查找组件/对象的 API。

降低执行频率

我们常在 Update 中重复执行某些逻辑;若超出实际需要的频率重复调用,会造成性能浪费。例如:怪物 AI 需要不断朝玩家寻路并移动,若在 Update 里每帧都做寻路,会造成不必要的消耗。

可以通过降低执行频率来避免:最常用的方式是自定义更新间隔,用计时变量累加 Time.deltaTime,达到间隔后再执行一次复杂逻辑,并将计时清零。

// 间隔多久更新一次(秒)
private float _updateTime = 0.25f;

// 用于计时的变量
private float _timer;

void Update()
{
    // 把原本每帧都执行的复杂逻辑,改为每隔一段时间执行一次;具体间隔根据实际情况设定
    _timer += Time.deltaTime;
    if (_timer >= _updateTime)
    {
        MonsterAILogic();
        _timer = 0;
    }
}

// 假设是执行怪物逻辑的函数
void MonsterAILogic()
{
    // 主要处理寻路逻辑
}

减少复杂计算

应尽量减少在 Update 中进行复杂数学计算。对于不变的计算结果,应在初始化阶段(Awake、Start)就计算好并存成字段,Update 中直接使用该字段,避免每帧重复计算。

事件驱动代替每帧检测

观察者模式取代在 Update 中每帧轮询状态:当状态变化时通过事件通知,而不是每帧检测。可参考 Unity 基础框架中的事件中心模块实现。

分帧执行

大批量对象的更新分帧处理:每帧只处理其中一部分对象,避免单帧内一次性处理过多导致卡顿。例如用列表记录所有待更新对象,用索引记录「当前处理到哪一位」,每帧只推进固定个数,索引到头再从头开始。

// 假设是存储所有游戏中怪物的容器
private List<Object> monsters = new List<Object>();

// 当前处理到第几个怪物(用于分帧)
private int _index = 0;

// 每帧最多更新多少个怪物
private int _monsterNums = 10;

void Update()
{
    for (int i = 0; i < _monsterNums; i++)
    {
        if (_index >= monsters.Count)
            _index = 0;
        // 假设这里是让容器中的某个怪物执行更新逻辑
        monsters[_index].GetType();
        _index++;
    }
}

自定义 Update 管理器(统一管理 Update 更新)

在 Unity 中,若每个继承 MonoBehaviour 的脚本都独立拥有自己的 Update,当这类对象很多时,会因引擎底层机制(C++ 调用 C# 的跨语言开销、内存访问方式等)带来额外性能开销。若对 Update 进行统一管理,只在一个地方执行「所有需要每帧更新的逻辑」,可以明显降低开销;需要每帧更新的对象越多,统一管理器的优势越明显(尤其在中大型场景中)。

常见做法:通过一个统一更新管理器,集中管理所有需要 Update 的对象或回调;场景中只需一个 MonoBehaviour 持有唯一的 Update,在该 Update 内遍历并调用所有注册的更新逻辑。实现方式可以是:用事件记录所有 Update 回调,或用列表记录需要更新的对象,在管理器内统一调用。可参考 Unity 基础框架中的公共 Mono 模块


32.2 知识点代码

Lesson32_性能优化_CPU_脚本_Update.cs

using System.Collections.Generic;
using UnityEngine;

public class Lesson32_性能优化_CPU_脚本_Update : MonoBehaviour
{
    // 间隔多久更新一次(秒)
    private float _updateTime = 0.25f;

    // 用于计时的变量
    private float _timer;

    // 假设是存储所有游戏中怪物的容器
    private List<Object> monsters = new List<Object>();

    // 当前处理到第几个怪物(用于分帧)
    private int _index = 0;

    // 每帧最多更新多少个怪物
    private int _monsterNums = 10;

    void Start()
    {
        #region 知识回顾一 移除空的Update函数

        /*
         * 定义了生命周期函数但未写逻辑,仍会带来额外开销。
         * 应避免写空的生命周期函数,尤其是 Update、LateUpdate、FixedUpdate、OnGUI。
         */

        #endregion

        #region 知识回顾二 需要在Update中频繁使用的组件应该缓存

        /*
         * 反复 GetComponent 是常见错误;常用组件应缓存。
         * 应避免在 Update 中调用 GetComponent、Find 等查找 API。
         */

        #endregion

        #region 知识点一 降低执行频率

        /*
         * Update 中超出需要的频率重复调用会造成浪费(如每帧寻路)。
         * 可通过自定义更新间隔:_timer += Time.deltaTime,达到间隔再执行逻辑并清零。
         */

        #endregion

        #region 知识点二 减少复杂计算

        /*
         * 尽量减少在 Update 中做复杂数学计算。
         * 不变的结果应在 Awake/Start 中算好,Update 中直接使用。
         */

        #endregion

        #region 知识点三 事件驱动代替每帧检测

        /* 用观察者模式取代 Update 中每帧状态检测,可参考事件中心模块 */

        #endregion

        #region 知识点四 分帧执行

        /* 大批量对象更新做分帧处理,每帧只处理一部分,避免单帧卡顿 */

        #endregion

        #region 知识点五 自定义Update管理器(统一管理Update更新)

        /*
         * 每个 MonoBehaviour 独立 Update 在对象多时会有跨语言、内存访问等开销。
         * 统一管理:一个 Update 内执行所有注册的更新逻辑,可大幅提升性能。
         * 可用事件或列表记录回调/对象,参考公共 Mono 模块。
         */

        #endregion
    }

    void Update()
    {
        // 降低执行频率:每隔 _updateTime 秒执行一次怪物逻辑
        _timer += Time.deltaTime;
        if (_timer >= _updateTime)
        {
            MonsterAILogic();
            _timer = 0;
        }

        // 分帧执行:每帧只更新 _monsterNums 个怪物
        for (int i = 0; i < _monsterNums; i++)
        {
            if (_index >= monsters.Count)
                _index = 0;
            monsters[_index].GetType();
            _index++;
        }
    }

    // 假设是执行怪物逻辑的函数
    void MonsterAILogic()
    {
        // 主要处理寻路逻辑
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏