UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - 厚积薄发
type: channel
link: https://blog.uwa4d.com/category/technology
description: UWA旨在诊断与优化游戏和VR项目开发过程中潜在的运行性能问题，帮助项研发团队快速检测项目运行时的性能瓶颈，高效定位问题根源，及时反馈针对瓶颈问题的深度分析和解决方法。
items: 为何iPad Pro上设置目标帧率为90时无法生效
items: 游戏AI行为决策——MLP（多层感知机/人工神经网络）
items: 导致Resources文件夹的资源在Android打包后丢失的原因
items: 为什么Android游戏画面在30帧运行时有抖动现象
items: 对Android游戏画面抖动现象的研究
items: 有什么指标可以判断手机是否降频
items: iOS框架内存中占用很高的ttc文件是否正常
items: 游戏AI行为决策——GOAP（目标导向型行为规划）
items: TextureStreaming针对不同分档机型一般怎么设置
items: 游戏在高负载场景下，整机功耗控制在多少

为何iPad Pro上设置目标帧率为90时无法生效
type: item
date: 2025-11-11T04:22:00+02:00
description: 1）为何iPad Pro上设置目标帧率为90时无法生效 2）为什么在Dedicated Server发布的程序中内存数值异常 这是第452篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From 问答社区 Q1：为什么iPad Pro上设置目标帧率为90时无法生效，仍然以120帧运行？ 在使用Application.targetFrameRate设置目标帧率为90时，在iPad Pro M2芯片上无法生效，目标帧率变为120，在iPhone 14 Pro上测试则能够以80的帧率运行。 在和平精英上用PerfDog采集，可以设置为90的选项，实际以80的帧率在跑，但在Demo中测试无法达到同样的效果。 A：使用UWA AI问答的功能尝试解读这个问题。 分享链接： https://www.uwa4d.com/main/uwa-shared.html?shareId=f3ea7cc1-879f-4f94-aeb5-b29860099ae2 该问题通常由垂直同步（VSync）与帧率控制机制的交互导致，尤其是在高刷新率设备如iPad Pro上。 核心原因分析 在Unity引擎中，Application.targetFrameRate是控制移动平台帧率的主要方式。然而，当垂直同步（VSync）开启时，它会优先于targetFrameRate生效。系统会强制将应用的帧率锁定到屏幕刷新率的整数分之一（如120Hz、60Hz、40Hz等），以避免画面撕裂。因此，即使设置了targetFrameRate=90，系统也无法稳定输出非刷新率整除的帧率（90不是120的整除因子），最终会退回到设备支持的最高帧率（120fps）或下一个可行的分频值。 此外，部分iOS设备（尤其是支持ProMotion技术的iPad Pro）具有动态刷新率调节能力，其底层图形驱动可能进一步影响帧率锁定行为，使得targetFrameRate的控制不够精确。 解决方案 1. 关闭VSync并依赖 targetFrameRate 确保在项目设置或运行时关闭垂直同步： QualitySettings.vSyncCount = 0; // 关闭VSync Application.targetFrameRate = 90; // 设置目标帧率为90 这是实现非标准帧率（如90fps）最直接的方法。关闭VSync后，targetFrameRate将正常生效。 2. 使用引擎级帧平滑（Frame Pacing） 在使用虚幻引擎时，可通过CVar精确控制帧率。对于目标帧率90，在配置文件中设置： r.setframepace 90 此方法直接对接设备刷新周期，能更可靠地在支持的设备上实现90fps。 3. 验证设备与系统支持 确认当前iPad Pro型号及iOS系统版本是否完整支持90Hz的输出模式。某些情况下，操作系统或驱动可能限制了非标准刷新率的使用。 4. 监控实际帧率 使用UWA GOT Online报告中的“FPS均值”和“>40ms帧数占比”指标，结合真机性能测试，验证帧率是否真正稳定在90fps，并评估GPU负载与功耗表现。 综上，欲在iPad Pro上稳定运行90fps，关键在于关闭VSync并通过Application.targetFrameRate明确指定目标帧率，同时确保引擎和设备层面的支持。 Q2：如果已经关闭垂直同步，还可能是什么原因？ A：即使已关闭垂直同步（VSync），在iPad Pro上设置Application.targetFrameRate = 90仍可能无法生效，原因如下： 1. 动态刷新率机制（ProMotion）的底层限制 iPad Pro搭载的ProMotion技术允许屏幕刷新率在120Hz、60Hz、40Hz、30Hz等特定档位间动态切换。系统倾向于选择最接近目标帧率的标准分频值。由于90不是120的整除因子，系统可能无法为90fps创建稳定的刷新周期，最终退回到默认的120Hz模式。 2. Unity引擎的帧时间计算与平台适配问题 根据Unity文档，移动平台上的targetFrameRate实际效果受限于屏幕刷新率的整数分频。即使VSync关闭，Unity的帧调度器仍可能因无法精确匹配90Hz的间隔而出现抖动或回落至120Hz。此外，若未将targetFrameRate设置为屏幕刷新率的约数（如120 → 60, 40, 30），系统可能忽略该设置。 3. 后台进程或系统策略干预 iOS系统可能会因性能管理、温控或后台任务（如应用更新、索引服务）临时提升设备性能至最大刷…

游戏AI行为决策——MLP（多层感知机/人工神经网络）
type: item
date: 2025-11-06T04:22:00+02:00
description: 【USparkle专栏】如果你深怀绝技，爱“搞点研究”，乐于分享也博采众长，我们期待你的加入，让智慧的火花碰撞交织，让知识的传递生生不息！ 你一定听说过神经网络的大名，你有想过将它用于游戏AI的行为决策上吗？其实在（2010年发布的）《最高指挥官2》中就有应用了，今天请允许我班门弄斧一番，与大家一同用C# 实现最经典的神经网络 —— 多层感知机（Multilayer Perceptron，简称MLP）。 一、前言 神经网络或者深度学习，总给人一种「量子力学」的感觉，总感觉它神秘无比，又无所不能。我未学习神经网络之前，总以为它是某种能够修改自身代码的代码，否则怎么能做到从「不会」变成「会」的呢？但在亲自学习后才会明白，它并没有做到这种地步，但依旧十分神奇。多层感知机是最基础的神经网络，很多其它类别的神经网络都是在这之上的变形。可以说，学会它是迈入深度学习的第一步。 多层感知机虽说经典，但并不过时。提到神经网络，大多数人脑海里想到的大概也就是类似这样的图片： 这就是一张典型的多层感知机结构图，看着好像很复杂，但实现它所需要用到的数学原理和编程知识都不难，早年间，研究神经网络的学者们还用C语言实现呢！ 二、什么是多层感知机 现在进入正题，我们先来简单讲讲MLP的原理（如果你对此十分熟悉，只是对代码实现感兴趣，那可以跳过这部分）。 既然叫「多层感知机」，那有单个的感知机吗？那是自然，单个感知机的结构十分简单： 它其实就是个算式（为方便理解，我将其分成两部分）： 将传入感知机的多个输入x，与对应的权重w相乘（输入的数量与权重的数量是一样的，且数量是任意的，本例中用了3个），再加上偏置b就可以得出一个计算值sum。再将这个计算值传入一个函数f(x)就可得到感知机的最终输出out。 相信你肯定能理解，只是可能对f(x)函数有些好奇，它具体内容是什么呢？这个函数也被称为「激活函数」，为什么叫这个名字？这就得提感知机的另一个名字 —— 人工神经元。其实感知机正是受神经元结构启发而被提出来的： 神经元会通过树突接受输入信号并汇总，而神经元对于各个输入刺激的响应强度并不相同，所以我们给各个输入设置了相应权重来模拟这个现象。之后，将处理的信息通过轴突传给末梢（终端）。但实际上，只有汇总的信号强度大于一定程度时神经元才会向末梢传递信号，而模拟这个现象的就是「激活函数」。 那偏置b又是模拟什么的？其实它是从数学角度考虑的、方便调整输入加权和的变量值而已。 既然感知机被称为「人工神经元」，那多层感知机岂不就是「人工神经网络」？一点没错，我们现在所说的「神经网络」，基本都是指人工神经网络，而不是真正的、生物的神经网络。而「神经网络」起初就是指多层感知机，只不过现在种类多了，定义也变宽泛了。 结合我们对单个感知机的认识，再看看多层感知机： 但这里的单个感知机（后面用「神经元」来代称）怎么输出了多个值（看标蓝色的那部分）？这种结构图可能会误导某些人，我做个解释，这里的每一条线并不是输出，可以看到它是有箭头的，每条线表示它所指向的那个神经元的一个权重。加上箭头是为了表示数据传递的方向。 不难看出，它就是将多个感知机以层为单位进行了组合，每层都有任意数量（每层的数量可以不同）的感知机，并将一层感知机的输出作为下一层的输入，依次套娃下去。像图中，下一层神经元的权重数量 = 上一层的神经元数量，称为全连接，是神经网络中常见的连接方式，本文也只考虑这种连接方式。 这里的「输入层」其实就是输入的数据（是的，这一层不是神经元），类似之前的x0、x1、x2；「输出层」就是用于输出的神经元所组成的层，有了多个感知机，我们也可以得到多个输出；夹在「输入层」与「输出层」之间的就叫「隐藏层」，因为在实际使用神经网络时，就只是输入一组数值作为「输入层」，再看看「输出层」得到的结果，并不关心中间的运算。 我们常说的「深度学习」里的「深度」指的就是神经网络中「隐藏层」的层数（只不过现在这个词有点被炒作了），当一个神经网络的隐藏层超过3层时，它就是「深度神经网络」。 通过改变神经网络的结构或者调节神经网络的权重和偏置，我们可以用神经网络近似任何的函数、甚至是一些摸不着头脑的规律。 比如影响小明今天玩不玩网游的因素有：今日作业量、心情、本月剩余流量、今天是星期几，但我们并不知道这些因素与小明玩不玩的具体数学关系，只能大概地推断：今天小明作业多，不会玩游戏；又或者今天是星期六，虽然作业还有很多，但他还是会玩游戏……可一旦知道具体数学关系，我们就可以通过计算准确预测小明是否会玩游戏，就像我们知道了牛顿力学公式，就可以根据物体的质量和被射出的力来计算它的运动轨迹一样。 所以我们所关心的、实际所使用的都是这种已经设置好正确权重和偏置的神经网络，像在与GPT聊天时怕「污染数据库」这类事就不用操心了。 要如…

导致Resources文件夹的资源在Android打包后丢失的原因
type: item
date: 2025-11-05T04:42:00+02:00
description: 1）导致Resources文件夹的资源在Android打包后丢失的原因 2）GPU Culled Primitive中的xyz三平面剔除与视锥体剔除有什么区别 3）按照不升级Unity引擎小改动快速适配安卓16KB页面要求操作后还是闪退 这是第451篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From UWA AI 问答 Q：请问Resources文件夹的资源在Android打包后丢失是什么导致的呢？有resources.load显式调用的。 A：Resources文件夹中的资源在Android打包后丢失，尽管有Resources.Load显式调用，通常并非资源未被打包进APK，而是由以下几种原因导致加载失败或资源无法访问： 1. 路径与大小写问题 Resources.Load的路径是不区分大小写的，但某些Android设备（尤其是定制ROM）的文件系统对路径敏感。如果代码中指定的路径与实际文件夹/文件名的大小写不完全一致，可能在部分设备上加载失败。确保路径字符串与Resources文件夹内的资源命名（包括嵌套文件夹）保持一致。 此外，路径应使用正斜杠“/”，且不包含“.asset”等扩展名。 2. 构建过程中资源被剥离或冲突 同名资源冲突：若项目中存在多个Resources文件夹，并且包含同名但不同内容的资源，Unity在构建时行为不确定，可能导致错误的资源被打包或覆盖。 Addressables或AssetBundle冲突：如果同一资源同时被标记为Addressable或打包进AssetBundle，又存在于Resources文件夹，可能会因构建流程混乱导致资源引用异常。建议避免资源重复管理。 3. 热更新或补丁机制干扰 使用Application Patching或自定义热更方案时，若处理不当可能导致StreamingAssets目录下原始资源被替换或删除。虽然Resources资源通常打包在主APK内，但在某些增量更新逻辑中，如果误操作了内部文件结构，也可能影响资源读取。 4. 安装路径变化（特定厂商手机） 部分国产手机（如华为、荣耀）将应用安装到SD卡时，路径中可能出现特殊字符（如 -），若项目中有自定义资源加载逻辑依赖路径拼接，未正确转义可能导致文件打开失败。虽然Resources.Load本身不受此影响，但如果涉及从外部加载补充资源并动态注册，则需注意。 5. 资源未实际打入包体 虽然Resources文件夹下的资源默认会被打包，但以下情况可能导致例外： 构建过程中出现异常中断，导致资源未完整写入。 使用了条件编译或脚本化构建流程，意外排除了Resources文件夹。 可通过解压APK文件（位于AndroidStudioProject/app/build/outputs/apk/或Unity导出目录），检查assets/bin/Data/resources.assets是否存在且包含预期资源，以确认资源是否成功打包。 排查建议 使用UWA GOT Online报告中的Resources.Load调用详情页，查看该资源的加载次数、耗时及是否返回null。 在真机运行时添加日志，捕获Resources.Load返回结果，确认是否为空。 检查目标设备的存储路径和安装方式，排除厂商ROM特殊行为影响。 清理构建环境，重新执行完整构建流程，避免增量构建残留问题。 综上，最可能的原因是路径问题或构建过程中的资源冲突，建议优先验证资源是否存在以及加载路径准确性。 分享链接： https://www.uwa4d.com/main/uwa-shared.html?shareId=88f1787d-7dc5-4d8a-91c2-09dc8d9c2258 From UWA AI 问答 Q：报告中的GPU Culled Primitive中的XY平面和Z平面剔除，和视锥体剔除是一样的吗？ A：GPU Culled Primitive中的剔除机制与视锥体剔除密切相关，但“XY平面和Z平面剔除”这一表述并不准确，实际并不存在独立的“XY平面”或“Z平面”剔除方式。通常所说的图元剔除主要指视锥体剔除（Frustum Culling），它是一种基于摄像机视锥空间的裁剪技术，判断物体是否位于可视范围内。 在图形渲染流程中，图元（如三角形）经过顶点着色器处理后，会进行一系列裁剪操作。其中，被裁剪掉的图元数量（culled_primitives）反映的是那些经过顶点计算后，最终因不在摄像机视野内而未被光栅化的图元。这部分图元的产生，主要受视锥体剔除机制影响 —— 即只有处于视锥内…

为什么Android游戏画面在30帧运行时有抖动现象
type: item
date: 2025-10-28T05:10:00+02:00
description: 1）为什么Android游戏画面在30帧运行时有抖动现象 2）团结引擎VS Skinning开启ENABLE_VS_SKINNING_MORE_BONES后，性能反而下降 这是第450篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From UWA AI 问答 Q1：Android平台下，游戏以30帧运行时，即便整体性能稳定，仍普遍存在画面抖动现象，想探讨一下这可能是由于什么原因造成的？ A：画面抖动（Jitter）是Android平台30帧游戏中常见但易被忽视的问题，即便整体FPS稳定，仍可能因以下原因导致视觉上的不流畅或抖动感： 1. 帧生成时间不均匀（Frame Pacing问题） 这是最核心的原因。即使平均帧率为30FPS，若帧的生成间隔不均匀（如一帧耗时28ms，下一帧耗时38ms），会导致视觉上出现“微卡顿”或“抖动”。Android设备尤其在高刷新率屏幕（如90Hz、120Hz）下，若未正确配置帧同步策略，GPU与显示子系统之间的帧提交节奏失配，极易引发此问题。 解决方案： 确保关闭Optimized Frame Pacing（部分模拟器和低端设备存在兼容性问题，实测可缓解抖动）。 使用Choreographer回调来对齐VSync信号，确保帧更新时机精准。 在Unity中，避免仅依赖Application.targetFrameRate=30，因其在动态刷新率设备上可能导致帧率倍数下降（如从30→15）。 可结合定时重设帧率策略： IEnumerator SetFrameRatePeriodically() { while (true) { yield return new WaitForSeconds(10); Application.targetFrameRate = 31; yield return new WaitForSeconds(0.1f); Application.targetFrameRate = 30; } } 2. GPU与CPU工作负载波动引起帧时间波动 即使平均性能达标，若每帧CPU或GPU耗时波动大（如某些帧CPU耗时接近33ms，其他帧仅15ms），会造成帧间隔不一致，表现为抖动。 排查建议： 使用UWA GOT Online的CPU每帧耗时曲线、>40ms帧数占比指标，分析是否存在高波动性。 检查是否存在偶发性任务（如GC、资源加载、逻辑计算集中爆发），导致单帧骤增。 3. 渲染管线与垂直同步机制冲突 Android设备厂商常自定义显示策略，部分机型在非原生刷新率（如60Hz设备跑30Hz）时，系统VSync调度可能出现偏差，导致帧呈现时间错位。 优化方向： 避免频繁切换目标帧率，保持帧率恒定。 在支持的设备上尝试使用SurfaceFlinger相关调试工具观察实际显示帧时序。 若使用自定义渲染管线，检查后处理、阴影等异步操作是否引入延迟。 4. 输入与渲染不同步 触摸输入更新频率（通常60Hz或更高）与30Hz渲染不同步，可能导致操作反馈延迟感叠加视觉抖动。 改进措施： 尽量将关键UI更新与渲染帧对齐。 对高频输入做插值或预测处理，提升交互平滑度。 综上，画面抖动多源于帧间时间不一致性而非平均帧率不足。建议优先通过UWA性能报告中的Jank均值、>40ms帧数占比及CPU每帧耗时曲线进行量化分析，并针对性优化帧生成节奏与负载均衡。 Q2：有什么解决方案？ A：针对Android平台30帧下画面抖动问题，可采取以下系统性解决方案： 1. 精准控制帧率同步（核心措施） 关闭Optimized Frame Pacing：该功能在部分设备（如MuMu模拟器）存在兼容性问题，易导致帧提交延迟。在Player Settings中禁用此选项可显著改善抖动。 使用Choreographer对齐VSync：通过Android原生API确保每帧更新与系统刷新信号同步，避免因调度偏差导致的帧间隔不均。 周期性重设目标帧率：应对动态刷新率设备（如120Hz降为60Hz时30帧变15帧），可定时重置targetFrameRate以强制系统重新计算： IEnumerator SetFrameRate() { while (true) { yield return new WaitForSeconds(10); Application.targetFrameRate = 31; yield return new WaitForSeconds(0.1f); Application.targetFrameRate = 30; } } 2. 优化…

对Android游戏画面抖动现象的研究
type: item
date: 2025-10-22T05:05:00+03:00
description: 【USparkle专栏】如果你深怀绝技，爱“搞点研究”，乐于分享也博采众长，我们期待你的加入，让智慧的火花碰撞交织，让知识的传递生生不息！ 一、前言 近期笔者一直在研究一个专题：在Android平台下，游戏以30帧运行时，即便整体性能稳定，仍普遍存在画面抖动现象。即使是知名厂商的游戏也不例外。例如笔者测试《星穹轨道》时发现： 角色跑动时留意那盆绿植会比较明显 可能是出于功耗的考虑，一进来就默认推荐了30帧，玩起来也有持续性的画面抖动，不像是偶发的卡顿，我的手机性能是较好的，不至于连30帧都跑不流畅。 通过一段时间的研究，对这个问题的原理和解决方案有了一定的见解。 二、原因分析 1. 显示机制 Android的显示机制是一个典型的生产者-消费者模型。应用端负责生产画面，通过调用SwapBuffer将内容写入BufferQueue，系统端（SurfaceFlinger进程）则从BufferQueue中取出内容，执行后续的合成与显示流程。 SurfaceFlinger取BufferQueue的节奏是根据系统Vsync-sf信号调节的，一般跟屏幕刷新率走，比如60hz那么就是16.6ms为一个周期定期触发，当游戏为30帧时，平均是33.3ms生产一个Buffer，生产的速率远远慢于消耗的速率，当Vsync-sf信号来临但BufferQueue里面为空时，系统会重复显示上一帧的内容。 理想情况下，在60Hz屏幕下运行30帧，SurfaceFlinger应该每两个周期消耗一帧内容。然而通过分析发现，实际的消耗周期并不均匀：有时一个周期消耗，有时甚至三个周期消耗。导致每帧画面在屏幕上的停留时间不一致，从而在视觉上产生抖动感。 2. 量化工具 接下来使用Perfetto来看一下这个游戏，Perfetto是Android上系统级别的Trace工具，可以使用它来观察具体的BufferQueue、Vsync情况。 Perfetto有一些配置，针对画面抖动问题需要确认开启ATrace的gfx、view模块： 下图中标注了每一次Buffer被消耗时，距离上一次的周期数： 这里筛选出了Trace数据中相关的轨道，从BufferQueue里面可以看到大多数情况是2个周期消耗一次，偶尔会有1、3个周期出现。 Perfetto生成的Trace文件可以通过SQL进行统计。笔者统计了每次BufferQueue减少时的时间点，进一步计算了这些时间间隔的标准差。该指标可用于量化画面抖动的严重程度，为后续方案对比与优化提供了数据基础。 完整SQL内容在附录中提供 三、目前的解决方案 1. Swappy方案 Swappy是Google GameSDK的一部分，这里是它的官方介绍： https://developer.android.com/games/sdk/frame-pacing?hl=zh-cn *上述网址需要使用VPN打开 它是大多数游戏引擎内置的方案，以Unity引擎为例直接设置一下生效就行。 此方案核心是通过两个EGL拓展来达成它的目的： 通过setPresentationTime，为Buffer设置一个时间戳，避免此Buffer被过快消耗掉。 回到之前我们的例子，如果这里设置了一个允许被消耗的时间戳，那么这里不会存在1个周期就消耗掉的情况： 利用EGL_KHR_fence_sync，可以追踪上一帧GPU处理完毕再将当前帧Swap进去。由于实际游戏运行过程中不会是平稳地每次33.3ms Swap一次，会有波动，此策略可以避免局部生产过快导致的BufferQueue堆积。 Hook了Swappy中的一些函数，将其标注到了Perfetto中以观察其运行过程： 当然，笔者在实际使用过程中，也是遇到了不少问题： 掉帧，常见于开60帧的情况，因为要等上一帧GPU结束，不同手机追踪到的GPU时间差异大，有的要10几ms，再加上Swappy是以周期为单位进行等待的，很容易延后的时间太多，最终导致帧率下降严重，所以建议60帧时不启用Swappy机制。 延迟明显，一方面是渲染线程进行Swap的平均时间是要延后很多的，另一个方面是主线程是先做逻辑Update，然后通过WaitForPendingPresent等待上一帧渲染线程结束后，再继续，从Update到最终呈现的链路多了一个流程，30帧时预估延迟会增加30~40ms左右，体感明显。 不稳定，不同机型效果差异很大，观察了一下发现有些机型setPresentationTime调用后，实际生效的时间并不精确，还有些机型60hz的屏幕刷新率但程序返回的值是59、58这些奇怪的值，可能会导致功能失效、锁帧之类的问题。 总的来说Swappy对画面抖动是有一定效果的，上述问题有些也好处理，只是此方案虽然开启简单，但决策前仍然要权衡、多机型…

有什么指标可以判断手机是否降频
type: item
date: 2025-10-21T09:12:00+03:00
description: 1）有什么指标可以判断手机是否降频 2）关于降低动画浮点数精度的问 这是第449篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From UWA AI 问答 Q：有什么指标可以判断手机是否降频？ A：判断是否发生降频的关键指标 可通过CPU与GPU频率曲线直接观察运行期间主频变化情况。若出现持续低于峰值频率或阶段性降频回弹的现象，说明硬件已触发动态调频机制。结合帧率波动与温度数据，若帧率下降同时伴随CPU/GPU温度上升但频率下降，则可判定为热致降频。当前硬件温度分析未发现明显过热或异常温升，且无显著温度驱动的降频迹象。 判断降频的核心指标 可通过报告中的CPU频率和GPU频率曲线直接观察运行期间是否出现主频下降。若频率低于设备峰值水平，尤其伴随帧率波动或性能下降，则表明已触发动态降频。 关联辅助指标分析 结合温度变化量（CPU/GPU/电池温度）与功率峰值数据：当温度持续升高并达到阈值，同时功耗较高，而频率开始回落，即可确认为热或功耗导致的降频。此外，每万帧耗电均值异常偏高也可能是带宽或GPU压力过大间接引发发热降频的信号。 关于CPU Bound的说明 即使DrawCall和三角面数相近，CPU Bound仍可能由逻辑更新、动画、物理或脚本开销引起。建议查看逻辑代码耗时、动画更新耗时等函数堆栈数据，定位具体模块瓶颈。 注意事项 若测试时设备连接USB或充电，功率与温度数据将失真，严重影响降频判断。务必在非充电状态下进行测试，以获取真实硬件行为。 综上，应重点查看CPU/GPU频率曲线与温度趋势联动情况，在真实供电环境下验证是否存在降频。 您可以前往UWA性能报告页，通过AI问答功能提出您关心的问题，期待您的使用和反馈。详细使用方式详见：《UWA AI问答上线：游戏开发问题秒级解惑》 From 问答社区 Q1：我看到社区中经常提到降低动画浮点数精度这种优化手段。这是针对FBX导入的动画吧？我们自己创建的AnimationClip还需要处理吗？ A：自己创建的一般内存比较小，不需要做这个处理。 Q2：动画曲线浮点精度优化，我理解是FBX导入的动画，有许多关键帧。浮点数精度优化之后的值是一样的，可以变成const。可以这么理解吗 ？ A：是的。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/68f5b96d682c7e5cd61bfb52 无论是社区里开发者们的互助讨论，还是AI基于知识沉淀的快速反馈，核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区，希望这些从真实开发场景中提炼的经验，能直接帮你解决当下的技术卡点，也让你在遇到同类问题时，能更高效地找到破局方向。 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859

iOS框架内存中占用很高的ttc文件是否正常
type: item
date: 2025-10-14T05:34:00+03:00
description: 1）iOS框架内存中占用很高的ttc文件是否正常 2）Constant Buffer在移动端的限制是多少 这是第448篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 无论是社区里开发者们的互助讨论，还是AI基于知识沉淀的快速反馈，核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区，希望这些从真实开发场景中提炼的经验，能直接帮你解决当下的技术卡点，也让你在遇到同类问题时，能更高效地找到破局方向。 From AI 问答 Q：用团结引擎Memory Profiler看iOS包的内存占用，这个可以定位iOS框架内存。发现里面有一个PingFang.ttc占用了74MB，不是在资源里，而是显示在iOS框架内存里面的，这个是正常的么？ A：该现象是正常的，无需过度担忧。 PingFang.ttc是苹果系统自带的苹方字体文件（TrueType Collection），属于iOS系统级资源。当应用首次使用系统默认字体（如在Text、TextMeshPro以外的UI元素中未指定字体）或系统进行文本渲染时，iOS会将该字体文件映射到应用的内存空间，因此在Memory Profiler的“iOS框架内存”或“Untracked Memory”部分中被统计为占用。 此部分内存由操作系统管理，不属于Unity托管堆或应用主动加载的Assets资源，因此不会出现在AssetBundle或资源列表中。其占用行为属于系统正常机制，且通常为共享内存（多个应用可能共用同一份字体数据的物理页），实际对整体PSS内存影响有限。 建议处理方式如下： 确认是否误用系统字体：检查项目中UI文本组件（尤其是Legacy UI Text）是否未指定自定义字体而依赖系统默认字体。若需统一视觉风格或降低对系统字体的依赖，建议使用嵌入式字体（.ttf/.otf）并打包至AssetBundle。 关注可优化内存区域：74MB的系统字体映射属正常范围，应优先关注Tracked Memory中的Managed Heap、Texture、Mesh等可控内存模块的优化。 避免不必要的文本渲染：减少动态生成大量文本对象的行为，合理使用对象池，防止因文本频繁创建导致系统字体缓存持续增长。 使用UWA GOT Online工具辅助分析：通过Resource模式下的内存自动采样，对比不同场景下PSS与Reserved Total的变化趋势，明确业务代码对内存的影响。 综上，PingFang.ttc占用74MB属于iOS系统正常行为，只要项目无异常卡顿或OOM崩溃，可视为合理内存使用。 分享链接： https://www.uwa4d.com/main/uwa-shared.html?shareId=29e9e0df-2c32-4b2e-9317-503e5891cfd8 From 问答社区 Q：CBuffer（Constant Buffer）在移动端一般大小限制是多少？ A：普通Shader的Constant Buffer，会受到Graphics API和硬件影响，可以用SystemInfo.maxConstantBufferSize获取。 通常除了GPU Instancing时，很难达到CBuffer的上限，所以一般不太关心这个，《Rendering 19--GPU Instancing》这篇文章里面有讲GPU Instancing的数量受到CBuffer的限制。 详见：《Rendering 19--GPU Instancing》第1.4 https://catlikecoding.com/unity/tutorials/rendering/part-19/ 原文如下图： 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/68d25f50682c7e5cd61bfb30 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859

游戏AI行为决策——GOAP（目标导向型行为规划）
type: item
date: 2025-10-13T05:32:00+03:00
description: 【USparkle专栏】如果你深怀绝技，爱“搞点研究”，乐于分享也博采众长，我们期待你的加入，让智慧的火花碰撞交织，让知识的传递生生不息！ 一、前言 像先前提到的有限状态机、行为树、HTN，它们实现的AI行为，虽说能针对不同环境作出不同反应，但应对方法是写死了的。有限状态机终究是在几个状态间进行切换、行为树也是根据提前设计好的树来搜索……你会发现，游戏AI角色表现出的智能程度，终究与开发者的设计结构有关，就有限状态机而言，各个状态如何切换很大程度上就影响了AI智能的表现。 那有没有什么决策方法，能够仅需设计好角色需要的动作，而它自己就能合理决定要选择哪些动作完成目标呢？这样的话，角色AI的行为智能程度会更上一层楼，毕竟它不再被写死的决策结构束缚；我们在添加更多AI行为时，也可以简单地直接将它放在角色需要的动作集里就好，减少了工作量，不必像行为树那样，还要考虑节点间的连接。 没错，GOAP（目标导向型行为规划）就可以做到。但请注意，并不是说GOAP就比其它决策方法好，后面也会提到它的缺点。选择何种决策方法还得根据实际项目和自身需求。 PS：本教程需要你具备以下前提知识： 知道数据结构、堆/优先队列、栈、图。 知道A星寻路的流程，如不了解可看此视频[1]。 基本的位运算与位存储（能做到理解Unity中的Layer和LayerMask的程度就行）。 二、运行逻辑 我们来看个简单的寻路问题：你能找到从A到B的最短路线吗？注意，道路是单向的。 聪明如你，这并不难找到： 现在，加大难度，假设每条道路口都有一个门，红色表示门关上了，蓝色表示门开着，你还能找出可达成的最短A到B路线吗？ 同样不难： 这样就足够了，GOAP的规划就是这么一个过程。只是把每个节点都当成一个状态，每条道路都当作一个动作、道路长度作为动作代价、路口的门作为动作执行条件，然后像你这样寻找出一条可以执行的最短「路线」，并记录下途径的道路（注意，不是节点），这样就得到了「动作序列」，再让AI角色逐一执行。GOAP中的图会长成下面这样（只画出了一条路的样子，但相信你们能举一反三的）： GOAP就是在不断执行「从现有状态到目标状态」，上图中的「现有状态」和「目标状态」分别就是「饿」和「饱」。请注意，虽说用了不同形状，但中间的那些椭圆节点，比如「在上网」，也是和「饿」、「饱」同类别的存在。也就是说「在上网」也可以作为现有状态或目标状态。 可想而知，只要状态够多，动作够多，AI就能做出更复杂的动作。虽说这对其它决策方法也成立，但GOAP不需要我们手动设置各动作、状态之间的关系，它能自行规划出要做的一系列动作，更省事且更智能，甚至可以规划出超出原本设想但又合理的动作序列。 希望我讲明白了它的运作（如果还是感觉有点不懂，可以看看这个视频[2]），下面一起来实现一个简单的GOAP进一步了解吧！顺带提一下，在Unity资源商店有免费的GOAP插件，并且做了可视化处理以及多线程优化，各位真的想将GOAP运用于项目的话，更推荐去学习使用成熟的插件。 三、代码实现 本文「世界状态」的实现参考了GitHub上一C语言版本的GOAP[3]。 1. 世界状态 所谓「世界状态」其实就是存储所有的状态放在一块儿的合集。而状态其实还有一个隐藏身份——动作条件。是的，状态也充当了动作的执行条件，比如之前图中的条件「有流量」，它其实也是一个状态。 世界状态会因自然因素变化，比如「饱」会随着时间流逝而变「饿」；也会因角色自身的一些动作导致变化，比如一个角色多运动，也会使「饱」变「饿」。 问题在于： GOAP规划需要时时获取最新的状态，才能保证规划结果的合理性（否则饿晕了还想着运动）； 「世界状态」中有些状态是「共享」的，比如之前说的时间，但还有一些状态是私有的，比如「饱」，是我饱、你饱还是他饱？在一个合集里该如何区分？ 如果你看过上一篇关于HTN的文章的话，你会发现这是如此的眼熟。不过没看过也没关系，我们将采取一种新的实现「世界状态」的方法——原子表示。 PS：在传统人工智能Agent中，对于环境的表示方式有三种： 原子表示（Atomic）：就是单纯描述某个状态有无，通常每个状态都只用布尔值（True/False）表示就可以，比如「有流量」。 要素化表示（Factored）：进一步描述状态的具体数值，这时，状态可以有不同的类型，可以是字符串、整数、布尔值……在HTN中，我们就是用这种方式实现的。 结构化表示（Structured）：再进一步，每个状态不但描述具体数值，还存储于其它数据的连接关系，就像数据结构中「图」的节点那样。 接下来将采用位存储的方式进行原子表示，因为借助位运算可以方便且高效地实现比较，还省空间。缺点就是有些难懂，所以，我希望你了解如int、long的二进制存储方式或者Unity中LayerMa…

TextureStreaming针对不同分档机型一般怎么设置
type: item
date: 2025-09-29T05:08:00+03:00
description: 1）TextureStreaming针对不同分档机型一般怎么设置 2）不升级Unity引擎小改动快速适配安卓16KB页面要求 这是第447篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 无论是社区里开发者们的互助讨论，还是AI基于知识沉淀的快速反馈，核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区，希望这些从真实开发场景中提炼的经验，能直接帮你解决当下的技术卡点，也让你在遇到同类问题时，能更高效地找到破局方向。 From AI 问答 Q：想问问TextureStreaming的选项有没有推荐值，针对高中低机型一般怎么设置呢？ A：关于Texture Streaming的参数设置，需结合设备性能分级（高中低）进行精细化配置，以在画面表现与内存占用间取得最佳平衡。以下是针对不同机型的推荐策略： 核心参数说明 Memory Budget：纹理内存预算，决定Unity开始管理Mipmap加载的阈值。所有纹理（含非流式）均计入此预算。 Max Level Reduction：允许丢弃的最大Mipmap层数级数，优先级高于Memory Budget。例如设为2，则最多只加载Mipmap 2及更小层级。 Add All Cameras：默认开启，对所有摄像机启用串流。若需精细控制，可关闭并手动为特定摄像机添加Streaming Controller组件。 Renderers Per Frame：每帧处理的Renderer数量，影响CPU开销。建议根据帧率动态调整，避免卡顿。 高中低机型设置推荐 1. 高端机型 目标：追求极致画质，充分利用硬件资源。 Memory Budget：512MB~1GB（确保略高于 Texture.desiredTextureMemory）。 Max Level Reduction：0或1（尽可能保留高分辨率Mipmap）。 其他： 可适当提高Async Upload Time Slice和Buffer Size，加快资源上传速度。 启用streamingTextureDiscardUnusedMips = false，利用缓存减少重复IO。 优势：画面细腻，无明显纹理“popping”现象。 注意：监控targetTextureMemory，避免超出物理内存导致OOM。 2. 中端机型 目标：平衡画质与性能，控制内存峰值。 Memory Budget：200MB~300MB。 Max Level Reduction：1~2。 Renderers Per Frame：256~512（根据实际帧率微调）。 Async Upload： asyncUploadTimeSlice：4ms asyncUploadBufferSize：16MB asyncUploadPersistentBuffer = true 推荐做法：通过真机测试获取desiredTextureMemory，将Budget设为其1.2倍左右，留出余量。 3. 低端机型 目标：保障流畅运行，严控内存。 Memory Budget：100MB~150MB。 Max Level Reduction：2~3（强制降级，节省内存）。 Texture Quality：可配合使用“Half Res”模式进一步压缩非关键纹理。 Renderers Per Frame：128~256，降低每帧CPU开销。 强制代码开启： QualitySettings.streamingMipmapsActive = true; 避免编辑器设置在移动端失效。 关键点：UI纹理务必关闭Streaming Mipmap，因其无需远近LOD，开启反而浪费计算和内存。 关键实践建议 1. 必须代码开启Streaming 仅在 Quality Settings 中勾选可能在移动端无效，务必在启动时调用： SystemInfo.supportsMipStreaming // 先检测是否支持 QualitySettings.streamingMipmapsActive = true; 2. 真机测试为准 Editor中纹理内存远高于真机（路径不同），一切优化效果验证必须在目标设备上进行。 3. 合理启用对象 仅对场景模型贴图启用Streaming Mipmap。 UI、图标、不带Mipmap的纹理应关闭。 确保纹理开启Generate Mipmap且未勾选Read/Write Enabled（CPU内存不受Streaming影响）。 4. 动态调整策略参考 使用以下公式估算摄像机…

游戏在高负载场景下，整机功耗控制在多少
type: item
date: 2025-09-23T05:07:00+03:00
description: 1）游戏在高负载场景下，整机功耗控制在多少合理 2）什么是MALLOC_SMALL和MALLOC_NANO，如何优化 3）Spine堆内存占用高怎么办 这是第446篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 UWA每周技术分享如约来到第446期，自上期我们开启升级时，便希望为大家解决开发中“问题急、需快答”的需求 —— 而本期，这份初心依旧延续：我们从UWA AI问答的真实提问库中筛选案例，既能帮你快速理清解题思路，也能让你切实感受到“不用等、随手查”的便捷。 Q：游戏在高负载场景下，整机功耗控制在4000-5000mW以内是否合理？ A：游戏在高负载场景下整机功耗控制在4000-5000mW是合理且常见的，尤其对于追求高画质、高帧率的移动端3D游戏。 功耗合理性分析 典型范围参考：根据行业经验，在运行复杂渲染与密集逻辑的高端游戏中，设备整机功耗达到4000-5000mW属于正常水平。这表明CPU、GPU等硬件资源被充分调动以维持流畅体验。 对比基准：在轻量应用或待机状态下，设备功耗通常为1000-2000mW。游戏作为高压应用，功耗显著上升是性能需求驱动的结果。 发热关联性：高功耗直接导致设备发热，可能触发系统级降频（如CPU/GPU throttling），进而影响帧率稳定性与用户体验。 高功耗的主要贡献因素 GPU带宽开销 GPU显存带宽是能耗的重要来源之一。数据显示，约1GB/s带宽可带来80-100mW功耗。若项目渲染带宽达10GB/s，则仅此项即可贡献约1000mW功耗，占总功耗的20%左右。因此，优化渲染管线、降低冗余绘制和纹理传输对控电至关重要。 渲染复杂度 同屏三角面数过高（如超过60万面）、DrawCall频繁、多Pass Shader使用等都会显著增加GPU负载与功耗。建议在低端设备上控制同屏面数在25万以内，高端机不超过60万。 CPU模块耗时 动画更新（Animators.Update）、物理模拟（Physics.Simulate）、粒子系统（ParticleSystem.Update）等若占用过高CPU时间，也会推高整体功耗。可通过异步计算、批处理或简化逻辑来缓解。 内存与GC压力 Mono堆内存峰值过高或GC.Collect频繁调用会引发CPU spike，间接增加功耗。应减少运行时内存分配，避免每帧创建对象，并合理管理资源生命周期。 优化建议 监控关键指标：利用UWA GOT Online报告中的“功率峰值”、“每万帧耗电均值”、“温度峰值”等指标，定位高功耗场景。 场景级排查：通过“场景性能列表”功能查看各场景的功率与电流表现，识别异常场景并结合函数堆栈深入分析。 针对性优化： 减少Overdraw，合并材质，压缩纹理格式（如使用ASTC替代RGBA32）。 合理使用LOD与视锥剔除，降低不必要的渲染负担。 控制后台加载频率，避免GC集中触发。 审查Shader复杂度，避免片元着色器过度计算。 综上，4000-5000mW功耗本身合理，但需结合设备散热能力、用户感知（发烫、掉帧）综合评估，并持续优化关键模块以提升能效比。 分享链接： https://www.uwa4d.com/main/uwa-shared.html?shareId=a2de5479-7f4a-4bab-8284-6d5e7715c8ca Q：Unity Profiler中，Untracked中的MALLOC_SMALL和MALLOC_NANO分别是什么意思，以及如何优化？ A：在Unity Profiler中，Untracked内存指的是引擎无法直接追踪和归因的原生内存分配。其中，MALLOC_SMALL和MALLOC_NANO是iOS平台（基于Apple的malloc堆分析）在Xcode或Instruments中对小块内存分配的分类，Unity Profiler在真机采样时也会呈现这些系统层内存信息。 MALLOC_NANO：指通过macOS/iOS的malloc系统中“Nano Zone”分配的小块内存（通常小于256字节）。Nano Zone是为高频、小对象分配优化的快速分配路径，具有低开销和高效率的特点。 MALLOC_SMALL：指大小在256字节至1MB之间的内存块，由malloc的“Small Zone”管理。这类分配适用于中等大小的对象。 这两类内存属于系统原生堆分配，Unity无法直接监控其具体来源，因此归为Untracked Memory。若其占用过高，可能表明存在大量频繁的小对象分配，或第三方库、插件、C++模块的内存使用异常。 优…