再议Unity3D中的Awake(),OnEnable()和Start()

本文通过测试案例详细探讨了Unity中脚本的执行顺序,包括Awake、OnEnable及Start等函数的调用时机,揭示了这些函数在GameObject实例化过程中的行为特性。

引言
近来遇到了一个问题,排查到最后,发现问题的原因在于,对于Unity原生函数执行顺序的理解还不到位。之前转载过一篇介绍Unity原生函数执行顺序的文章Unity3D中脚本的执行顺序和编译顺序
单纯的讨论一个GameObject的Awake在Start函数执行是没什么实际意义的。当遇到动态加载对象,或者相互引用时,情况要复杂一些。
官方介绍
First Scene Load
These functions get called when a scene starts (once for each object in the scene).
● Awake: This function is always called before any Start functions and also just after a prefab is instantiated. (If a GameObject is inactive during start up Awake is not called until it is made active.)
● OnEnable: (only called if the Object is active): This function is called just after the object is enabled. This happens when a MonoBehaviour instance is created, such as when a level is loaded or a GameObject with the script component is instantiated.
● OnLevelWasLoaded: This function is executed to inform the game that a new level has been loaded.
Note that for objects added to the scene, the Awake and OnEnable functions for all scripts will be called before Start, Update, etc are called for any of them. Naturally, this cannot be enforced when an object is instantiated during gameplay.
Before the first frame update
● Start: Start is called before the first frame update only if the script instance is enabled.
For objects added to the scene, the Start function will be called on all scripts before Update, etc are called for any of them. Naturally, this cannot be enforced when an object is instantiated during gameplay.
可以看到Awake和OnEnable是在GameObject instantiate阶段被调用的。而Start是在Update第一次执行前被调用的。
测试
测试用例:
写了两段脚本,第一段脚本StartGo.cs挂在默认对象上,通过点击场景上的按钮来动态创建第二个对象,其上挂载着第二段脚本DynamicGo.cs。
StartGo.cs

using UnityEngine;
using System.Collections;

public class StartGo : MonoBehaviour {

    private bool operateOnce = false;
    void Awake()
    {
        Debug.Log("StartGo Awake, frame: " + Time.frameCount);
    }
    void OnEnable()
    {
        Debug.Log("StartGo OnEnable, frame: " + Time.frameCount);
    }
    // Use this for initialization
    void Start () 
    {
        Debug.Log("StartGo Start, frame: " + Time.frameCount);
    }

    // Update is called once per frame
    void Update () 
    {
        if (!operateOnce)
        {
            Debug.Log("StartGo Update, frame: " + Time.frameCount);
            operateOnce = true;
        }
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 200, 30), "Create Dynamic Go"))
        {
            Debug.Log("Click Create Dynamic Go, frame: " + Time.frameCount);
            GameObject go = Resources.Load("DynamicGo") as GameObject;
            GameObject.Instantiate(go);
            Debug.Log("Instantiate Finished, frame: " + Time.frameCount);
        }
    }
}

DynamicGo.cs

using UnityEngine;
using System.Collections;

public class DynamicGo : MonoBehaviour {
    private bool operateOnce = false;
    void Awake()
    {
        Debug.Log("DynamicGo Awake, frame: " + Time.frameCount);
    }
    void OnEnable()
    {
        Debug.Log("DynamicGo OnEnable, frame: " + Time.frameCount);
    }
    // Use this for initialization
    void Start () 
    {
        Debug.Log("DynamicGo Start, frame: " + Time.frameCount);
    }

    // Update is called once per frame
    void Update () 
    {
        if (!operateOnce)
        {
            Debug.Log("DynamicGo Update, frame: " + Time.frameCount);
            operateOnce = true;
        }

    }
}

运行结果:
这里写图片描述这里写图片描述这里写图片描述
1.其中StartGo.cs的Start信息在OnEnable的下一帧打印出来,而DynamicGo.cs的Start信息与OnEnbale在同一帧打印出来。
2.通过查看DynamicGo的堆栈调用,可以看到OnEnable与Awake是在UnityEngine.Object:Instantiate阶段被调用的。而Start是在Update执行之前在会触发。
结论:
OnEnable与Awake是在GameObject实例化阶段被调用的。
Start可能与OnEnable在同一帧执行,也可能在OnEnable的下一帧执行。

<think>好的,我现在需要帮助用户解决在Unity3D中动态修改UI Text组件文字内容的问题。用户提到了“动态更新”“setText”,可能想要通过代码实时改变文本内容。首先,我需要回忆Unity中处理UI文本的方法,尤其是传统的UI Text较新的TextMeshPro。 根据用户提供的引用内容,引用[1]详细介绍了TextMeshPro的优势,比如更清晰的渲染更多的格式控制,推荐使用它替代旧版Text。用户的问题虽然提到的是UI Text,但可能需要考虑是否应该引导用户使用TextMeshPro,因为Unity官方推荐后者。不过,用户可能还在使用旧版,所以需要同时覆盖两种情况。 接下来,我需要确定如何动态修改文本。对于传统UI Text,通常是通过获取Text组件的引用,然后修改其text属性。例如,在代码中使用GetComponent<Text>().text = "新内容"。而对于TextMeshPro,则是获取TextMeshProUGUI组件,并修改其text属性。用户可能需要知道这两种方法的区别步骤。 用户可能遇到的常见问题包括未正确获取组件引用、未分配组件到脚本变量,或者在错误的时机更新文本。例如,如果在StartAwake中没有获取组件,可能导致NullReferenceException。此外,动态更新时可能需要考虑性能,比如频繁更新文本是否会影响效率,但根据引用[1],TextMeshPro在性能上更优,可能更适合高频更新。 用户提供的引用[3]中的代码示例展示了如何通过脚本更新UI元素,比如调用UpdateSpriteIdUpdateColorIcon方法。这可能类比到文本更新,需要类似的方法来触发文本内容的改变。比如,用户可能在事件驱动的情况下更新文本,如按钮点击或游戏事件发生时。 还需要考虑UI组件的启用状态,如果组件所在的GameObject未激活,修改text属性可能不会立即生效或导致错误。此外,跨脚本访问时,需要确保正确的引用传递,比如通过public变量拖拽赋值或使用Find方法查找。 另外,引用[4]提到了将世界坐标转换为屏幕坐标来更新UI位置,这可能与动态更新文本内容无关,但用户可能需要了解如何结合位置变化文本更新,比如在屏幕外目标指示器中同时更新文本内容。 总结回答结构,首先区分传统TextTextMeshPro,分别说明步骤,提供代码示例,并指出常见问题解决方案。需要提醒用户优先使用TextMeshPro,同时兼容旧版方法。最后,生成相关问题,帮助用户深入理解。</think>### Unity3D 动态修改 UI 文字内容的完整指南 在 Unity3D 中动态修改 UI 文字内容有两种主流方式:**传统 UI Text** **TextMeshPro(推荐)**。以下是具体实现方法及常见问题解决方案: --- #### 一、传统 UI Text 修改方式 ##### 1. **基本步骤** 1. **添加 Text 组件**:在 UI Canvas 下创建 Text 对象(右键 -> UI -> Text)。 2. **编写脚本**:通过 `Text.text` 属性动态赋值。 3. **绑定脚本**:将脚本挂载到 Text 对象或其父物体上。 **示例代码**: ```csharp using UnityEngine; using UnityEngine.UI; public class TextUpdater : MonoBehaviour { public Text uiText; // 拖拽绑定 Text 组件 void Start() { // 初始化文字内容 uiText.text = "初始文本"; } // 通过方法动态更新文字 public void UpdateText(string newText) { if (uiText != null) { uiText.text = newText; } else { Debug.LogError("未绑定 Text 组件!"); } } } ``` ##### 2. **常见问题** - **问题1:未正确绑定组件** 若未在 Inspector 中拖拽赋值 `uiText`,需通过代码获取: ```csharp void Start() { uiText = GetComponent<Text>(); } ``` - **问题2:文字不更新** 检查是否在运行时调用了 `UpdateText` 方法(例如通过按钮事件或协程)。 --- #### 二、TextMeshPro 修改方式(推荐) 根据引用[1],TextMeshPro 是 Unity 官方推荐的文本解决方案,支持更清晰的渲染富文本格式[^1]。 ##### 1. **基本步骤** 1. **导入 TextMeshPro**:首次使用时,通过菜单 `Window -> TextMeshPro -> Import TMP Essential Resources` 导入资源。 2. **创建 TextMeshPro 对象**:右键 -> UI -> Text - TextMeshPro。 3. **编写脚本**:通过 `TextMeshProUGUI.text` 属性修改内容。 **示例代码**: ```csharp using TMPro; using UnityEngine; public class TMProUpdater : MonoBehaviour { public TMP_Text tmpText; // 拖拽绑定 TextMeshPro 组件 void Start() { tmpText.text = "TextMeshPro 初始文本"; } public void UpdateText(string newText) { if (tmpText != null) { tmpText.text = newText; // 支持富文本标签,如 "<color=red>红色文字</color>" } } } ``` ##### 2. **高级功能** - **富文本标记**:支持颜色、大小、超链接等(参考引用[1]中提到的30+富文本标记): ```csharp tmpText.text = "<b>加粗</b> <i>斜体</i> <size=24>大号文字</size>"; ``` - **动态数据绑定**:结合引用[2]中的属性绑定思想,可通过事件系统实现数据驱动更新。 --- #### 三、性能优化与常见问题 1. **性能对比** - TextMeshPro 的渲染效率与 UI Text 接近,但支持更高清显示[^1]。 - 高频更新文本时(如计时器),建议使用 `StringBuilder` 减少内存分配。 2. **跨脚本更新** 参考引用[3]中的 `UI_CustomizationUI` 脚本逻辑,可通过事件或委托触发更新: ```csharp // 定义事件 public class GameEvents : MonoBehaviour { public static System.Action<string> OnTextUpdate; } // 订阅事件 void OnEnable() { GameEvents.OnTextUpdate += UpdateText; } void OnDisable() { GameEvents.OnTextUpdate -= UpdateText; } ``` 3. **动态本地化** 结合 JSON 或 ScriptableObject 存储多语言文本,动态切换内容。 --- ### 四、最终代码整合 ```csharp using TMPro; using UnityEngine; public class DynamicTextManager : MonoBehaviour { [Header("传统 UI Text")] public Text legacyText; [Header("TextMeshPro")] public TMP_Text tmpText; private void Start() { // 初始化两种文本 if (legacyText != null) legacyText.text = "传统文本已加载"; if (tmpText != null) tmpText.text = "<color=#00FF00>TMP 文本已加载</color>"; } // 统一更新接口 public void SetText(string content) { if (legacyText != null) legacyText.text = content; if (tmpText != null) tmpText.text = content; } } ``` --- ### 相关问题 1. 如何实现 TextMeshPro 的文字动画效果? 2. UI 文本如何根据屏幕分辨率自适应缩放? 3. 怎样通过 JSON 文件动态加载多语言文本? 4. TextMeshPro 的富文本标记具体支持哪些格式?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值