[{"content":"一句话速览 团结引擎 1.8 打包到 iOS 26 上时，若 TMP InputField 开启了 Hide Mobile Input，输入字符后 InputField 会先失活再重新获得焦点，此时文本被全选，再输入任意字符，开头的第一个字符变成乱码。这是 Unity 2021 时代的老 Bug 在团结引擎上的复现。\n一、场景：一个\u0026quot;本不该出问题\u0026quot;的输入框 故事从一个非常常规的需求开始：给 iOS 端的用户做个输入框，需要屏蔽掉系统键盘自带的输入预览条（就是那个键盘上方悬浮的白色条）。\n做法也很常规——勾上 TMP_InputField 的 Hide Mobile Input（代码里就是 shouldHideMobileInput = true）。\n在编辑器里测试一切正常。打包上 iOS 26 真机之后，Bug 出现了。\n二、现象：精确到每一步的复现路径 预期的行为 步骤 正常应发生的事 ① 点击输入框 聚焦，弹出键盘 ② 输入若干字符 输入框正常展示字符 ③ 点击输入框外部再点回来 光标定位到最后，继续输入正常 ④ 多轮输入 全文无乱码，一切正常 实际的行为（Bug 复现路径） 步骤 实际发生的事 ① 点击输入框 ✅ 聚焦，键盘弹出 ② 输入若干字符（如 \u0026ldquo;Hello\u0026rdquo;） ✅ 显示正常 ③ 输入框失活 → 再激活（获得焦点） ⚠️ 文本被全选（全部高亮） ④ 再输入任意字符（如 \u0026ldquo;X\u0026rdquo;） ❌ 开头的字符变成乱码，如 \u0026ldquo;䮙ello\u0026rdquo; 或类似不可识别字符 这个 Bug 不是偶发——稳定复现，只要 Hide Mobile Input + 输入框经历\u0026quot;失活→重新聚焦\u0026quot;这个流程，必现。\n定位线索：Unity 2021 的老 Bug 查了一圈，在 Unity 的 Bug 修复记录里找到了相似案例。这是 Unity 2021 以前版本的一个已知 Bug：Toggle 隐藏原生键盘输入显示时，TMP_InputField 的文本选择状态和键盘监听器之间存在竞态条件，导致文本缓冲区在 OnFocus 回调中被错误地部分覆盖。\n按说这个 Bug 在 Unity 2021 LTS 里已经被修复了。团结引擎是基于 Unity 2021 LTS 做的二次开发，理论上不应该有这个问题。但事实就是——它在团结引擎 1.8 上复现了。\n🤔 猜测：团结引擎在 iOS 键盘交互层（TouchScreenKeyboard 相关部分）做了自定义改动来适配国内分发环境，可能是这些改动把已经修好的 Bug 又带回来了。不过这只是我的推测，没有深入源码验证。\n三、实战：三条应对方案及其取舍 既然 Bug 是真的，就只能找绕行方案。以下是实测可用的三条路：\n方案一：不 Hide Mobile Input 最简单的，把 shouldHideMobileInput 设回 false，回归系统默认行为：\n// InputField 上取消勾选 Hide Mobile Input // 或代码里： inputField.shouldHideMobileInput = false; ✅ 优点：Bug 从根源消失，代码改动最小，不需要额外引入任何库 ❌ 代价：iOS 键盘上方那条白色的输入预览条又回来了。如果你的 UI 布局紧凑，预览条会挡住后面的内容，视觉上也不够干净 适用场景：对键盘预览条没有强烈洁癖的项目，或者布局上有足够余量避开遮挡。\n方案二：不要 Select All on Reselect 保持 Hide Mobile Input 勾选，但在 InputField 上关掉 On Focus Select All（或者代码里 inputField.onFocusSelectAll = false）。\n✅ 优点：降低问题触发概率。聚焦时不再自动全选，能绕过部分竞态路径 ❌ 代价：\u0026ldquo;Select All\u0026rdquo; 在很多输入场景下是符合用户预期的行为（比如清空旧内容快速重输），关掉它等于牺牲了一部分交互习惯 适用场景：紧急修复、临时上线，配合定期的 DeactivateInputField / ActivateInputField 调用来降低触发率。\n方案三：使用 UMI（原生输入插件） 引入 UMI（Unity Native Input Plugin / 原生输入插件），让 TMP InputField 走原生键盘通道，完全绕过 Unity 的 TouchScreenKeyboard 管理层。\n✅ 优点：从底层规避整类问题，输入体验最贴近原生 iOS ❌ 代价：UMI 的原生视图会绘制在所有 Unity UI 之上。如果你的 UI 里还有全屏盖层、弹窗、动画切换，UMI 输入框会\u0026quot;穿模\u0026quot;到它们上面，视觉效果非常突兀 适用场景：可以接受\u0026quot;原生输入覆盖一切\u0026quot;的产品形态，或者输入框所在页面几乎没有层叠 UI。\n方案对照表 方案 改动量 副作用 根治概率 ① 不 Hide Mobile Input 极小 键盘预览条显示 100% ② 关 Select All on Reselect 小 聚焦不自动全选，仍有残留风险 ≈80% ③ 换 UMI 插件 中 原生视图覆盖所有 Unity UI 99% 四、误区复盘：我踩过的坑 误区一：\u0026ldquo;Unity 2021 修过的 Bug 团结引擎肯定也有\u0026rdquo; 这件事给我最大的教训。团结引擎不是 Unity 2021 的\u0026quot;纯净版\u0026quot;——它在底层做了很多本地化修改，尤其是 iOS 适配层和国内分发通道。信任\u0026quot;原版已修复\u0026quot;等于假设团结引擎完全没有改过相关代码。基于\u0026quot;如果没改就别动\u0026quot;的假设来判断 Bug 是否存在，在 fork 类引擎上不成立。\n误区二：\u0026ldquo;Hide Mobile Input 该不会是这个 Bug 的根因吧？\u0026rdquo; 我第一反应是 shouldHideMobileInput 属性本身有缺陷，考虑过用反射 Hack 它的内部字段。但仔细看了 Unity 官方的修复记录后发现——这个属性的逻辑本身是对的，问题是它触发了 TouchScreenKeyboard 内部状态机的某个边缘路径，导致文本选择与键盘缓冲区不同步。根因不在这个属性，而在属性触发的连锁反应。不要急于杀死\u0026quot;嫌疑人\u0026quot;——有时候问题出在它触发的下游，不是它自己。\n误区三：\u0026ldquo;用 UMI 就万事大吉了\u0026rdquo; UMI 解决了输入本身的问题，却引入了新的渲染层问题。\u0026ldquo;原生视图在所有 Unity UI 之上\u0026quot;意味着你的弹窗、Toast、全屏过渡都可能被输入框\u0026quot;刺穿\u0026rdquo;。在采纳第三方插件之前，一定要先跑一个\u0026quot;UI 层叠 + 弹窗 + 动画\u0026quot;的综合测试用例，确认插件在你的 UI 体系里不会引起更大的视觉问题。\n五、后续 目前我选了方案二（关 Select All on Reselect）作为临时方案上线，同时在跟进团结引擎官方对这个 Bug 的修复状态。如果你的项目也遇到了，建议先记一笔、走方案一或方案二绕过，等团结引擎下一个补丁再看能否切回原生行为。\n如果你在做跨引擎项目（Unity → 团结），强烈建议建立一张\u0026quot;已知已在原版修复、但团结上仍需验证\u0026quot;的清单。像这类\u0026quot;老 Bug 复现\u0026quot;坑，一次就够了。\n如果你遇到过类似的\u0026quot;被修好又重新出现\u0026quot;的 Bug，欢迎在评论区分享你的经历。毕竟有些 Bug 就像老游戏的 Boss——你以为通关了，续作里换了身皮肤又回来了。\n","permalink":"https://davinci-13.github.io/posts/unity-tmp-inputfield-hidemobile-ios26-bug/","summary":"\u003ch2 id=\"一句话速览\"\u003e一句话速览\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e团结引擎 1.8 打包到 iOS 26 上时\u003c/strong\u003e，若 TMP InputField 开启了 \u003ccode\u003eHide Mobile Input\u003c/code\u003e，输入字符后 InputField 会先失活再重新获得焦点，此时文本被全选，再输入任意字符，\u003cstrong\u003e开头的第一个字符变成乱码\u003c/strong\u003e。这是 Unity 2021 时代的老 Bug 在团结引擎上的复现。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"一场景一个本不该出问题的输入框\"\u003e一、场景：一个\u0026quot;本不该出问题\u0026quot;的输入框\u003c/h2\u003e\n\u003cp\u003e故事从一个非常常规的需求开始：给 iOS 端的用户做个输入框，需要屏蔽掉系统键盘自带的输入预览条（就是那个键盘上方悬浮的白色条）。\u003c/p\u003e\n\u003cp\u003e做法也很常规——勾上 \u003ccode\u003eTMP_InputField\u003c/code\u003e 的 \u003ccode\u003eHide Mobile Input\u003c/code\u003e（代码里就是 \u003ccode\u003eshouldHideMobileInput = true\u003c/code\u003e）。\u003c/p\u003e\n\u003cp\u003e在编辑器里测试一切正常。打包上 iOS 26 真机之后，Bug 出现了。\u003c/p\u003e\n\u003ch2 id=\"二现象精确到每一步的复现路径\"\u003e二、现象：精确到每一步的复现路径\u003c/h2\u003e\n\u003ch3 id=\"预期的行为\"\u003e预期的行为\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e步骤\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e正常应发生的事\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e① 点击输入框\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e聚焦，弹出键盘\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e② 输入若干字符\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入框正常展示字符\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e③ 点击输入框外部再点回来\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e光标定位到最后，继续输入正常\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e④ 多轮输入\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e全文无乱码，一切正常\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"实际的行为bug-复现路径\"\u003e实际的行为（Bug 复现路径）\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e步骤\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e实际发生的事\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e① 点击输入框\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e✅ 聚焦，键盘弹出\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e② 输入若干字符（如 \u0026ldquo;Hello\u0026rdquo;）\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e✅ 显示正常\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e③ 输入框失活 → 再激活（获得焦点）\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e⚠️ \u003cstrong\u003e文本被全选（全部高亮）\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e④ 再输入任意字符（如 \u0026ldquo;X\u0026rdquo;）\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e❌ \u003cstrong\u003e开头的字符变成乱码\u003c/strong\u003e，如 \u0026ldquo;䮙ello\u0026rdquo; 或类似不可识别字符\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e这个 Bug 不是偶发——稳定复现，只要 \u003ccode\u003eHide Mobile Input\u003c/code\u003e + 输入框经历\u0026quot;失活→重新聚焦\u0026quot;这个流程，必现。\u003c/p\u003e","title":"记一次团结引擎 + iOS 26 上的 TMP InputField 乱码 Bug"},{"content":"写在前面 这是一篇\u0026quot;主观认识\u0026quot;系列的开篇。我是一个做了十年游戏 / 客户端开发的工程师，今年才开始认真碰 AI，水平大致在\u0026quot;能调 API、看得懂科普、论文还吃力\u0026quot;的初级阶段。\n这个系列不追求严谨，也不打算教别人什么——只是把我自己对 AI 的一些粗浅但真实的认识记下来，既是梳理，也方便以后回看自己当时有多 naive。\n第一篇，先聊一个对我来说很重要的\u0026quot;分法\u0026quot;：把\u0026quot;AI 开发\u0026quot;再拆成两层。\n一、AI 开发可以再分两层：算法侧 vs 应用侧 概念：我以前把\u0026quot;做 AI\u0026quot;想简单了 很长一段时间里，我默认\u0026quot;做 AI\u0026quot; ≈ \u0026ldquo;训练模型\u0026rdquo;。直到自己真上手去用 AI 做事，才发现这个词底下其实藏着两条几乎不相交的路：\nAI 算法（模型侧 / Algorithm side）：研究怎么\u0026quot;造出那个会思考 / 会生成的东西本身\u0026quot;。它关心模型架构、训练、微调、推理优化、底层框架（PyTorch / JAX 那一套）、数据和数学。这一侧的产出物是\u0026quot;模型 / 能力\u0026quot;。 AI 应用开发（工程侧 / Application side）：拿到现成的、或别人微调好的模型，把它真正塞进产品和工作流里用起来。它关心 API 调用、Prompt 工程、RAG、Agent 编排、向量数据库、评估与线上监控。这一侧的产出物是\u0026quot;能用的功能 / 产品\u0026quot;。 🔧 技术校准（AI 专家视角）：这个二分法在业界有对应的说法。研究 / 模型侧常叫 ML Research / Deep Learning；把模型落地成系统的那一侧，近年被 Chip Huyen 等人明确称为 AI Engineering，也叫 Applied ML / ML Engineering。需要注意：两层之间并没有一道墙——微调（fine-tuning）、MLOps、模型压缩这些恰好卡在中间，所以\u0026quot;算法\u0026quot;和\u0026quot;应用\u0026quot;更像光谱的两端，而不是非黑即白。\n对比：两条路的难度维度根本不同 AI 算法（模型侧） AI 应用开发（工程侧） 像什么 科研：深、窄、靠硬功 工程：广、快、靠整合 核心门槛 懂模型、懂数学 / 系统 懂产品、懂怎么把模型用出价值 周期 长，迭代慢 短，能快速出 demo 我的位置 门外，只浅尝过 正在摸门 我自己是游戏 / 客户端工程师出身，天然更靠近应用侧：我不需要从零训一个大模型，但我能用\u0026quot;API + RAG\u0026quot;很快把一个知识库变成能对话的东西。一度我因此产生一种错觉——\u0026ldquo;原来我也能做 AI 了\u0026rdquo;。后来才意识到，我做的只是应用侧里很薄的一层。\n实战：我目前只摸到了应用侧的门 目前我手上的 AI 实践几乎全在应用侧：\n调各家 API、写（并反复改）Prompt； 搭 RAG：把博客 / 笔记喂进向量库，做成能检索的助手； 用 Agent 把\u0026quot;查资料 → 整理 → 成稿\u0026quot;的工作流串起来（比如现在这篇，就是我和我的博客 Agent 协作的产物）。 算法侧我只浅尝：跟着跑过几个 Colab notebook、扫过一点训练代码，离\u0026quot;能独立训一个有用模型\u0026quot;还差得远。\n但应用侧也不是\u0026quot;调个接口就完事\u0026quot;。我踩过的坑包括：Prompt 看着像玄学其实有方法论；RAG 的上限基本由数据质量决定；Agent 的编排和容错，往往比模型本身更折磨人。\n误区：我想纠正自己当初的几个误解 \u0026ldquo;会用 ChatGPT / 调 API = 懂 AI\u0026rdquo; —— 这是应用侧的入门，远不是 AI 的全部。 \u0026ldquo;做 AI 一定要数学超好、会训模型\u0026rdquo; —— 应用侧对数学的要求低得多，更吃工程整合和产品感觉。这也是为什么工程背景的人（比如我）能比较顺地切进来。 \u0026ldquo;算法侧比应用侧更高级\u0026rdquo; —— 两者难在不同的维度。把模型稳定、便宜、可控地用出业务价值，同样极难，绝不\u0026quot;低级\u0026quot;。 后续：这篇之后我还想写 为什么我觉得\u0026quot;AI 应用开发\u0026quot;更适合工程背景的人切入； 我对 Agent 的主观看法（以及被它坑过的真实经历）； AI 帮我提效的几个具体场景； 小结：把\u0026quot;AI 开发\u0026quot;拆成算法侧和应用侧，帮我理清了自己到底在哪儿、该往哪儿使劲。下一篇接着聊——应用侧为什么适合我这种人。\n","permalink":"https://davinci-13.github.io/posts/ai-basics-notes/","summary":"\u003ch2 id=\"写在前面\"\u003e写在前面\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e这是一篇\u0026quot;主观认识\u0026quot;系列的开篇。我是一个做了十年游戏 / 客户端开发的工程师，今年才开始认真碰 AI，水平大致在\u0026quot;能调 API、看得懂科普、论文还吃力\u0026quot;的初级阶段。\u003c/p\u003e\n\u003cp\u003e这个系列不追求严谨，也不打算教别人什么——只是把我自己对 AI 的一些\u003cstrong\u003e粗浅但真实\u003c/strong\u003e的认识记下来，既是梳理，也方便以后回看自己当时有多 naive。\u003c/p\u003e\n\u003cp\u003e第一篇，先聊一个对我来说很重要的\u0026quot;分法\u0026quot;：把\u0026quot;AI 开发\u0026quot;再拆成两层。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一ai-开发可以再分两层算法侧-vs-应用侧\"\u003e一、AI 开发可以再分两层：算法侧 vs 应用侧\u003c/h2\u003e\n\u003ch3 id=\"概念我以前把做-ai想简单了\"\u003e概念：我以前把\u0026quot;做 AI\u0026quot;想简单了\u003c/h3\u003e\n\u003cp\u003e很长一段时间里，我默认\u0026quot;做 AI\u0026quot; ≈ \u0026ldquo;训练模型\u0026rdquo;。直到自己真上手去用 AI 做事，才发现这个词底下其实藏着两条几乎不相交的路：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAI 算法（模型侧 / Algorithm side）\u003c/strong\u003e：研究怎么\u0026quot;造出那个会思考 / 会生成的东西本身\u0026quot;。它关心模型架构、训练、微调、推理优化、底层框架（PyTorch / JAX 那一套）、数据和数学。这一侧的\u003cstrong\u003e产出物是\u0026quot;模型 / 能力\u0026quot;\u003c/strong\u003e。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAI 应用开发（工程侧 / Application side）\u003c/strong\u003e：拿到现成的、或别人微调好的模型，把它真正塞进产品和工作流里用起来。它关心 API 调用、Prompt 工程、RAG、Agent 编排、向量数据库、评估与线上监控。这一侧的\u003cstrong\u003e产出物是\u0026quot;能用的功能 / 产品\u0026quot;\u003c/strong\u003e。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e🔧 \u003cem\u003e技术校准（AI 专家视角）\u003c/em\u003e：这个二分法在业界有对应的说法。研究 / 模型侧常叫 \u003cstrong\u003eML Research / Deep Learning\u003c/strong\u003e；把模型落地成系统的那一侧，近年被 Chip Huyen 等人明确称为 \u003cstrong\u003eAI Engineering\u003c/strong\u003e，也叫 \u003cstrong\u003eApplied ML / ML Engineering\u003c/strong\u003e。需要注意：两层之间并没有一道墙——微调（fine-tuning）、MLOps、模型压缩这些恰好卡在中间，所以\u0026quot;算法\u0026quot;和\u0026quot;应用\u0026quot;更像光谱的两端，而不是非黑即白。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"对比两条路的难度维度根本不同\"\u003e对比：两条路的难度维度根本不同\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eAI 算法（模型侧）\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eAI 应用开发（工程侧）\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e像什么\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e科研：深、窄、靠硬功\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e工程：广、快、靠整合\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e核心门槛\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e懂模型、懂数学 / 系统\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e懂产品、懂怎么把模型用出价值\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e周期\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e长，迭代慢\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e短，能快速出 demo\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e我的位置\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e门外，只浅尝过\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e正在摸门\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e我自己是游戏 / 客户端工程师出身，\u003cstrong\u003e天然更靠近应用侧\u003c/strong\u003e：我不需要从零训一个大模型，但我能用\u0026quot;API + RAG\u0026quot;很快把一个知识库变成能对话的东西。一度我因此产生一种错觉——\u0026ldquo;原来我也能做 AI 了\u0026rdquo;。后来才意识到，我做的只是应用侧里很薄的一层。\u003c/p\u003e","title":"我对 AI 的一点基础认识（一）：AI 开发可以再分两层"},{"content":"为什么要写这个 跨四个领域的并行学习，如果不把路线拆清楚，很容易陷入\u0026quot;什么都想学、什么都推进不动\u0026quot;的状态。这篇路线图不是一份完美计划，而是一张\u0026quot;此刻的自我定位地图\u0026quot;——知道自己站在哪里、下一站去哪里、哪些路暂时不走。\n四个领域按目标性质可以粗分为两类：产出驱动型和基础建设型。Unity 自定义渲染管线、UE5 Steam 项目是\u0026quot;先做出东西来再说\u0026quot;；GAMES101、OpenGL、机器学习课程则更偏向打基础——短期内不一定出成果，但决定你一年后能走多远。两类要并行，但节奏和验收标准不一样。\n一、Unity：自定义渲染管线 目标 从写到跑通一个完整的 SRP（Scriptable Render Pipeline），理解渲染管线是怎么\u0026quot;串起来\u0026quot;的。\n拆解 从 Built-in RP 切到 URP，先搞清楚 URP 的 Asset/Renderer/RenderPass 分层 手写一个最简单的自定义管线（不继承 URP，自己写 RenderPipeline 子类），跑通最基本的渲染 往管线里加自己的 Pass：后处理、自定义光照、G-buffer 输出 跟已有的 SRP 教程对标一下差异 这里容易翻车的是\u0026quot;一上来就想做大而全的管线\u0026quot;。正确的姿势是先跑通最简版本——一个能渲染纯色 Cuboid 的自定义管线已经值 60 分了，后面的 pass 是往里插的。\n验收 Unity 场景里有一个物体是用你自己的自定义管线渲染的，能切回 Built-in 对比效果。时间不要拖超过两周。\n二、UE5：第一个能上架 Steam 的项目 这个目标本身有歧义，需要先拆清楚——\u0026ldquo;能上架 Steam 程度\u0026quot;不等于\u0026quot;做一个惊天动地的大作\u0026rdquo;，而是：有完整循环（开始→游玩→结束）、没有崩溃级 bug、有商店页面的最小体量游戏。\n方向建议 跟 Unity 的 SRP 学习不太一样——那个偏技术实验，这个偏产品交付。所以项目体量一定要小到能一个人 3-4 个月收尾。具体做什么我不替你做判断，但你心里要有数：如果是俯视角潜行、小型解谜、视觉小说这种 Scope，可行。如果是开放世界第三人称刷刷刷，这里大概率是坑。\n技术要点 Gameplay Ability System 如果是目标项目的核心，值得现在就学 上架流程的坑不在游戏本身，在 Steamworks 接入、打包配置、商店图素材、Depot 上传——这些最好提前两周单独排时间 第一次上架建议走 Steam Direct，准备 100 USD 费用 误区 \u0026ldquo;先做个完整的 Demo 再考虑上架\u0026rdquo;——不是的。做一些核心玩法后就该跑一遍完整的打包和上架流程（用临时名字），确认这个链路没断，再回头完善内容。上架是另一个技能树，不是游戏开发的自然延伸。\n三、图形学 GAMES101 作业 从零到尾写完。这个没什么花哨的，就是硬啃。已知有 Unity 图形基础和一点点数学底子，所以 rasterization 和 transformation 部分可能过得比较快，但光线追踪和蒙特卡洛那几节会卡。卡了就放，先跳下个作业再回头。\n直播美颜算法 这是个很好的\u0026quot;应用驱动图形学习\u0026quot;案例。本质上就是：人脸检测→特征点定位→磨皮（双边滤波/导向滤波）→美白（色彩空间映射）→瘦脸（网格变形）。每一步都是一篇论文变一个 Shader 或 Compute Pass。可以和 URP 自定 Pass 联动——在 Unity 里写美颜后处理。\nOpenGL（未来规划） 不是现在开始学的。等 GAMES101 做完、SRP 跑通之后，再回来学 OpenGL 会更顺。因为到时候你对\u0026quot;一个三角形怎么画到屏幕上\u0026quot;已经有了直觉和理解，OpenGL 只是另一个 API 封装。优先级排在这份计划里最低的位置。\n四、AI / 机器学习 吴恩达机器学习课程 保持节奏继续学。这门课最大的价值不是让它教你怎么写模型，而是建立直觉框架——什么场景该用线性回归、SVM 的核函数在干什么、过拟合的长相。这些直觉在后面读《机器学习》周志华时能少掉很多头发。\n《机器学习》（周志华）和《深度学习》（花书） 这两个要排到吴恩达课程之后。不推荐还没上完课就硬看公式推导——你会把自学本身变成负担。\n误区 AI 领域最容易翻车的是\u0026quot;资料收藏癖\u0026quot;——收藏 50 个教程不如跑通一个 MNIST。这条跟游戏开发不一样，游戏开发的碎片积累是有复利的，AI 学习的碎片积累如果没有动手验证，基本等于零。\n五个任务的时间排布 7月 8月 9月 10月 11月 12月 SRP ████████████ UE5 ████████████████████████████████████ 101 ████████████████ 美颜 ████████████████ 吴恩达 ████████████████████ 不是说你得按这个表严格执行——计划存在的意义是：任何时候你感到\u0026quot;不知道干什么\u0026quot;，就看表上一个没停超过两周的任务，继续推进。\n一旦某个任务连续停了超过两周，就需要判断：是临时中断还是它已经不再是你的兴趣方向了。后者不是失败，撤掉就好。\n该不该做但没放进来的事 Blueprint 可视化脚本、Steamworks SDK 接入细节、网络同步入门、ShaderGraph、Substance Painter 工作流、GitHub Pages SEO 优化——这些要么是具体实现时查文档就能解决的，要么优先级不够。做完上面四块之后，你自然会知道自己缺什么，不需要提前全部列出来。\n","permalink":"https://davinci-13.github.io/posts/2026-learning-roadmap/","summary":"\u003ch2 id=\"为什么要写这个\"\u003e为什么要写这个\u003c/h2\u003e\n\u003cp\u003e跨四个领域的并行学习，如果不把路线拆清楚，很容易陷入\u0026quot;什么都想学、什么都推进不动\u0026quot;的状态。这篇路线图不是一份完美计划，而是一张\u0026quot;此刻的自我定位地图\u0026quot;——知道自己站在哪里、下一站去哪里、哪些路暂时不走。\u003c/p\u003e\n\u003cp\u003e四个领域按目标性质可以粗分为两类：\u003cstrong\u003e产出驱动型\u003c/strong\u003e和\u003cstrong\u003e基础建设型\u003c/strong\u003e。Unity 自定义渲染管线、UE5 Steam 项目是\u0026quot;先做出东西来再说\u0026quot;；GAMES101、OpenGL、机器学习课程则更偏向打基础——短期内不一定出成果，但决定你一年后能走多远。两类要并行，但节奏和验收标准不一样。\u003c/p\u003e\n\u003ch2 id=\"一unity自定义渲染管线\"\u003e一、Unity：自定义渲染管线\u003c/h2\u003e\n\u003ch3 id=\"目标\"\u003e目标\u003c/h3\u003e\n\u003cp\u003e从写到跑通一个完整的 SRP（Scriptable Render Pipeline），理解渲染管线是怎么\u0026quot;串起来\u0026quot;的。\u003c/p\u003e\n\u003ch3 id=\"拆解\"\u003e拆解\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e从 Built-in RP 切到 URP，先搞清楚 URP 的 Asset/Renderer/RenderPass 分层\u003c/li\u003e\n\u003cli\u003e手写一个最简单的自定义管线（不继承 URP，自己写 RenderPipeline 子类），跑通最基本的渲染\u003c/li\u003e\n\u003cli\u003e往管线里加自己的 Pass：后处理、自定义光照、G-buffer 输出\u003c/li\u003e\n\u003cli\u003e跟已有的 SRP 教程对标一下差异\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这里容易翻车的是\u0026quot;一上来就想做大而全的管线\u0026quot;。正确的姿势是先跑通最简版本——一个能渲染纯色 Cuboid 的自定义管线已经值 60 分了，后面的 pass 是往里插的。\u003c/p\u003e\n\u003ch3 id=\"验收\"\u003e验收\u003c/h3\u003e\n\u003cp\u003eUnity 场景里有一个物体是用你自己的自定义管线渲染的，能切回 Built-in 对比效果。时间不要拖超过两周。\u003c/p\u003e\n\u003ch2 id=\"二ue5第一个能上架-steam-的项目\"\u003e二、UE5：第一个能上架 Steam 的项目\u003c/h2\u003e\n\u003cp\u003e这个目标本身有歧义，需要先拆清楚——\u0026ldquo;能上架 Steam 程度\u0026quot;不等于\u0026quot;做一个惊天动地的大作\u0026rdquo;，而是：有完整循环（开始→游玩→结束）、没有崩溃级 bug、有商店页面的最小体量游戏。\u003c/p\u003e\n\u003ch3 id=\"方向建议\"\u003e方向建议\u003c/h3\u003e\n\u003cp\u003e跟 Unity 的 SRP 学习不太一样——那个偏技术实验，这个偏产品交付。所以项目体量一定要小到能一个人 3-4 个月收尾。具体做什么我不替你做判断，但你心里要有数：如果是俯视角潜行、小型解谜、视觉小说这种 Scope，可行。如果是开放世界第三人称刷刷刷，这里大概率是坑。\u003c/p\u003e\n\u003ch3 id=\"技术要点\"\u003e技术要点\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eGameplay Ability System 如果是目标项目的核心，值得现在就学\u003c/li\u003e\n\u003cli\u003e上架流程的坑不在游戏本身，在 Steamworks 接入、打包配置、商店图素材、Depot 上传——这些最好提前两周单独排时间\u003c/li\u003e\n\u003cli\u003e第一次上架建议走 Steam Direct，准备 100 USD 费用\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"误区\"\u003e误区\u003c/h3\u003e\n\u003cp\u003e\u0026ldquo;先做个完整的 Demo 再考虑上架\u0026rdquo;——不是的。做一些核心玩法后就该跑一遍完整的打包和上架流程（用临时名字），确认这个链路没断，再回头完善内容。上架是另一个技能树，不是游戏开发的自然延伸。\u003c/p\u003e","title":"2026 下半年学习路线图"},{"content":"前言 在 UE5 日常开发里，我们经常需要做各种“过渡动画”：\nUI 面板淡入淡出 物体平滑移动和缩放 相机参数渐变 数值从 A 过渡到 B（例如血条、进度条） 这些需求可以用 Timeline、动画蓝图或手写 Tick 实现，但当项目里“轻量过渡动画”越来越多时，代码和蓝图会逐渐分散。Fresh Cooked Tween 这类 Tween 插件的价值，就是把这类逻辑统一成更高复用、可读性更好的工作流。\n1. Fresh Cooked Tween 是什么 Fresh Cooked Tween 是面向 Unreal 的 Tween 动画插件，核心目标是：\n用更简洁的方式描述“某个值在一段时间内如何变化” 提供常见缓动（Easing）曲线 支持回调与链式组织，降低样板代码 一句话理解：它把“插值过程”从零散的 Tick 逻辑中抽离出来，变成结构化的动画任务。\n2. 它解决了什么问题 1) 降低样板代码 手写插值通常要自己管理：\n起始值、目标值 已经过时间 每帧更新 完成回调 中途取消/中断 Tween 插件会把这些通用步骤封装好，让你聚焦“做什么动画”，而不是重复“怎么驱动时间”。\n2) 统一动画语义 当团队都用统一 Tween 风格时，像“EaseOut、Yoyo、Loop、Delay”这些概念更容易沟通。\n3) 适合技术美术协作 对于技术美术和玩法程序协作场景，Tween 非常适合做：\nUI 动效原型 镜头参数微调 交互反馈（按钮弹性、悬停位移、提示浮动） 3. 核心概念（入门必懂） 无论具体 API 名称如何，Tween 工作流通常都围绕以下概念：\nFrom/To：起始值和目标值 Duration：持续时间 Ease：缓动函数（线性、缓入、缓出等） OnUpdate：每帧回调，用于把当前值应用到对象 OnComplete：结束回调 Kill/Cancel：中断 Tween 你可以把 Tween 看作一个“随时间推进的插值任务”。\n4. 常见使用场景 4.1 UI 动画 Widget 的位置、透明度、缩放过渡 弹窗出现/关闭动效 进度条平滑追值 适用理由：UI 动画往往是大量短时、可复用的过渡，Tween 写起来比堆很多 Timeline 更轻。\n4.2 场景物体过渡 门的平滑开启 道具浮动或旋转 交互物高亮参数渐变 4.3 数值驱动动画 血量变化时 UI 跟随平滑变化 镜头 FOV 从 90 过渡到 70 后处理参数平滑切换 5. 与 Timeline / Tick 的对比 什么时候优先 Tween 逻辑是“短时过渡”，且场景里数量多 希望统一动画组织方式 需要快速写出可迭代的动效 什么时候仍可用 Timeline 需要在编辑器里可视化曲线并频繁手调 单个关键动效非常依赖曲线细节 什么时候要谨慎用 Tick 手写 当动画逻辑已高度重复，继续手写 Tick 会造成维护成本上升 仅在性能或行为高度定制时保留手写实现 6. 实战建议（UE5 项目） 1) 给 Tween 生命周期“挂靠对象” 推荐把 Tween 与 Actor/Widget 生命周期关联，避免对象销毁后回调还在执行。\n2) 中断策略要先设计 例如：\n新动画开始前是否自动 Kill 旧 Tween 状态切换（例如切场景）时是否批量取消 3) 命名与分层 建议按用途组织：\nUI Tween Gameplay Tween Camera Tween 并把通用动效封装成函数库，避免散落在各个蓝图事件里。\n4) 联机项目注意职责边界 Tween 一般只负责“表现层过渡”。\n真实状态变化仍由服务器权威决定 客户端 Tween 做平滑展示，不应替代网络同步逻辑 7. 新手常见误区 把 Tween 当状态机：Tween 适合过渡，不适合复杂状态流程编排。 忘记中断：同一属性上叠多个 Tween 导致抖动或抢写。 忽略对象销毁：对象没了，回调还在跑，容易出现空引用问题。 过度追求“全 Tween 化”：关键系统仍需保持清晰的业务逻辑边界。 小结 Fresh Cooked Tween 的核心价值，不只是“动画更丝滑”，而是把大量重复的插值逻辑标准化。\n对于 UE5 开发者，尤其是需要兼顾玩法与技术美术协作的团队，它能显著提升：\n动效迭代速度 脚本可读性 过渡逻辑复用度 如果你正在把项目从“零散 Timeline + 手写 Tick”逐步升级到可维护的动效体系，Tween 插件通常是性价比很高的一步。\n后续可以继续写 Fresh Cooked Tween 在蓝图中的实战模板（UI 弹窗、按钮反馈、血条追值）。 Fresh Cooked Tween 在 C++ 中的封装思路（统一入口 + 自动回收）。 Tween、Timeline、动画蓝图在中大型项目里的分层方案。 ","permalink":"https://davinci-13.github.io/posts/fresh-cooked-tween-intro/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e在 UE5 日常开发里，我们经常需要做各种“过渡动画”：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUI 面板淡入淡出\u003c/li\u003e\n\u003cli\u003e物体平滑移动和缩放\u003c/li\u003e\n\u003cli\u003e相机参数渐变\u003c/li\u003e\n\u003cli\u003e数值从 A 过渡到 B（例如血条、进度条）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这些需求可以用 Timeline、动画蓝图或手写 Tick 实现，但当项目里“轻量过渡动画”越来越多时，代码和蓝图会逐渐分散。Fresh Cooked Tween 这类 Tween 插件的价值，就是把这类逻辑统一成更高复用、可读性更好的工作流。\u003c/p\u003e\n\u003ch2 id=\"1-fresh-cooked-tween-是什么\"\u003e1. Fresh Cooked Tween 是什么\u003c/h2\u003e\n\u003cp\u003eFresh Cooked Tween 是面向 Unreal 的 Tween 动画插件，核心目标是：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e用更简洁的方式描述“某个值在一段时间内如何变化”\u003c/li\u003e\n\u003cli\u003e提供常见缓动（Easing）曲线\u003c/li\u003e\n\u003cli\u003e支持回调与链式组织，降低样板代码\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e一句话理解：它把“插值过程”从零散的 Tick 逻辑中抽离出来，变成结构化的动画任务。\u003c/p\u003e\n\u003ch2 id=\"2-它解决了什么问题\"\u003e2. 它解决了什么问题\u003c/h2\u003e\n\u003ch3 id=\"1-降低样板代码\"\u003e1) 降低样板代码\u003c/h3\u003e\n\u003cp\u003e手写插值通常要自己管理：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e起始值、目标值\u003c/li\u003e\n\u003cli\u003e已经过时间\u003c/li\u003e\n\u003cli\u003e每帧更新\u003c/li\u003e\n\u003cli\u003e完成回调\u003c/li\u003e\n\u003cli\u003e中途取消/中断\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTween 插件会把这些通用步骤封装好，让你聚焦“做什么动画”，而不是重复“怎么驱动时间”。\u003c/p\u003e\n\u003ch3 id=\"2-统一动画语义\"\u003e2) 统一动画语义\u003c/h3\u003e\n\u003cp\u003e当团队都用统一 Tween 风格时，像“EaseOut、Yoyo、Loop、Delay”这些概念更容易沟通。\u003c/p\u003e\n\u003ch3 id=\"3-适合技术美术协作\"\u003e3) 适合技术美术协作\u003c/h3\u003e\n\u003cp\u003e对于技术美术和玩法程序协作场景，Tween 非常适合做：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUI 动效原型\u003c/li\u003e\n\u003cli\u003e镜头参数微调\u003c/li\u003e\n\u003cli\u003e交互反馈（按钮弹性、悬停位移、提示浮动）\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"3-核心概念入门必懂\"\u003e3. 核心概念（入门必懂）\u003c/h2\u003e\n\u003cp\u003e无论具体 API 名称如何，Tween 工作流通常都围绕以下概念：\u003c/p\u003e","title":"动画插件 Fresh Cooked Tween 入门：让 UE5 动画插值更顺手"},{"content":"前言 在 UE5 C++ 开发里，“选对父类”几乎决定了脚本的生命周期、可放置位置、初始化时机，以及你能用到哪些系统能力（输入、碰撞、网络复制、UI、动画等）。\n这篇文章按“实战常用频率”整理：\n常见父类分别适合做什么。 每类对象的核心生命周期顺序。 常用属性、函数和委托（含高频回调）。 说明：本文聚焦入门和日常开发高频内容，细节会因引擎版本和项目架构（单机/联机）略有差异。\n1. UObject：一切反射对象的基础 适用场景 纯数据对象 工具类对象 不需要直接出现在场景中的逻辑对象 生命周期要点 Constructor：构造默认值。 PostInitProperties：属性初始化后调用。 BeginDestroy / FinishDestroy：销毁流程。 常用属性/函数 GetWorld()：部分上下文可用，不是所有 UObject 都有有效世界。 GetOuter()：获取所属外部对象。 IsValid()（配合对象指针检查）：避免访问失效对象。 常见委托 UObject 本身没有像 Actor 那样丰富的生命周期委托，但可以参与你自定义的动态委托系统。\n2. AActor：关卡中最核心的可放置对象 适用场景 场景实体（可见或不可见） 可复制的游戏逻辑载体 挂组件、处理交互、触发器、机关等 生命周期（常见顺序） Constructor OnConstruction（编辑器放置或属性变更后触发） PreInitializeComponents PostInitializeComponents BeginPlay Tick（启用时每帧） EndPlay Destroyed 常用属性 PrimaryActorTick.bCanEverTick：是否允许 Tick。 bReplicates：是否参与网络复制。 NetUpdateFrequency：网络更新频率。 Tags：对象标签，快速分类检索。 常用函数 BeginPlay() / Tick(float DeltaSeconds) / EndPlay() SetActorLocation() / SetActorRotation() / SetActorTransform() Destroy() HasAuthority()：联机场景判断服务器权限。 GetComponentsByClass()：运行时检索组件。 常见委托 OnActorBeginOverlap OnActorEndOverlap OnTakeAnyDamage OnDestroyed 3. APawn 与 ACharacter：可控制角色核心 3.1 APawn 适用场景 可被 Controller 控制的对象 载具、飞行器、棋子等不一定是“人形角色”的可控实体 生命周期要点 继承 AActor 生命周期，同时常见控制链路是：\nPossessedBy(AController*)：被服务器控制器占有 UnPossessed()：解除占有 SetupPlayerInputComponent()：绑定输入（常见于玩家 Pawn） 常用属性/函数 AutoPossessPlayer / AutoPossessAI GetController() / GetPawnViewLocation() AddMovementInput() 常见委托 Pawn 自身内置公开委托较少，项目里通常通过组件或自定义委托广播状态变化。\n3.2 ACharacter ACharacter 是 APawn 的高频子类，内置：\nCapsuleComponent CharacterMovementComponent Mesh 适用场景 人形角色 需要走跑跳、重力、坡度、网络移动预测 生命周期/回调补充 Landed(const FHitResult\u0026amp;)：落地事件 Jump() / StopJumping() OnMovementModeChanged()：移动模式切换 常用属性/函数 GetCharacterMovement() bUseControllerRotationYaw GetMesh() 常见委托（常配合组件） 角色受伤/死亡常通过自定义委托或 Ability 系统委托实现 角色重叠事件常来自 Capsule 的 OnComponentBeginOverlap 4. AController / APlayerController / AAIController：控制层 适用场景 输入转发、相机控制、UI 交互（PlayerController） AI 决策与行为树驱动（AIController） 生命周期要点 BeginPlay OnPossess(APawn*) OnUnPossess() Tick 常用属性/函数 GetPawn() / SetViewTarget() APlayerController::GetHUD() APlayerController::ClientTravel() AAIController::RunBehaviorTree() 常见委托 OnPossessedPawnChanged（常见于 PlayerController 使用场景） 5. UActorComponent 与 USceneComponent：可复用能力模块 5.1 UActorComponent 适用场景 为 Actor 提供可复用能力（属性系统、锁定系统、交互系统等） 生命周期（常见） Constructor OnRegister InitializeComponent BeginPlay TickComponent EndPlay OnUnregister 常用属性/函数 PrimaryComponentTick.bCanEverTick GetOwner() SetComponentTickEnabled() 常见委托 多数委托由具体子类组件提供（例如碰撞组件、生命组件等） 5.2 USceneComponent USceneComponent 在 UActorComponent 基础上增加了变换层级能力。\n常用函数 SetupAttachment() AttachToComponent() GetComponentLocation() SetRelativeLocation() / SetWorldLocation() 6. UPrimitiveComponent（及常见子类）：碰撞与可见性核心 常见子类 UStaticMeshComponent USkeletalMeshComponent UBoxComponent / USphereComponent / UCapsuleComponent 生命周期要点 继承组件生命周期，运行期高频关注“注册后可参与渲染/碰撞”。\n常用属性/函数 SetCollisionEnabled() SetGenerateOverlapEvents() SetSimulatePhysics() SetVisibility() 常见委托 OnComponentBeginOverlap OnComponentEndOverlap OnComponentHit 7. UUserWidget：UI 脚本核心基类 适用场景 HUD 菜单 背包、任务、提示等界面 生命周期（常见） NativeOnInitialized NativePreConstruct NativeConstruct NativeTick NativeDestruct 常用属性/函数 AddToViewport() / RemoveFromParent() GetOwningPlayer() BindWidget（通过 UPROPERTY 绑定蓝图控件） 常见委托 按钮点击：OnClicked 复选框：OnCheckStateChanged 滑条：OnValueChanged 8. AGameModeBase / AGameStateBase / APlayerState：规则与状态分层 AGameModeBase 服务器侧规则核心（客户端通常拿不到 GameMode） 常用函数：StartPlay()、PostLogin(APlayerController*) AGameStateBase 用于跨客户端同步的全局对局状态 常用：倒计时、回合状态、比赛阶段 APlayerState 玩家层面的可复制状态（分数、队伍、名称等） 角色死亡重生后常仍保留 PlayerState 生命周期提示 联机下要明确“规则在 GameMode，展示/同步在 GameState/PlayerState”。 9. 委托速查：最常见三类 1) 原生委托（非动态） 性能更好 不能直接暴露给蓝图 2) 动态委托（Dynamic） 可被反射系统识别 可用于蓝图绑定 通常比原生委托开销更高 3) 多播委托（Multicast） 一次广播多个监听者 常用于事件总线式解耦 示例（动态多播声明）：\nDECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChanged, float, NewHealth); UPROPERTY(BlueprintAssignable, Category = \u0026#34;Event\u0026#34;) FOnHealthChanged OnHealthChanged; 10. 新手最容易踩的生命周期问题 在 Constructor 里访问运行时对象（World、PlayerController）导致空指针。 组件还没注册就读碰撞/物理状态，结果和预期不一致。 把初始化全部塞进 BeginPlay，忽略了 OnConstruction 在编辑器下的价值。 联机逻辑没区分 Authority，导致客户端和服务器重复执行。 在委托解绑上遗漏 RemoveDynamic / RemoveAll，引发悬空回调。 小结 UE5 C++ 常用父类可以粗分为：\n数据与工具层：UObject 场景实体层：AActor、APawn、ACharacter 控制层：AController、APlayerController、AAIController 复用能力层：UActorComponent、USceneComponent、UPrimitiveComponent 界面层：UUserWidget 对局规则与状态层：AGameModeBase、AGameStateBase、APlayerState 掌握这些基类的“职责 + 生命周期 + 常用委托”，基本就能搭建大多数游戏玩法脚本框架。\n后续可继续写：\nUE5 C++ 生命周期时序图（从对象创建到销毁）。 联机场景下 GameMode/GameState/PlayerState 的数据流设计。 角色系统实战：Character + Component + Widget 的解耦范式。 ","permalink":"https://davinci-13.github.io/posts/ue5-cpp-common-base-classes-lifecycle/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e在 UE5 C++ 开发里，“选对父类”几乎决定了脚本的生命周期、可放置位置、初始化时机，以及你能用到哪些系统能力（输入、碰撞、网络复制、UI、动画等）。\u003c/p\u003e\n\u003cp\u003e这篇文章按“实战常用频率”整理：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e常见父类分别适合做什么。\u003c/li\u003e\n\u003cli\u003e每类对象的核心生命周期顺序。\u003c/li\u003e\n\u003cli\u003e常用属性、函数和委托（含高频回调）。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp\u003e说明：本文聚焦入门和日常开发高频内容，细节会因引擎版本和项目架构（单机/联机）略有差异。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1-uobject一切反射对象的基础\"\u003e1. UObject：一切反射对象的基础\u003c/h2\u003e\n\u003ch3 id=\"适用场景\"\u003e适用场景\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e纯数据对象\u003c/li\u003e\n\u003cli\u003e工具类对象\u003c/li\u003e\n\u003cli\u003e不需要直接出现在场景中的逻辑对象\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"生命周期要点\"\u003e生命周期要点\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eConstructor\u003c/code\u003e：构造默认值。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePostInitProperties\u003c/code\u003e：属性初始化后调用。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBeginDestroy\u003c/code\u003e / \u003ccode\u003eFinishDestroy\u003c/code\u003e：销毁流程。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"常用属性函数\"\u003e常用属性/函数\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eGetWorld()\u003c/code\u003e：部分上下文可用，不是所有 \u003ccode\u003eUObject\u003c/code\u003e 都有有效世界。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eGetOuter()\u003c/code\u003e：获取所属外部对象。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eIsValid()\u003c/code\u003e（配合对象指针检查）：避免访问失效对象。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"常见委托\"\u003e常见委托\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eUObject\u003c/code\u003e 本身没有像 Actor 那样丰富的生命周期委托，但可以参与你自定义的动态委托系统。\u003c/p\u003e\n\u003ch2 id=\"2-aactor关卡中最核心的可放置对象\"\u003e2. AActor：关卡中最核心的可放置对象\u003c/h2\u003e\n\u003ch3 id=\"适用场景-1\"\u003e适用场景\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e场景实体（可见或不可见）\u003c/li\u003e\n\u003cli\u003e可复制的游戏逻辑载体\u003c/li\u003e\n\u003cli\u003e挂组件、处理交互、触发器、机关等\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"生命周期常见顺序\"\u003e生命周期（常见顺序）\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003eConstructor\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOnConstruction\u003c/code\u003e（编辑器放置或属性变更后触发）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePreInitializeComponents\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePostInitializeComponents\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBeginPlay\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eTick\u003c/code\u003e（启用时每帧）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eEndPlay\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDestroyed\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"常用属性\"\u003e常用属性\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ePrimaryActorTick.bCanEverTick\u003c/code\u003e：是否允许 Tick。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ebReplicates\u003c/code\u003e：是否参与网络复制。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eNetUpdateFrequency\u003c/code\u003e：网络更新频率。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eTags\u003c/code\u003e：对象标签，快速分类检索。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"常用函数\"\u003e常用函数\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eBeginPlay()\u003c/code\u003e / \u003ccode\u003eTick(float DeltaSeconds)\u003c/code\u003e / \u003ccode\u003eEndPlay()\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eSetActorLocation()\u003c/code\u003e / \u003ccode\u003eSetActorRotation()\u003c/code\u003e / \u003ccode\u003eSetActorTransform()\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDestroy()\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eHasAuthority()\u003c/code\u003e：联机场景判断服务器权限。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eGetComponentsByClass()\u003c/code\u003e：运行时检索组件。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"常见委托-1\"\u003e常见委托\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eOnActorBeginOverlap\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOnActorEndOverlap\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOnTakeAnyDamage\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOnDestroyed\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"3-apawn-与-acharacter可控制角色核心\"\u003e3. APawn 与 ACharacter：可控制角色核心\u003c/h2\u003e\n\u003ch2 id=\"31-apawn\"\u003e3.1 APawn\u003c/h2\u003e\n\u003ch3 id=\"适用场景-2\"\u003e适用场景\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e可被 Controller 控制的对象\u003c/li\u003e\n\u003cli\u003e载具、飞行器、棋子等不一定是“人形角色”的可控实体\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"生命周期要点-1\"\u003e生命周期要点\u003c/h3\u003e\n\u003cp\u003e继承 \u003ccode\u003eAActor\u003c/code\u003e 生命周期，同时常见控制链路是：\u003c/p\u003e","title":"UE5 C++ 脚本常用父类：生命周期、常用属性/函数/委托速览"},{"content":"前言 在 Unreal Engine 5 的 C++ 开发中，除了标准 C++ 类型，还会频繁接触 UE 自己封装的一套类型系统。本文作为入门草稿，先把最常用、最容易混淆的类型梳理清楚。\n1. 数值与布尔类型 UE5 中可以直接使用标准 C++ 基本类型，但官方代码风格更常见的是固定宽度别名：\nint8 / uint8 int16 / uint16 int32 / uint32 int64 / uint64 float double bool 示例：\nint32 Health = 100; float MoveSpeed = 600.0f; bool bIsAlive = true; 说明：\nint32 在 UE 项目中非常常用，跨平台行为更稳定。 uint8 常用于小范围状态值或位标记。 布尔变量通常以 b 开头（如 bIsAlive），是 UE 常见命名习惯。 2. 字符串相关类型 UE 里最常见的文本类型有 3 个：FString、FName、FText。\n为什么不建议在 UE5 C++ 脚本里优先用 C++ 原生 std::string：\n与 UE 反射系统和编辑器生态耦合较弱，不能直接享受 UPROPERTY、蓝图暴露和常见序列化工作流。 与 UE 常用 API 对接时通常还要频繁做类型转换（std::string \u0026lt;-\u0026gt; FString / FText），会增加样板代码和维护成本。 本地化场景下，FText 有完整的文本管线支持；std::string 更适合纯底层逻辑，不适合直接做玩家可见文本。 UE 社区和官方示例主要围绕 FString、FName、FText，团队协作和排查问题时可读性更高。 FString 可变字符串，适合拼接、调试输出、运行时处理。 类似“通用字符串容器”。 FString PlayerName = TEXT(\u0026#34;DaVinci\u0026#34;); FString Msg = FString::Printf(TEXT(\u0026#34;玩家: %s\u0026#34;), *PlayerName); FName 不区分大小写语义（底层做了名称表优化）。 适合做标识符、标签、键值名、资源名。 频繁比较时通常比 FString 更高效。 FName SocketName = TEXT(\u0026#34;WeaponSocket\u0026#34;); FText 面向本地化显示文本（UI 文本优先用它）。 需要给玩家看的文本，优先考虑 FText。 FText DisplayText = FText::FromString(TEXT(\u0026#34;开始游戏\u0026#34;)); 3. 常用数学结构体 UE5 提供了大量数学类型，最常见的是：\nFVector：三维向量（位置、方向、速度） FRotator：欧拉角旋转（Pitch/Yaw/Roll） FQuat：四元数旋转 FTransform：位置 + 旋转 + 缩放组合 FColor / FLinearColor：颜色 FVector Location(0.0f, 0.0f, 100.0f); FRotator Rotation(0.0f, 90.0f, 0.0f); FTransform SpawnTransform(Rotation, Location); 4. 常用容器类型 UE 有自己的模板容器，最常见是：\nTArray\u0026lt;T\u0026gt;：动态数组 TMap\u0026lt;Key, Value\u0026gt;：键值映射 TSet\u0026lt;T\u0026gt;：集合（去重） 为什么不建议在 UE5 C++ 脚本里优先用原生 std::array / std::list 等容器：\nUE 容器与引擎内存管理、反射和序列化工具链配合更完整，尤其在编辑器可视化、保存和网络同步场景更友好。 很多 UE API（例如常见 Gameplay、蓝图桥接、工具函数）默认使用 TArray/TMap/TSet，使用 STL 容器时经常要做中转转换。 std::list 在游戏场景里通常不是高频首选，节点分散会降低缓存命中率；TArray 在遍历密集场景里往往更实用。 UE 项目代码风格通常以 UE 容器为主，统一使用能降低沟通成本并减少“容器混用”带来的 bug 风险。 TArray\u0026lt;int32\u0026gt; Scores; Scores.Add(100); Scores.Add(200); TMap\u0026lt;FName, int32\u0026gt; AttrMap; AttrMap.Add(TEXT(\u0026#34;HP\u0026#34;), 100); AttrMap.Add(TEXT(\u0026#34;MP\u0026#34;), 50); 5. UObject 指针相关类型（入门版） 在 UE5 中，和 UObject 体系交互时，指针类型选择非常重要：\nTObjectPtr\u0026lt;T\u0026gt;：UE5 推荐的 UObject 成员引用方式 TWeakObjectPtr\u0026lt;T\u0026gt;：弱引用，不阻止对象被 GC TSoftObjectPtr\u0026lt;T\u0026gt;：软引用，可延迟加载资源 原始指针 T*：仍可使用，但要注意生命周期与空指针检查 UPROPERTY(EditAnywhere) TObjectPtr\u0026lt;class UStaticMesh\u0026gt; WeaponMesh; 6. 与反射系统配合：UPROPERTY / UFUNCTION 如果变量要在编辑器中可见、可序列化、可复制或暴露给蓝图，通常需要 UPROPERTY。\nUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \u0026#34;Stats\u0026#34;) int32 MaxHealth = 100; 常见要点：\n只写 C++ 变量但不加 UPROPERTY，编辑器里通常看不到。 需要蓝图访问时，常用 BlueprintReadWrite 或 BlueprintReadOnly。 分类用 Category，方便在 Details 面板整理。 7. 新手易踩坑 UI 文本直接用 FString：后续本地化成本高，优先 FText。 把所有标识都用 FString：很多场景 FName 更合适。 忽略空指针检查：尤其在异步加载、对象销毁后访问时。 容器里放 UObject 原始指针却不考虑生命周期：容易产生悬空引用。 小结 UE5 C++ 的“基本类型”不只是 C++ 原生类型，更包括 UE 的字符串、数学结构体、容器和 UObject 指针体系。掌握这些后，再学习反射系统（UPROPERTY / UFUNCTION）和内存管理机制，会更容易写出稳定的游戏逻辑代码。\n后续可以继续写：\nUE5 C++ 中 UPROPERTY 常用元数据速查。 TObjectPtr、TWeakObjectPtr、TSoftObjectPtr 的实战对比。 从蓝图迁移到 C++ 时的类型映射与重构策略。 ","permalink":"https://davinci-13.github.io/posts/ue5-cpp-basic-types/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e在 Unreal Engine 5 的 C++ 开发中，除了标准 C++ 类型，还会频繁接触 UE 自己封装的一套类型系统。本文作为入门草稿，先把最常用、最容易混淆的类型梳理清楚。\u003c/p\u003e\n\u003ch2 id=\"1-数值与布尔类型\"\u003e1. 数值与布尔类型\u003c/h2\u003e\n\u003cp\u003eUE5 中可以直接使用标准 C++ 基本类型，但官方代码风格更常见的是固定宽度别名：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eint8\u003c/code\u003e / \u003ccode\u003euint8\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint16\u003c/code\u003e / \u003ccode\u003euint16\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint32\u003c/code\u003e / \u003ccode\u003euint32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint64\u003c/code\u003e / \u003ccode\u003euint64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003efloat\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edouble\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ebool\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e示例：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eint32\u003c/span\u003e \u003cspan class=\"n\"\u003eHealth\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003efloat\u003c/span\u003e \u003cspan class=\"n\"\u003eMoveSpeed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mf\"\u003e600.0f\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e \u003cspan class=\"n\"\u003ebIsAlive\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e说明：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eint32\u003c/code\u003e 在 UE 项目中非常常用，跨平台行为更稳定。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint8\u003c/code\u003e 常用于小范围状态值或位标记。\u003c/li\u003e\n\u003cli\u003e布尔变量通常以 \u003ccode\u003eb\u003c/code\u003e 开头（如 \u003ccode\u003ebIsAlive\u003c/code\u003e），是 UE 常见命名习惯。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"2-字符串相关类型\"\u003e2. 字符串相关类型\u003c/h2\u003e\n\u003cp\u003eUE 里最常见的文本类型有 3 个：\u003ccode\u003eFString\u003c/code\u003e、\u003ccode\u003eFName\u003c/code\u003e、\u003ccode\u003eFText\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e为什么不建议在 UE5 C++ 脚本里优先用 C++ 原生 \u003ccode\u003estd::string\u003c/code\u003e：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e与 UE 反射系统和编辑器生态耦合较弱，不能直接享受 \u003ccode\u003eUPROPERTY\u003c/code\u003e、蓝图暴露和常见序列化工作流。\u003c/li\u003e\n\u003cli\u003e与 UE 常用 API 对接时通常还要频繁做类型转换（\u003ccode\u003estd::string\u003c/code\u003e \u0026lt;-\u0026gt; \u003ccode\u003eFString\u003c/code\u003e / \u003ccode\u003eFText\u003c/code\u003e），会增加样板代码和维护成本。\u003c/li\u003e\n\u003cli\u003e本地化场景下，\u003ccode\u003eFText\u003c/code\u003e 有完整的文本管线支持；\u003ccode\u003estd::string\u003c/code\u003e 更适合纯底层逻辑，不适合直接做玩家可见文本。\u003c/li\u003e\n\u003cli\u003eUE 社区和官方示例主要围绕 \u003ccode\u003eFString\u003c/code\u003e、\u003ccode\u003eFName\u003c/code\u003e、\u003ccode\u003eFText\u003c/code\u003e，团队协作和排查问题时可读性更高。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"fstring\"\u003eFString\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e可变字符串，适合拼接、调试输出、运行时处理。\u003c/li\u003e\n\u003cli\u003e类似“通用字符串容器”。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFString\u003c/span\u003e \u003cspan class=\"n\"\u003ePlayerName\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eTEXT\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;DaVinci\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFString\u003c/span\u003e \u003cspan class=\"n\"\u003eMsg\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFString\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"n\"\u003ePrintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTEXT\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;玩家: %s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"n\"\u003ePlayerName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"fname\"\u003eFName\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e不区分大小写语义（底层做了名称表优化）。\u003c/li\u003e\n\u003cli\u003e适合做标识符、标签、键值名、资源名。\u003c/li\u003e\n\u003cli\u003e频繁比较时通常比 \u003ccode\u003eFString\u003c/code\u003e 更高效。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFName\u003c/span\u003e \u003cspan class=\"n\"\u003eSocketName\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eTEXT\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;WeaponSocket\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"ftext\"\u003eFText\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e面向本地化显示文本（UI 文本优先用它）。\u003c/li\u003e\n\u003cli\u003e需要给玩家看的文本，优先考虑 \u003ccode\u003eFText\u003c/code\u003e。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFText\u003c/span\u003e \u003cspan class=\"n\"\u003eDisplayText\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFText\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"n\"\u003eFromString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTEXT\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;开始游戏\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"3-常用数学结构体\"\u003e3. 常用数学结构体\u003c/h2\u003e\n\u003cp\u003eUE5 提供了大量数学类型，最常见的是：\u003c/p\u003e","title":"UE5 C++ 脚本的基本类型"},{"content":"Introduction This is bold text, and this is emphasized text.\nVisit the Hugo website!\n","permalink":"https://davinci-13.github.io/posts/my-first-post/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eThis is \u003cstrong\u003ebold\u003c/strong\u003e text, and this is \u003cem\u003eemphasized\u003c/em\u003e text.\u003c/p\u003e\n\u003cp\u003eVisit the \u003ca href=\"https://gohugo.io\"\u003eHugo\u003c/a\u003e website!\u003c/p\u003e","title":"My First Post"}]