UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - unity
UWA DAY 2025 正式启动｜十年筑基，驱动游戏未来引擎
由侑虎科技UWA主办的年度游戏开发者大会 - UWA DAY 2025，正式拉开帷幕！作为第八届盛会，也是侑虎科技成立十周年的里程碑之作，本届大会将以“十年筑基 · 驱动游戏未来引擎”为主题，于2025年8月在上海盛大举办。 从开发优化到质量保障，从前沿技术到实践落地，UWA DAY始终紧贴行业脉动，持续为开发者提供深度技术交流的平台。今年，我们将以更加开放的姿态，与广大开发者共同探讨下一阶段游戏工业的进化之路。 十年耕耘，与开发者共筑技术星河 自2015年创立以来，UWA始终坚持“快｜精准｜有温度｜值得信赖”的初心，持续深耕游戏研发工具和服务。十年来，从性能评测工具到全生命周期解决方案，从小型沙龙到全国技术峰会，我们始终与开发者并肩前行，见证并参与了中国游戏产业的不断跃迁。 UWA DAY，作为技术交流的风向标，也在这三千多个日夜中与千万开发者一起成长。站在十周年的新起点，我们愿以更加坚实的技术底座，为下一代引擎注入持续动力。 技术破界，重构未来研发生态 本届UWA DAY将围绕「引擎革新」、「工业化工具链」与「研发生态建设」三大核心主题，来自UWA及行业顶尖团队的资深技术专家，预计带来30+场高质量分享内容——从理论探索到实战落地，绘制一幅面向未来的技术图谱。 议题议程 2025年8月1日 周五 2025年8月2日 周六 议题总览 ➨十年筑基，驱动游戏未来引擎 主题演讲 张鑫 侑虎科技 CEO 随着移动游戏研发成本持续攀升与轻量化游戏生态的成熟，中国游戏行业正呈现 "品质深耕" 与 "敏捷开发" 并行的双轨发展格局。一端是3A化手游对工业化流程的极致打磨，另一端是轻量化团队对快速迭代效率的不懈追求。这种行业分化既考验着UWA技术服务的专业性与适配性，也为工具矩阵的场景化创新开辟了全新赛道。 过去一年，UWA围绕研发效率与玩家体验两大核心场景展开深度探索，在技术落地与产品迭代中实现关键突破。本次报告将重点分享三个方向的成果： GPM 2.0：玩家端体验的精准洞察。通过精准捕捉玩家的性能异常波动，为高品质游戏的长线运营提供质量保障。 GOT Online & Pipeline：工业化流程的 AI 赋能。通过与AI大模型的结合，进一步降低游戏团队对于研发工业化流程的准入门槛。 Gears：降低研发端性能检测的使用门槛。通过这一年来不断完善的检测功能，满足广大游戏团队的日常常规检测需求。 行业变革之下，UWA 始终相信，唯有扎根研发痛点的持续创新，才能在品质与效率的双螺旋中找到平衡点。本次报告将深度拆解技术落地的实战经验，期待与行业同仁共探 "工业化流程+智能化工具" 的破局路径，以十年积淀为基，为游戏未来研发注入新的动能。 ➨ UWA Gears深度体验 - 运行时性能测试与GPU深度分析新突破 张强 侑虎科技 CTO UWA Gears作为一款集成运行时性能分析与截帧渲染分析的专业测评工具，是游戏GPU性能优化的重要支撑，能够为开发者提供精准的性能数据与问题定位依据。 当前，随着游戏引擎对Vulkan API的支持日益成熟，以及移动设备的快速更新换代，Vulkan的兼容性持续改善，其在渲染效率提升、图形资源内存优化等方面的优势逐渐凸显，越来越多的研发团队开始使用Vulkan。在此背景下，UWA Gears近期实现了对Vulkan API截帧分析的支持，为开发者在Vulkan环境下的GPU性能优化提供了更强助力。 本次报告将围绕UWA Gears展开分享：一方面，详细演示如何借助该工具进行GPU性能分析；另一方面，重点展示在Vulkan环境下的重要参数解读与分析方法。通过这些内容，助力开发者更透彻地理解Vulkan特性，精准发现并高效解决GPU性能问题。 ➨ 虚幻引擎5构建开放世界下的优化、挑战以及开发方向 陈宝康 Epic Games 开发者关系工程师 现担任Epic Games开发者关系工程师，协助解决游戏公司在使用虚幻引擎中遇到的相关问题，曾先后就职于字节、腾讯，从事游戏开发多年。 随着引擎技术的不断发展，游戏引擎未来会更加关注开放世界的构建能力来带给玩家以沉浸式的体验，而Epic作为以赋能创作者和构建数字生态为核心的商业引擎，长远目标同样也是为了创建出一个元宇宙的虚拟世界，并带给玩家以身临其境的体验。每个人都可以自由的进行创作和表达，构建虚拟世界的未来，同时并将其技术赋能于游戏、影视、工业等不同领域。而在这长远目标下如何去构建开放世界成为了其中的重要一环，因为构建开放世界下的场景对游戏引擎提出了更高的要求，更大的开放世界意味着制作过程中需要面临一系列问题，本次报告与大家分享的就是虚幻引擎5针对开放世界在不同版本中主要做了哪些工作、目前的进度、面临了哪些挑战，以及未来的开发方向。主要包括：UE5构建开放世界的技术点、不同版本新增了哪些功能、开放世界下pcg工作流展示、UE5.6的主要内容介绍以及未来的开发方向。 ➨ AI在游戏开发领域的颠覆式革新 麒麟子 Cocos引擎 技术总监 游戏引擎领域深耕15年，目前负责Cocos引擎的产品研发与开发者社区。社区 KOL、技术布道师、科技自媒体博主，接触了4000+研发公司，全网程序员粉丝15万+。当前阶段的核心任务，是研究AI技术如何转化为工作流和生产力，为产品研发带来极致的效率提升。 人工智能正在以前所未有的速度重塑游戏开发的每一个环节。从传统的手工编程到智能化工具辅助，从昂贵的资产制作到AI一键生成，从复杂的开发流程到30分钟构建游戏原型，游戏开发正经历着一场深刻的技术革命。本次分享将深入探讨AI技术在游戏开发领域的五大革新方向：开发工具、资产生成、技术栈、端侧AI、实时内容驱动。这不仅仅是一次技术分享，更是对游戏开发未来的深度思考。无论你是资深开发者、创业团队负责人，还是对游戏技术充满好奇的从业者，这次演讲都将为你提供一些思考和帮助。让我们一起探索AI如何重新定义游戏开发的边界，见证一个全新时代的到来。 ➨ UE-DS的长跑挑战 - 长生命周期大世界的性能治理探索之路 赵珂 腾讯天美J1工作室 高级工程师 十年游戏开发经验，2019年入职腾讯，先后在《ApexMobile》、《高能英雄》项目中长期专注UE4引擎网络同步和DS性能优化工作。目前在《元梦之星-山海寻灵》玩法中负责DS基建和战斗相关工作。 本次报告将介绍在使用UE4引擎开发一款长生命周期大世界游戏过程中面临的技术挑战和实践经验。在短单局射击游戏中，DS优化聚焦逻辑精简和单线程压榨，通过限制同步、关闭动画等常规手段提升承载；而《元梦之星-山海寻灵》的长生命周期大世界需直面复杂业务叠加与持久战斗体验的冲突，迫使优化思路发生本质转型。报告主要内容包括：常规DS的优化、长生命周期大世界的新挑战、长局优化的混合方案和实验探索。 ➨ 《归环》项目研发效率提升之路 贾伟昊 萨罗斯网络科技（深圳）有限公司 项目主程 毕业于清华大学软件学院，先后任职于网易、心光流美、字节等公司，积累了十余年游戏开发经验，喜欢思考和分享，目前担任《归环》项目主程序。 为了满足玩家对于内容和品质的需求，游戏项目的体量越来越大，动辄几百人的团队规模对于开发和协作效率的挑战也越来越大，如何让团队高效地生产出高品质的游戏内容，成为了项目成败的关键。本次报告将从研效基建、技术框架、工作流建设等方面和大家交流《归环》这款刚刚首曝的二次元产品在效能提升方面的一些思考和实践，也会和大家分享一些我们走过的弯路、踩过的坑，希望能够带给一同在游戏研发之路上探索的朋友们一些新的思路。 ➨ 大世界场景工业化：程序化生成x模块化资产x渲染优化工作流 何文雅 珠海西山居 资深技术美术 拥有16年游戏开发经验，现任职西山居技术美术经理。专注美术资产管线规范化，主导制定跨项目的美术标准与工具链体系，推动场景自动化生成、虚拟几何体、全局光照、画质分级等核心技术落地，致力于用引擎技术解放美术生产力，实现画质与性能的动态平衡。 在游戏工业化进程中，流程效率瓶颈与技术迭代需求已成为核心挑战。本次报告将分享《解限机》项目基于Unity引擎定制的大世界场景自动构建解决方案，揭秘超大规模场景高效开发的破局之道。主要内容包括：通过Houdini程序化生成实现自然生态系统自动化构建；采用模块化数据架构革新人文环境生产管线；并融合虚拟几何体等次世代渲染技术。在零成本改造美术流程的前提下，完成从场景生成到电影级表现的技术闭环。 ➨ 技术美术：AI在游戏研发中的应用探索- 搭建游戏开发AI智能体与AI生成美术资产工业化管线 丁彦清 资深技术美术 北京电影学院数字媒体艺术专业硕士，现就职于某游戏大厂，深耕游戏开发领域。曾参与《生死狙击2》、《矩阵·零日危机》等项目的研发工作，积累了丰富的实战经验。核心专长聚焦于：游戏渲染技术 - 致力于高品质视觉效果的实现与渲染管线优化；性能优化 - 对游戏运行效率有深入理解与实践，擅长解决性能瓶颈；AI技术应用探索 - 积极研究并实践生成式AI等前沿技术在游戏美术管线、内容创作及玩法创新中的应用潜力。 本次报告系统性拆解AI技术在游戏美术管线中的工业化实践方案，涵盖三大核心模块： AI智能体游戏开发实践：基于Dify构建的TA-Agent实现材质规范审查、性能风险预警、资产版本自动化管理；基于Dify构建的智能助手如何解决材质开发、性能优化等跨领域难题； 生成式工作流革新：通过ComfyUI实现3D模型→UI资产的一键转化（含IP Lora训练/ControlNet姿势控制）、PBR贴图智能生成（SDXL+Substance 3D Sampler联动）、立方体贴图批量化生产； 工业化管线集成：贴图生成工作流与Substance 3D Sampler的PBR管线对接方案，立方体贴图批量生成标准。 ➨ UE美术表现的非技术力思考 - 效果的合理偷懒 李季（老李头儿） 数字艺术家，TA 上海市美术家协会会员，上海市青年美术摄影家协会副秘书长，视觉暂留TA合伙人。第四届瑞云渲染大赛季军，第六届GGAC裸眼3D赛道季军，B站UP渲染封神榜TOP50，数字艺术作品参与2025大阪世博会Shining Hat，WESTBUND西岸艺术博览会，全国青年艺术邀请大展等国内外诸多艺术展览。服务对象包括米哈游、腾讯、网易、开天工作室等。 在虚幻引擎的美术创作中，“合理偷懒”并非降低品质，而是通过策略性取舍与美术思维重构技术实现路径，以最低技术成本撬动高效视觉表现。其核心逻辑在通过资源复用、流程优化与注意力引导，在保障表现力的前提下规避冗余劳动。本次报告将介绍资源复用策略以及视觉转移设计的两个方向，运用美学决策优先的策略以减少手动调优的负担。让技术成为创意的赋能者。 ➨ 稳定、高效、安全的出海之路 - 阿里云游戏出海解决方案 云骧 阿里云智能集团 高级解决方案架构师 10年云计算领域经验，15年数据库、大数据经验。现任阿里云解决方案架构师，熟悉医疗、教育和游戏行业，帮助多家大中型企业完成系统上云、出海架构规划。 在全球化浪潮不断加速的背景下，越来越多的游戏企业开始走向海外市场，但随之而来的技术挑战也愈加复杂。本次报告将从出海游戏的实际需求出发，结合案例，分享阿里云全球基建、系统架构等方面的优势和实践结果。帮助客户快速高效的开展海外业务，探讨阿里云提供的云产品和解决方案如何助力游戏企业实现稳定、高效、安全的出海之路，希望能为正在或计划出海的游戏客户提供有价值的参考与启发。 ➨ 移动端Unity游戏功耗调优的破局实践 周旋 侑虎科技 引擎开发工程师 随着移动端Unity游戏在画质精度与玩法复杂度上的持续突破，功耗问题已成为制约用户体验的核心瓶颈 - 过度发热不仅导致设备降频、性能波动，更会直接影响玩家留存与口碑传播。然而，当前行业对功耗的优化仍停留在经验驱动阶段：CPU主线程压力、子线程负载、GPU算力消耗、带宽占用等关键因素与发热之间的定量关联缺乏直观呈现，开发者往往陷入“盲目排查 - 试错优化 - 效果模糊” 的低效循环。 正是基于这一痛点，本次分享旨在为开发者提供一套可量化的功耗调优方法论。过去一年，UWA团队深耕移动端功耗优化领域，依托Perfetto性能分析工具与ODPM（On-Device Power Metering）功耗数据采集方案，构建了从底层机制到项目落地的全链路分析体系。 通过本次分享，我们期望帮助开发者建立“精准定位 - 科学优化 - 效果可测”的功耗调优闭环，切实解决项目发热难题，助力打造低耗高效、持久稳定的移动端游戏体验。 ➨ 从App到多平台小游戏 - 高效迁移的全链路优化策略 周旋 侑虎科技 引擎开发工程师 随着小游戏平台的流量红利与用户触达优势日益凸显，将成熟Unity App游戏迁移至多平台小游戏已成为行业重要的拓展方向。然而，这种跨平台转化往往面临引擎适配壁垒、性能瓶颈突出、资源约束严苛等系列挑战，导致多数团队在迁移过程中效率低下、效果不达预期。 过去一年，UWA团队深度参与数十个Unity游戏向微信、抖音等主流小游戏平台的迁移项目，在实战中系统性沉淀了覆盖迁移全流程的优化方法论。本次分享将聚焦迁移关键内容，包括：同步加载转异步加载的方案；URP性能优化；内存优化和iOS平台的渲染异常表现修正等。 我们期望通过这套经过验证的全链路策略，助力开发者规避迁移陷阱，实现从App到多平台小游戏的高效转化与体验升级。 ➨ 基于自定义格式对Unity场景的深度性能优化 李俊 欢乐互娱科技有限公司 引擎组组长 欢乐互娱引擎组长，曾任青果灵动黑光工作室技术负责人，字节跳动客户端主程序，技术专家。曾参与《黑暗之光》、《魔法王座》、《放置少女》、《RO仙境》等多款MMORPG和卡牌手游的研发。 重度MMO游戏中，大世界的场景加载效率、内存、渲染效率都是游戏性能的瓶颈。从提升流式块的加载速度出发，提出了无解码的自定义场景格式，大幅提升了场景流式加载速度，但是因为场景已经没有组件了，Unity原先支持的机制不再支持。在重新造轮子过程中，我们深度优化了场景内存、渲染提交等方面的性能，同时解决了被引入的新问题。本次报告将重点介绍的内容包括：无解码的自定义场景格式实现；Lightmap流式实现；自定义格式下地形渲染；基于Batchrendergroup的渲染优化等。 ➨ “AI拼UI”的探索历程 罗培羽 广州四三九九 主程序 就职于4399技术研发中心，负责前沿探索和技术支撑工作。著有《Unity3D网络游戏实战》、《百万在线：大型游戏服务端开发》等书籍。 “拼UI”一直是游戏前端开发的一个痛点，工作量大且繁琐，尽管已有诸如PSD2UGUI、FairyGUI等方案，但却也未能完全解决问题，很多时候是把程序的工作量转移到美术而已。4399公司的游戏迭代速度很快，“拼UI”是一个突出痛点，因此开展了使用图像识别方法辅助拼UI的相关研究。本次报告将叙述这一研究历程，包括最初使用传统的图像识别方法、后续分析PSD结构的迭代算法，以及使用大语言模型来辅助识别UI结构的探索。尽管“AI自动拼UI”离落地尚有距离，但这一课题是行业共同面对的痛点，通过分享其中的探索历程，以作启发。 ➨ Vibe Playing & Agentic & 共创游戏：新游戏体验与商业模型 蔡俊鸿（卡卡）潜在领域 CEO、CTO 游戏全栈开发者，游戏热爱者，极客 人工智能不仅正在改变游戏的开发方式，更正在重塑玩家体验与商业逻辑。从“千人一面”的任务到因人而异的剧情，从静态付费点到动态行为预测与实时个性化触达，AI正在成为驱动游戏留存与变现的新引擎，未来的游戏，不再只是研发人员为玩家开发，而是玩家与AI共创。本次分享将围绕从Generative AI驱动的内容生成，到Agentic设计理念下的动态剧情与交互体验；从AI Agents对玩家行为的实时响应，到Vibe Playing带来的沉浸式参与与内容共创，探讨如何用AI提升玩家留存与付费。如果你是研发、策划、或者是对AI充满热情的探索者，希望能为你打开一个Agentic Game Design的全新视角。 ➨ 探索基于模拟现实的应用开发 - 以Godot引擎为基础的辅助驾驶可视化案例 张骋 莲花跑车 高级技术专家，UWA学堂作者 10年游戏行业和8年汽车行业辅助驾驶开发经验，现就职于莲花跑车智驾部门，负责辅助驾驶人机交互开发工作，将设计优秀的技术解决方案视为长期事业。 模拟现实应用开发已经在生活各处逐渐出现，如智能家具、辅助驾驶等。目前辅助驾驶系统的高级可视化已成为了中国新能源汽车的标配。高级可视化功能需要精美的视觉效果和自由交互的能力，同时还需满足较为复杂的开发环境要求。本次报告将通过我们的开发实践，分享基于Godot引擎实现高级可视化的经验，相信程序化内容和受限环境的开发经验对Unity引擎用户及其它机器人交互系统的开发者也有很高的参考性。 ➨ 性能优化新基建：GPM 2.0，如何从 “被动修复” 到“主动赋能”，重塑游戏竞争力底层逻辑 张鑫 侑虎科技 CEO 在传统游戏性能管理模式中，卡顿、发热、降频等影响体验的问题，往往需依赖玩家反馈或舆情发酵后才被察觉。研发团队不仅要消耗巨大人力成本复现问题场景，更易因响应滞后错失最佳修复窗口，最终导致用户流失与口碑损耗。 UWA GPM 2.0 的推出，标志着游戏性能管理进入“新基建”时代 - 通过构建“主动赋能”的全新范式，实现从被动响应到主动预判的关键跨越。 本次分享将聚焦GPM 2.0的核心能力：解析其如何基于玩家真实行为数据构建分析体系，实现性能风险的及时识别；详解运行时卡顿帧分析功能，如何精准定位卡顿帧逻辑、代码耗时点与资源加载瓶颈；阐述崩溃检测分析模块如何实时捕获崩溃前的场景状态、堆栈信息，为根因定位提供完整依据。 通过GPM 2.0，研发团队可在用户感知抱怨前锁定问题核心，将性能管理转化为前置化的体验优化能力，从而显著降低用户流失率，以技术基建的升级重塑游戏竞争力的底层逻辑。 ➨ 搞性能，不一定是和美术做对抗 - 美术向TA在性能优化中的思路分享 Wek（韦克） 技术美术专家，《矩阵零日危机》TA负责人 北京电影学院毕业，10年游戏工作经验，曾参与项目《一梦江湖》（原名楚留香）、《燕云十六声》、《矩阵零日危机》。 不少项目在性能优化的时候都会遇到推进有阻力的问题，甚至有的项目会出现临近上线加班优化性能，但是最后还是不得已砍掉一些效果的情况。《矩阵零日危机》从立项到上线一直没有遇到类似问题，可以实现较好的性能和美术效果的平衡。本次报告将分享美术向TA在性能优化中的思路和方法。 ➨ Unity引擎资源管理经验 - 如何使用YooAsset进行高效的资源管理 何冠峰 途游游戏 技术专家 YooAsset资源系统作者，长期专注于游戏开发领域，热爱技术研究和开源社区建设。技术特长包括游戏资源管理，游戏框架设计与性能优化。 在使用Unity引擎开发移动端游戏时，资源管理至关重要。合理的资源管理可以优化游戏的运行效率，减少加载时间，提高用户留存率。本次报告将深入讲解Unity资源管理，从资产管理到性能优化，全面讲解YooAsset的高效能应用。重点内容涵盖：资源管理与打包策略、自动化脚本配置、加载卡顿优化与调试工具使用，以及常见问题的解决方案。 ➨ 《仙遇》MMO手游工具链设计的思考与实践 李博皇 海南元游信息技术有限公司 客户端主程 拥有多年游戏研发与项目上线经验，专注技术创新与突破，持续推动协作效率与产品品质的提升。 制作一款大型MMO手游，工具链不仅是技术实现的手段，更是保障研发效率与产品品质的核心支撑。本次报告将围绕《仙遇》项目从立项、研发、上线到运营全过程，探讨所采用的工具链设计理念及其背后的原因，聚焦于为什么要有这些工具及其设计初衷（Why），以及它们如何提升团队协作效率和优化产品性能。通过回顾工具链的设计与迭代，分享实战经验，期待为同行提供参考。 ➨ 如何用物理驱动游戏角色 许晨光 （Shadow） Nubrain Game Studio 工程师 独立游戏《无极道人》开发者。 在各种游戏和3A大作中，为玩家提供更丰富可信的角色动画一直是业界追求的方向之一。基于物理的角色运动系统是一种利用物理引擎实时模拟角色运动和动画的游戏开发技术。与传统的预设动画驱动的角色控制不同，它通过力学计算各种运动中的力和碰撞反馈等效果来提供更丰富的视觉表现，以及实时与周围环境物体交互的能力。本次报告将介绍如何实现一个基础的由物理驱动的角色运动系统，以及对于传统物理引擎的算法在驱动物理角色时遇到的一些问题的改进思路和尝试。 ➨ Unity移动游戏工业级性能优化指南 3.0 余含章 侑虎科技 技术支持工程师 在过去一年中，UWA团队持续深耕于各项目性能优化一线，聚焦游戏研发过程中的各类疑难问题，不断沉淀实战经验并致力于向行业开发者分享。本次分享的《Unity移动游戏工业级性能优化指南 3.0》，在内容上实现了多维度的升级与深化： 其一，针对数年前完成的内存与加载测试进行重置与细化，在Unity 2022版本普及率持续提升的当下，重新验证既有结论的适配性与可靠性； 其二，系统梳理了新版Unity的高频问题，涵盖新接口评测、内存“黑洞” 定位等核心痛点，提供针对性分析； 其三，专项剖析iOS包相较于Android包更易出现的内存紧张、闪退等差异化问题，并配套输出经过实践验证的优化策略与落地经验。 我们期望通过本次分享，助力开发者更精准地解读数据背后的逻辑，共同探索性能优化的深层价值。 ➨ Tripo 3D大模型如何赋能全球游戏行业 宋亚宸 VAST 创始人、CEO VAST创始⼈兼⾸席执⾏官，本科毕业于约翰霍普⾦斯⼤学，曾在商汤科技落地了多个从零到⼀的⼈⼯智能项⽬，实现AIGC技术在动画及游戏行业的商业化落地，也曾参与创⽴通用大模型独⻆兽MiniMax。 为了适应全球用户对于3D用户体验升级、内容表达创新和新质生产力提升的持续追求，Tripo Studio重新定义了AI驱动的3D内容创作流程。本次报告将围绕VAST正式发布全球首个AI驱动的一站式3D工作台Tripo Studio展开探讨，例如：如何让每个创意都能从模糊构想走向完整成果，让3D创作不再止步于模型生成，而是获得完整、可用、有价值的可交互3D内容。 我们期待能为相关领域从业者提供“基础模型 + 生态插件 + 原生工作台”的端到端产品体系，从而构建覆盖从专业级（PGC 生产者）、达人级（PUGC 创作者）到大众级（UGC 用户）的创作者，进而推动全球3D内容创作领域新发展。 ➨ 3D MMORPG手游征服2GB设备之历程 张益宁 欢乐互娱科技有限公司 技术专家 《仙境传说：爱如初见》性能优化负责人，擅长技术架构，性能优化。喜欢为玩家带来极致游戏体验的同时，探索技术的极限。曾就职于腾讯，字节，吉比特，趣加。 3DMMO游戏在全球受众广泛，但因其复杂的业务需求，在2GB/3GB手机上内存面临极大的挑战，而这一部分用户，对于发行买量，游戏生态的构建，又是不可忽略的群体，基本每一个项目都会花费大量的精力在内存优化上。本次报告主要介绍一种系统的解决3DMMO游戏内存问题的思路，让3DMMO游戏畅通无阻的覆盖全球手机设备。 ➨ 《心动小镇》内存优化经验谈 刘贞明 心动网络 小镇技术组负责人 2009年进入游戏行业，先后供职于2K Games、CCP Games、Red Accent以及心动网络，有Console与Mobile平台的开发经验，并参与了心动多款自研项目的性能优化工作。 本次报告主要介绍《心动小镇》项目在内存优化过程中遇到的挑战和经验。具体包括： 项目的设计特点和在内存方面的挑战，例如：大世界、无缝集成家园、家园承载上限、潮流季等动态活动内容。 项目内存问题的分析与定位的流程、工具、方法和经验。 项目线上数据收集方法：自研工具及线上数据分析的方法及帮助。 我们为《心动小镇》实施的内存方面的优化方案，例如：针对资产系统（AssetBundle）的分析与方案、ECS化（去GameObject化：地形、草、树、岩石、房屋、HLOD、BRG等、Streaming系统、物理方案、Significance Manager、Jobs Animator、Shader的优化、配置数据库化、资产方面的优化（Mesh精度压缩、骨骼层级优化、贴图通道合并等），以及引擎相关的优化方案。 ➨ 动态大规模场景破碎解决方案，及有重量感的角色物理场景交互 唐翎（flashyiyi） 幽灵猫科技 独立游戏开发者 6年游戏行业经验，5年Unreal图形引擎/技术美术，知乎作者，知乎5W关注。曾在《影之刃零》和网易《归唐》项目任职；目前在制作个人独立游戏《猫咪漂移》。 本次报告将会介绍一种少量修改引擎代码，就可以让Unreal的Chaos物理引擎支持大规模的破碎且确保卡顿现象不出现的方案，以及一种可以避免角色和模拟物体碰撞偶然产生的“超高速弹飞”，“挤压抽搐”现象的方法。这两个方案在技术层面上可以基本解决开发者在游戏中加入实时物理模拟的顾虑。 十周年特别福利｜致敬每一位支持者 为感谢广大开发者一路以来的信任与陪伴，UWA在十周年之际特别推出【UWA用户专属免费票】！数量有限，先到先得。点击即可报名！ 活动信息 主办方：侑虎科技 时 间：2025年8月1日~8月2日（周五、周六） 地 点：上海市静安区广中西路333号 上海宝华万豪酒店 扫描上方图片二维码报名 感谢每一位提前锁票的你，是你们的热情与期待，让这个八月注定不凡。 让我们在代码的碰撞与灵感的激荡中，一起书写游戏产业的下一个黄金十年！ 了解更多大会详情，敬请关注官方平台后续发布。UWA DAY 2025，期待与你现场相见！ 往届精彩盘点 UWA DAY 2024｜创新潜藏无限可能 UWA DAY 2022｜躬身入局 践行游戏研发工业化 UWA DAY 2021｜助力游戏研发迈入工业化时代 UWA DAY 2020｜品往鉴来 质存高远 UWA DAY 2019｜以数之名，赋能游戏精品化 UWA DAY 2018｜快、精准、有温度 UWA DAY 2017｜没有横空出世，只有厚积薄发 关于UWA UWA是一家创业十年的高新技术企业，作为游戏行业的深耕者，UWA始终专注于为使用Unity、Unreal引擎的开发者提供丰富的优化产品，帮助开发者高效解决开发问题、定位性能瓶颈、提供解决方案，已支持超过一万款游戏项目。还打造了技术博客、问答、开源库、学堂等社区产品，为开发者提供便利和高效的支持。线上培训和线下教育的新业务， 满足行业对人才培育的需求。
自2017年首届「UWA DAY」举办以来，每一年的UWA DAY都如同与老友的相聚，大家齐聚一堂，共同探讨行业发展、切磋技术方案，携手进步，收获颇丰。 今年UWA DAY 2024以「创新潜藏无限可能」为主题，为大家带来了26场精彩纷呈的演讲，涵盖了App/小游戏开发、优化、质量保障及前沿技术等主题。同时，还有以“AI数字人”为主题的Unreal引擎工作坊，深入探讨行业前沿动态。 创新潜藏无限可能 主题演讲 https://edu.uwa4d.com/course-intro/1/567 近年来，随着移动游戏研发成本的显著上升和小游戏平台的逐步成熟，中国游戏行业正呈现出明显的两极化趋势。移动游戏团队不断追求高品质的研发，深入推进工业化研发流程；而小游戏团队则采用更高效、更灵活的方式来开发或移植游戏，力求迅速进入市场。这种变化为我们UWA带来了更高的要求，但同时也带来了新的挑战和机遇。 在过去的两年中，UWA在多个方面进行了深入探索。无论是工业化研发的进一步落地，还是小游戏的移植优化，我们都取得了显著的进展和突破。在这次报告中，我们将分享UWA在这两年中的主要工作和成果，重点包括： 工业化研发流程的持续创新 小游戏研发和优化方面的经验积累 玩家端性能分析工具的研发 在当前的游戏行业大环境下，我们相信，唯有持续创新和积极投入，才能找到破局之道。希望通过本次分享，能够为研发团队在移动游戏和小游戏的开发中带来更多的启示和推动力。 Gears — 运行时性能测试与GPU深度分析 https://edu.uwa4d.com/course-intro/1/568 UWA Gears 是侑虎科技近期发布的一款客户端无SDK性能分析工具，具有运行时性能测试与GPU深度分析两大分析模式。本次报告将对Gears进行基本的功能介绍与演示，并结合实际案例，详细讲解运行时需要关注的重要指标，分析App的性能瓶颈。以及通过Gears的截帧分析功能，排查渲染问题，分析渲染管线细节，定位GPU压力来源等。 也可参考本文介绍《UWA Gears正式上线，助力移动平台性能优化》 如何通过UWA GPM 2.0高效预警和定位玩家端的性能问题 https://edu.uwa4d.com/course-intro/1/569 近年来，随着大量新游戏的涌现，玩家对游戏品质和服务质量的要求日益提高。在众多同类产品中脱颖而出，成为各研发团队的首要任务。其中，性能体验是一个关键因素。那么，如何快速、精准地定位玩家端遇到的问题，成为了UWA近两年来的研究和解决重点。 此次报告将重点介绍我们在这一领域的进展，主要内容包括： 如何快速了解游戏项目上线后每个版本在玩家端的性能体验 如何快速掌握每个场景或局部玩法环节在玩家端的性能压力 如何高效定位每台设备的具体性能问题 我们希望通过UWA GPM2.0，研发团队不再需要等到玩家主动反馈时才发现问题，而是可以实时获悉版本、场景甚至单个玩家端的性能问题，并进行快速调整，从而进一步提升游戏质量和服务品质。希望此次分享能为研发团队在游戏性能优化方面提供有价值的参考和帮助。 也可参考本文介绍《如何高效预警和定位玩家端的性能问题｜UWA GPM 2.0 新品发布》 《忍者必须死》全球同服架构改造和上线前后经验分享 https://edu.uwa4d.com/course-intro/1/570 《忍者必须死》在海外上线的过程中，为了适配海外环境并达到全球同服的业务目标，我们面临一系列的技术选型以及对服务器架构的改造工作。本次报告首先介绍我们关于全球同服改造的背后思考和相关工作；然后介绍项目上线前的各类准备工作，包括压测、容灾测试、网络延迟优化和运维等。最后将介绍游戏上线后，为了降低云成本，项目组将DocumentDB改为对象存储的优化工作。 Unity移动游戏性能优化全解析与小游戏转化的实战经验 https://edu.uwa4d.com/course-intro/1/571 App和小游戏的性能优化是研发团队持续关注的热点，UWA在过往的报告中曾从各个维度讲解过关于性能优化的经验，那今年的报告又有何亮点？ 在本次报告中，我们总结了关于Unity移动游戏的优化案例以及由App转微信小游戏的经验分享，重点包括： 原理简单但易忽视的误区、老生常谈但仍高频出现的问题 稍作调整后性能表现就能明显提升的设置 针对来源不明的高内存占用拆解方式 排查项目发热原因等颇具挑战性的疑难杂症的排查思路 App转小游戏过程中的常见问题、性能采集工具的使用案例，以及URP项目的优化经验 用JetBrains Rider和TeamCity助力游戏开发的高效秘诀 https://edu.uwa4d.com/course-intro/1/572 你是否期待在游戏开发过程中获得更高效的开发方式？本次报告就将探讨JetBrains Rider和TeamCity如何助力Unity和Unreal引擎的游戏开发，显著提升开发效率。内容将演示Rider强大的IDE功能，包括快捷键技巧、代码重构、调试功能等，让你轻松编程、加速完成项目工作。另外，也会介绍TeamCity在测试和构建游戏项目的重要作用，包括针对Unity和Unreal引擎设计的构建任务、依项目需求搭建流水线、多平台发布等，除了优化工作流外，更能实现极速开发。 《诛仙2》中的全平台大世界游戏性能优化技巧分享 https://edu.uwa4d.com/course-intro/1/573 本次报告将介绍《诛仙2 》三端互通MMO大地图项目的性能规划、立体大地图的性能优化；在MMO游戏复杂场景下对多玩家场景多角色的优化；对多端游戏进行画质分级的经验总结，以及如何从性能分析工具中提取具有意义的信息。 优化与创新：Python在虚幻引擎工具开发中的高级应用 https://edu.uwa4d.com/course-intro/1/574 本次报告中，我们将深入探讨Python在虚幻引擎工具开发中的高级应用。内容涵盖TAPython插件的核心功能与实现、动态组件和复合编辑器工具的开发。通过实际示例，展示如何高效生成PCG场景、使用AI生成贴图纹理、实现数据可视化及场景优化等。同时将分享开发过程中遇到的挑战和解决方案，与您共同探索提升工具开发效率与效果的最佳实践。 UE项目工程化 - 经典端游的快速移动化方案 https://edu.uwa4d.com/course-intro/1/575 基于已经失传的古老游戏引擎开发的经典的3D端游，通过对其技术进行系统性的分析、拆解、规划、推进、整合，利用本次报告将一起探讨基于UE如何做工程规划、技术选型，如何转换底层系统和业务系统。 《鸣潮》编写高性能TypeScript代码的较佳实践 https://edu.uwa4d.com/course-intro/1/576 《鸣潮》项目的脚本语言选型是TypeScript，基于Puerts插件。TypeScript有类型系统，有VSCode的天然IDE支持，在代码编写速度、重构能力上，有较大的效率优势。在Runtime，TypeScript会被编译为JavaScript，运行在以高性能著称的V8引擎上。但是，如果不理解V8的一些设计思路和实现细节，就可能会写出性能不好的代码。本次报告会从V8的Element Type，Hidden Class，Deoptimize等概念入手，讲解实际编码中可能遇到的性能陷阱，给出高效代码的编写建议，以及如何监控此类性能问题的方式。 Unreal游戏优化实践 - 从平台出发我们都做了什么 https://edu.uwa4d.com/course-intro/1/577 性能对于游戏品质而言非常的重要，作为平台部门，我们需要有一套优化体系与方法。本次报告将分享珠海西山居质量中心团队从制定标准、监控资源、自动化工作流等方面所搭建的优化体系；同时讲解Unreal项目优化内存和提升帧率为目标所运用的几类工具和使用方法；最后分享优化过程中所遇到的问题以及解决方案等。 如何在Apple Vision Pro环境中创造充满沉浸感的大世界 https://edu.uwa4d.com/course-intro/1/578 本次报告旨在总结Apple Vision Pro作为入口的空间计算相关开发经验，以场景表现为侧重，探讨在以下结合点的效率效果平衡： PC/移动端移植，常用工具在AVP平台的支持与否及相关替代方案； 镜头语言结合，AVP不同模式下及timeline功能的善用以提升叙事力； 沉浸模式下的空间/边界探索、环境叙事、及大世界解决方案讨论等。 AI在游戏资源产出过程中的运用 https://edu.uwa4d.com/course-intro/1/579 本次报告将概述AI在游戏美术资源产出的实际运用，从起草、细化到成稿等各阶段的具体操作流程，以及各种功能的发散和实例介绍。这套方案可以运用于不同美术风格的项目，辅助资源产出，提高产量，缩减流程周期，提供概念参考，快速验证风格效果，减少项目立项前期的测试成本。 基于UWA Pipeline搭建《忍者必须死3》性能管线 https://edu.uwa4d.com/course-intro/1/580 《忍者必须死3》作为一款长线运营的线上项目，游戏内容和表现力持续增加和提升，为了保证玩家的顺畅游戏体验，我们对性能进行了持续的关注和优化。为此，我们基于UWA Pipeline落地搭建了自定义的性能管线，规范明确了持续生产过程中的游戏内容性能审核标准，有效提高了排查、解决性能问题的能力和效率，并最终获得了较好的客户端性能优化效果。本次报告较为完整地呈现和介绍了成熟的在线运营《忍者必须死3》项目是如何落地搭建自定义的性能管线，UWA Pipeline在其中提供的帮助与作用，并将分享项目的三个真实案例。 《青云诀之伏魔》海量服务器的降本优化解决方案 https://edu.uwa4d.com/course-intro/1/581 《青云诀之伏魔》游戏采用的是小服生态模式，这意味着虽然每个服务器的负载相对较低，但服务器总数却非常庞大，这种模式带来了较高的运维成本。为了降低成本，我们的目标是在单个云服务器上尽可能多地部署单服，从而节省资源。本次报告将介绍项目组在服务架构、CPU、内存和网络等方面采取的有效措施和实践经验。我们的优化重点不在于提升常规的服务响应速度或增加负载能力，而是更加专注于减少数量众多的非活跃服务器所占用的资源。 MongoDB开发者数据平台 - 助力游戏企业高效开发 https://edu.uwa4d.com/course-intro/1/582 随着游戏行业的快速发展，游戏开发者面临着越来越复杂的技术挑战，尤其是在数据处理和人工智能（AI）应用方面。MongoDB作为领先的NoSQL数据库解决方案，提供了强大的工具和灵活的架构，能够帮助游戏开发者应对这些挑战，实现卓越的游戏体验。本次报告将通过核心功能介绍和案例讲解，为游戏开发者提供解决高性能数据读写、数据一致性与可用性、扩展性，以及查询与分析能力的解决方案。 游戏性能分析与挖掘 https://edu.uwa4d.com/course-intro/1/583 性能分析是性能优化的关键之一，通过分析我们能掌握性能的瓶颈点和性能问题的来龙去脉，这有助于提高我们做性能优化时的效率和效果。本次报告将分享性能分析技巧和性能工具方案，包含实用的性能优化策略、如何做性能分析、性能工具原理与改进方案。 《合金弹头：觉醒》 - 基于Unity引擎的多线程解决方案 https://edu.uwa4d.com/course-intro/1/584 介绍《合金弹头：觉醒》项目多线程改造方案。围绕整个多线程改造，讲述背后原因、线程模型、性能优化、物理系统、资源等如何处理，以及过程中遇到的相关技术问题。 利用CUDA加速ASTC率失真优化，实现手游纹理数据存储极致缩减工作流 https://edu.uwa4d.com/course-intro/1/585 随着手游品质的提升，资源包体积不断增大，直接影响国内运营策略和海外发行。受视频高压缩率RDO编码压缩的启发，RDO编码也可以应用于纹理分块编码的ASTC有损压缩格式，使纹理的压缩率得到了大幅提高。本方案在Unity和Unreal等不同引擎项目中都具有良好的兼容性。本次报告分享内容将包括纹理RDO处理的技术原理，以及使用CUDA GPU并行工作流架构。 通过分布式编译及缓存技术加快游戏及引擎编译速度 https://edu.uwa4d.com/course-intro/1/586 游戏开发中，开发商通常需要对引擎进行修改或二次开发以适用自身的游戏开发业务场景，其中Unreal引擎由于具有8000+ 级别的cpp文件数量，开发人员每次Rebuild通常需要30分钟以上的编译时间。另外基于Unreal引擎的游戏客户端打包也会涉及大量的shader任务编译，通常也需要30分钟甚至几个小时的时长。这漫长的编译时间大大拖慢了开发效率。本次报告将结合游戏开发中常用的IDE：Visual Studio和JetBrains Rider，分享基于现有的CPU资源，如何通过分布式构建的方式大幅缩短引擎及shader编译时间，提升开发效率。 Unity逻辑代码模块性能优化 https://edu.uwa4d.com/course-intro/1/587 本次报告通过大量的逻辑代码模块的性能优化案例，系统地总结了逻辑代码相关的定位和优化方法。包括C#和Lua脚本的CPU耗时、内存分配和内存泄漏等，以及针对逻辑代码在HybridCLR、小游戏等相关方面的注意点和常见问题等。 如何及时发现线上问题 - 运营期游戏的监控体系分享 https://edu.uwa4d.com/course-intro/1/588 线上监控对于每一个产品都是至关重要的，本次报告将从线上事故出发，针对不断迭代变化的需求，如何运用各种工具链建立线上监控，再由此扩展阐述我们现有的几大监控平台，并进一步阐述如何确保监控发出后的落地效果。 小游戏的引擎选型与实践 https://edu.uwa4d.com/course-intro/1/589 从2022年开始，国内的小游戏逐步成为行业新的显著增长点，而游戏引擎成为我们现在制作游戏的核心工具。对具体引擎进行选型，这涉及到一系列的考虑因素，包括：游戏项目的目标平台、团队经验与开发风格、甚至团队在中长期的技术发展规划。本次报告议题从小游戏平台的项目出发，分享相关开发经验，供同行参考：小游戏项目的引擎选型评估、Laya 引擎项目案例的技术数据，以及Laya引擎的实战经验。 《钢岚》的画面和渲染管线开发实践 https://edu.uwa4d.com/course-intro/1/590 《钢岚》是一款由Blackjack Studio开发的，写实机甲风格的战棋游戏。本次报告中将着重介绍《钢岚》项目中SRP迭代的经验详情，同时也将介绍研发过程中使用的各类开发工具，以及这些工具是如何帮助美术和TA提高画面一致性，提高生产效率和保障资产健康的。 野生TA：美术力与技术力的平衡 https://edu.uwa4d.com/course-intro/1/591 在CG行业中，往往会过度关注技术的极限，忽视了美术创作的重要性。本次报告将着重介绍建立面向审美的技术迭代机制，提高对用户体验和美学诉求的重视，将美学目标融入技术路标，持续优化作品的视觉呈现。 GPU压力衡量方法与案例分析 https://edu.uwa4d.com/course-intro/1/592 移动游戏对高品质画面的追求日益升温，不同GPU芯片之间的性能差异及可能导致的GPU瓶颈问题，让优化工作变得更具挑战性。本次报告将以GPU性能优化为主线，介绍GPU压力的判定及其性能问题来源的定位、画质分级中的重要指标的说明、渲染特性对GPU压力的影响以及如何合理的对其进行分级。通过对渲染问题的剖析让研发团队更高效的优化GPU性能，让画面与性能表现得到充分的平衡。 Unreal引擎工作坊 大会期间，我们还开设了以“AI数字人”为主题的「Unreal引擎工作坊」。来自华东理工大学、上海音乐学院、中国传媒大学、青岛电影学院、上海济光职业技术学院等多所高校的师生，以及来自游戏、汽车、电影拍摄等领域，有意向从事Unreal引擎开发的学员齐聚一堂。工作坊由Unreal引擎授权讲师、UWA引擎开发工程师任治国授课，带领学员们进行现场实操，深入探索Unreal引擎的应用。 再次感谢各位讲师的精彩分享。我们深信，技术的进步与行业的发展，离不开每一位开发者的共同推动。每一次宝贵的技术和经验分享，都为未来的创新奠定了基础。无论前路多艰难，我们都会坚定前行，愿继续成为开发者们的明灯与伙伴，携手共进。期待明年再次相聚！
1）Unity性能优化分析思路 ​2）资源打包关系依赖树 3）SpriteAtlas 中Include in Build的作用 4）使用Streaming Mipmap后纹理内存没有下降的疑问 5）URP Renderer Feature实现二次元描边，Cutout的处理问题 UWA每周推送的知识型栏目《厚积薄发 | 技术分享》已经伴随大家走过了330个工作周。精选了2022年上半年的精彩问答分享给大家，期待UWA问答继续有您的陪伴。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Unity Q：Unity有多少优化点？比如合批：静态合批、SRP合批、GPU实例化、UGUI Reruild、光照烘焙、反射探针、光照探针、Shader.Parse、Shader.CreateGPUProgram、场景加载优化和GC优化，还有哪些优化点？ A1：大方向上可以从CPU、内存、GPU这三个方向切入。 细分一下可以从CPU、内存、渲染、资源优化、耗电优化、网络优化、卡顿优化、优化工具的选择掌握这几个点入手。 一、CPU优化 缓存计算结果 预处理 限帧法 主次法 多线程 引擎模块（动画、物理、粒子、导航） 逻辑优化 二、内存优化 缓存法 内存池 资源管理器 控制GC 逻辑优化 Shader变体数量优化 三、渲染优化 SetPassCall渲染状态切换频次控制 DrawCall数量控制 带宽负载 显存占用 GPU计算量 四、卡顿优化 降帧法 摊帧法 限制数量法 逻辑优化 IO优化 使用进度条 五、资源优化 纹理优化 UI优化 字体优化 模型优化 场景优化 粒子优化 材质优化 指定标准美术规范 Shader变体数量优化 六、耗电优化 上面说到的优化点，或多或少都会影响到手机的耗电，也是优化耗电的措施，除此之外还有： 动态调整限帧 动态调整画质 七、网络优化 减少无用字段 降低字段精度 避免重复发送 网络异步化 压缩无效字节 压缩协议包 以上说的这些要点，大部分摘抄归纳自《移动游戏性能优化通用技法》。强烈建议多花些时间认真阅读一下这篇文章，然后以这篇文章作为指南，再去仔细研究里面提到的优化细节该如何展开。 感谢马三小伙儿@UWA问答社区提供了回答 A2：优化点肯定是无穷无尽的，这里搬运UWA的客户端性能优化思路，针对常见的引擎模块的相关问题都做了分析，讲的是比较全比较透的，常见的优化难题都罗列了。 《Unity性能优化 — 物理模块》 《Unity性能优化 — 动画模块》 《Unity性能优化系列 — 资源内存泄漏》 《Unity性能优化系列—Lua代码优化》 《粒子系统优化——如何优化你的技能特效》 《Unity性能优化系列—加载与资源管理》 《Unity性能优化系列—渲染模块》 《Unity性能优化 — UI模块》 《支持资源加载分析、场景分割》 《UWA报告使用小技巧，你get了吗？》 《UWA本地资源检测更新，助你严守项目性能的每个角落！》 感谢芭妮妮@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61a0a56ed8413e18eb297ac3 AssetBundle Q：想做包体资源分析，大家有什么好的树显示工具或者思路推荐吗？有比较好的开源方案也可以。最简单就像N叉树一样，比如root一个文件名，然后展开整个树结构。 A1：我自己做了一个，供参考。都是用Unity自己的IMGUI最基本的接口去实现。 EditorWindows GUI.Box GUI.BeginGroup GUI.Label Handles.DrawBezier Handles.DrawWireDisc TreeView 基本上，组织好各个AssetBundle的依赖关系其实是很好呈现的。 感谢黄程@UWA问答社区提供了回答 A2：推荐一款比较好用的插件，不止有依赖树，还有其他打包的资源数据可供分析： https://assetstore.unity.com/packages/tools/utilities/build-report-tool-8162 感谢郑骁@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/623c8322a43e6117d040ae9b AssetBundle Q：SpriteAtlas中Include in Build的作用是什么？ A：专门做了一些测试，具体如下： 以下表达中Sprite对应的是Sprite类型的对象，Texture2D对应的是Texture2D的对象，这和Sprite对象是完全不同的东西，sactx表示生成的图集纹理。 测试情况包括2个变量： SpriteAtlas对象是否主动打包AssetBundle SpriteAtlas对象上是否勾选Include in Build 第一种情况，SpriteAtlas打包AssetBundle： 那这里要考虑的是SpriteAtlas引用的Sprite是否会单独打包，如果这些小Sprite不主动打包，是会被动进这个SpriteAtlas的AssetBundle里面的，如果其他的UI Prefab中，比如有个Image使用了一个小Sprite，那么这个小Sprite就冗余了。 这里勾不勾选Include in Build的区别在于：加载Image的时候，这个Image会不会自动显示，勾选了Include in Build，会自动显示图片，不勾选，则需要脚本添加回调来主动加载SpriteAtlas，并callback（spriteatlas）。 第二种情况，SpriteAtlas不加入AssetBundle打包： 不勾选Include in Build 假设小的Sprite打包AssetBundle，在这个AssetBundle里面不会有sactx，这个sactx的Texture2D的纹理变成“消失”的状态，没有任何东西可以引用到这个sactx纹理，而且由于在工程里面有SpriteAtlas的存在，所以在小的Sprite的AssetBundle里面也不能让其本身对应的小的Texture2D纹理进AssetBundle包，所以图像就永远显示不出来了。 勾选Include in Build 所有的小的Sprite所在的AssetBundle里面都会被动包含sactx的图，且会包含所有没有主动打包的小的Sprite。 如Sprite1和Sprite2是SpriteAtlas里面的两个小的Sprite。Sprite1主动打包，Sprite2不主动打包，那么Sprite1的AssetBundle里面是会有Sprite1和Sprite2以及sactx纹理。 总结： 如果有Sprite加入了某个SpriteAtlas，那么任何真正使用到这个Sprite的资源都不会有对Sprite对应的小的Texture2D纹理的引用，而是对sactx图集纹理的引用。 如果SpriteAtlas不打包，必须勾选Include in Build，否则sactx纹理就“消失”了，在勾选Include in Build的前提下，而且SpriteAtlas中的所有小的sprite必须打包到同一个AssetBundle里面，否则sactx会冗余。 如果SpriteAtlas打包了AssetBundle，sactx永远不会冗余了（这里的冗余是指打包AssetBundle造成的冗余）。SpriteAtlas里面的小的Sprite也最好打包AssetBundle，不然这些小的Sprite就会冗余。勾选或者不勾选Include in Build都不影响各种依赖关系，唯一的区别是是否会主动显示图片，勾选了就会主动显示图片，不勾选就需要脚本控制来显示图片。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61dbee5cd8413e18eb7bcc3e Texture Q：为什么我在项目中使用了Streaming Mipmap但是在GOT报告中看纹理内存没有下降？是没有正确生效还是统计有问题？ A：之前做过相关测试，发现GOT Online是可以统计到被Streaming Mipmap影响的纹理的正确内存的，所以我推测你遇到的情况大概率还是没有正确生效导致的。以下是对如何让一张纹理应用Streaming Mipmap的简单流程总结，其中会注明需要特别注意的一些条件（有些被官方文档收录，有些则文档中没有，但实验证明为必要）： 在Project Settings-Quality中开启Texture Streaming选项。然而实验发现Editor中开启该选项而真机上却会失效的现象，导致所有纹理的Streaming Mipmap设置全部失效。所以为了确保生效，首先应该在代码中调用QualitySettings.streamingMipmapsActive API全局地开启这个选项，才能确保Streaming Mipmap可用。 调整1中设置的参数。比较重要的参数是Memory Budget参数和Max Level Reduction参数。Memory Budget表示纹理资源的预算，默认值是512MB，但根据UWA的大量项目数据来看，中低端机上一般为200MB左右。它的数值代表的是所有纹理资源的预算——即，它既包括了非流式的纹理、又包括了我们想要采用流式的纹理——但这个“预算”并不代表纹理资源可占用的上限，只是Unity判断对于一个开启Streaming Mipmap的纹理到底采用它的哪些Mipmap通道的参考值，非流式纹理能够轻易突破这个预算。 Max Level Reduction则是代表着Unity通过流式存储最高能取到哪一级的Mipmap通道，这个参数的优先级比Memory Budget要高，也就会造成实际内存超过预算的情况。（比如该参数为2时，则最多剔除Mipmap0和1通道，即便丢弃以后还远超出预算值也不会进一步剔除。） 也就是说，如果Memory Budget值设置的远高于项目中纹理实际占用的内存，则Texture Streaming可能完全不起效，所有开启Streaming Mipmap的纹理仍将保留它们的全部Mipmap通道。 设置开启Streaming Mipmap的纹理。1、2中提到的设置只对开启了Streaming Mipmap的纹理起效，而这只是官方文档的说法，从实际操作来看，Texture Streaming只对同时满足以下三个条件的纹理生效： 1）开启了Streaming Mipmap且开启了Generate Mipmap的纹理（这一点官方文档中没有提及，事实上开启Generate Mipmap才会生成Mipmap通道供Streaming Mipmap剔除）； 2）被即时加载的纹理（如一开始就已经在场景中被依赖的纹理，即便开启了Streaming Mipmap其内存也不会发生变化，通过AssetBundle加载和Res.Load()加载的纹理则可以），也即官方文档中这句话的实际含义： 如果是进行Android开发，还需要打开Build Setting，并将Compression Method设置为LZ4或LZ4HC。Unity需要使用其中一种压缩方法进行异步纹理加载，这是纹理串流系统所必需的操作。 3）Gfx部分内存（这里指的是纹理资源开启Read/Write选项时，复制到CPU端的那一部分内存是不受Streaming Mipmap影响的）； Streaming Mipmap剔除Mipmap通道的规律。它的机制其实和Texture Quality是类似的。我们知道，开启Mipmap的纹理之所以会变成原来的4/3倍，实际上是它各个通道所占用的内存之和。举例而言，一个具有11个Mipmap通道的原大小为1MB的纹理（10241024分辨率、ASTC44格式），其内存占用为1+1/4+1/16+…的11项等比数列之和，即约4/3。等比数列的各项就对应了Mipmap0、Mipmap1、Mipmap2…等各个Mipmap通道。那么，当Max Level Reduction参数设置为2时，其实际意义就是保留Mipmap2和后续所有更小的通道，而剔除Mipmap0和Mipmap1通道，此时的内存大小为4/3MB-1MB-1/4MB=85.33KB。这和我在Profiler或GOT Online中看到的数据基本一致。 关于采用Streaming Mipmap方案的建议。根据上述实验和分析不难看出，Streaming Mipmap是确实具有一些优点的，对于对内存比较敏感，尤其是纹理内存占用很大的项目，采用Streaming Mipmap方案是非常合理和推荐的选项。与此同时，它的实际使用要求对项目中纹理资源的内存占用有相当的了解和规划——相关设置在Quality中，理所当然地应该考虑到不同设备Lod分级时不同的设置。在中低端机中，设置尽量低的Memory Budget和尽量高的Max Level Reduction；在高端机上则恰恰相反，在内存可接受范围内尽量开启最好的画面表现。除此之外，对于哪些纹理要开启Streaming Mipmap，一般是场景中3D物体的纹理，而UI模块采用的纹理则尽量关闭。因为Mipmap的意义主要在于适应纹理在距离镜头远近时的表现需要、避免失真等，而UI完全不需要这些，开启只会白白浪费内存和计算时间。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62943f7fb87a45735168fe5f Rendering Q：之前我们的卡通渲染是在Shader里写多个Pass来绘制的描边，最近尝试用SRP Batcher优化时发现，SRP Batcher不支持多个Pass的Shader。于是我尝试用URP Renderer Feature来渲染所有角色的描边。 实现起来很简单，但有一个问题解决不了，就是Cutout的问题。 裙子的下边缘是用贴图的Alpha控制的，并不是真正的顶点。以前的Pass写在角色渲染的Shader里，可以用贴图来控制，但用Renderer Feature来处理后，所有角色模型的描边是用的同一个材质，不能再用模型各自的贴图的通道来处理了。显示效果就是Cutout的描边部分无法处理： 边缘比较复杂，但是控制的顶点就只有几个，感觉不太好实现。 现在模型顶点的颜色信息我已经用过了，RGB是描边颜色，A是描边粗细。我能想到的办法是用A的一些特殊值来特殊处理一下某些顶点（相当于Clip掉一些顶点，但肯定没有相关API），但又感觉似乎不太可行。不知道大家有没有遇到过，或者有没有什么好办法呢？ 自己尝试了用顶点信息标记点，但有瑕疵，点关联的边会受影响，描边没了： 让美术加了一些点，基本也能解决（其实外边缘还是不会显示全，但已经看不太出来了）： 但这方法还是不好。最好在Renderer Feature里可以获取到正在渲染的模型的材质信息。 是否可以把一个多Pass的Shader里的某个Pass弄到单独的Renderer Feature里画，而不是重新画一遍？比如画第一遍的时候禁用这个Pass，Renderer Feature画的时候再启用它？ A1：自定义一个LightMode，Render Feature里设置这个LightMode，这样材质球上的贴图数据什么就能有。 补充个截图大概是这样的： 对于自定义的LightMode，Unity默认忽略，只有Render Feature里手动指定了要画这个LightMode，Unity才会去绘制。 感谢jim@UWA问答社区提供了回答 A2：如下，这样SRP Batcher终于可以合批多Pass的Shader了： 不用禁用正常的渲染。 两个Pass，一个正常的Pass，一个自定义LightMode的Pass，lightMode的Pass渲染描边用ScriptableRendererFeature手动指定渲染自定义的LightMode。Unity不会去渲染你自定义的LightMode。 感谢题主仇磊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62036e2834a9f84e39f8bd51 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）Unity性能优化分析思路 ​2）Unity2020后Paticle子节点旋转并把ScalingMode设置为Hierarchy后，对根节点进行缩放时表现不正常 3）FBX默认会冗余lit.mat、lit.shader如何解决 4）部分手机（小米12）运行时不时闪退问题 这是第294篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Unity Q：Unity有多少优化点？比如合批：静态合批、SRP合批、GPU实例化、UGUI Reruild、光照烘焙、反射探针、光照探针、Shader.Parse、Shader.CreateGPUProgram、场景加载优化和GC优化，还有哪些优化点？ A1：大方向上可以从CPU、内存、GPU这三个方向切入。 细分一下可以从CPU、内存、渲染、资源优化、耗电优化、网络优化、卡顿优化、优化工具的选择掌握这几个点入手。 一、CPU优化 缓存计算结果 预处理 限帧法 主次法 多线程 引擎模块（动画、物理、粒子、导航） 逻辑优化 二、内存优化 缓存法 内存池 资源管理器 控制GC 逻辑优化 Shader变体数量优化 三、渲染优化 SetPassCall渲染状态切换频次控制 DrawCall数量控制 带宽负载 显存占用 GPU计算量 四、卡顿优化 降帧法 摊帧法 限制数量法 逻辑优化 IO优化 使用进度条 五、资源优化 纹理优化 UI优化 字体优化 模型优化 场景优化 粒子优化 材质优化 指定标准美术规范 Shader变体数量优化 六、耗电优化 上面说到的优化点，或多或少都会影响到手机的耗电，也是优化耗电的措施，除此之外还有： 动态调整限帧 动态调整画质 七、网络优化 减少无用字段 降低字段精度 避免重复发送 网络异步化 压缩无效字节 压缩协议包 以上说的这些要点，大部分摘抄归纳自《移动游戏性能优化通用技法》。强烈建议多花些时间认真阅读一下这篇文章，然后以这篇文章作为指南，再去仔细研究里面提到的优化细节该如何展开。 感谢马三小伙儿@UWA问答社区提供了回答 A2：优化点肯定是无穷无尽的，这里搬运UWA的客户端性能优化思路，针对常见的引擎模块的相关问题都做了分析，讲的是比较全比较透的，常见的优化难题都罗列了。 《Unity性能优化 — 物理模块》 《Unity性能优化 — 动画模块》 《Unity性能优化系列 — 资源内存泄漏》 《Unity性能优化系列—Lua代码优化》 《粒子系统优化——如何优化你的技能特效》 《Unity性能优化系列—加载与资源管理》 《Unity性能优化系列—渲染模块》 《Unity性能优化 — UI模块》 《支持资源加载分析、场景分割》 《UWA报告使用小技巧，你get了吗？》 《UWA本地资源检测更新，助你严守项目性能的每个角落！》 感谢芭妮妮@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61a0a56ed8413e18eb297ac3 ParticleSystem Q：Unity 2020后Paticle子节点旋转并把ScalingMode设置为Hierarchy后，对根节点进行缩放时表现不正常。 在2019及其之前的版本没有这个问题。升级到2020后出现。 子节点的旋转会改变对应的缩放轴，如果x轴旋转了-90度，那么根节点缩放y轴就等于缩放了z轴。 具体见下图： A：已找到对应官方答复： 《particle-system-behavior-changes-when-it-is-rotated-or-scaled-in-negative-axis》 2018.3b版本后不建议对Billboard节点使用旋转和非统一缩放。 感谢jiacat@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6246ad4fa43e6117d04e7f0a Material Q：打AssetBundle包发现会包含lit.mat、lit.shader，原因是FBX默认自带的，有尝试下面方法： **1. 代码设置 renderer.sharedMaterials = new Material[] ，但是下次再打开Unity或Reimport，又变回lit。** 2. 监听文件变化，发现是FBX的同上操作，但是这个在打AssetBundle包时又报一些莫名其妙的错误。 请问大家都是怎么处理的？ A1：两种方式解决这个问题： 一种是美术在出资源时直接不导出材质资源。 一种是自己在Unity导入资源进程里写个批处理，在导出时默认去除掉这材质球。 不管用哪种，都需要导入后，自己在资源上做好策略管理，手动的整理好材质与Shader的目录，跟打包的策略对应。这样可以更好地做好项目规范，还能为后期资源冗余的问题做优化。前期能做好这个规范，后期关于资源冗余的问题也不会太头疼。 感谢廖武兴@UWA问答社区提供了回答 A2：在2020.3.17里面有效，不论是ReImport FBX还是重启Unity，都是有用的： public class ProcessMaterialImport : AssetPostprocessor { void OnPostprocessModel(GameObject model) { var renderers = model.GetComponentsInChildren<Renderer>(); if (renderers == null) { return; } foreach (var renderer in renderers) { if (renderer == null) { continue; } renderer.sharedMaterials = new Material[0]; } ModelImporter importer = (ModelImporter)base.assetImporter; if (importer != null) { if (importer.materialImportMode != ModelImporterMaterialImportMode.None) { importer.materialImportMode = ModelImporterMaterialImportMode.None; importer.SaveAndReimport(); } } } } 材质球会变成下面这样： 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62442163a43e6117d04ad8fa Memory Q：目前只出现在小米12手机，Android系统 12，小米系统13.0.16.0。运行后10-20分钟左右就时不时闪退。 Unity版本 2019.4.3f1 L2cpp E/Unity: Using memoryadresses from more than 16GB of memory (Filename: /Users/builduser/buildslave/unity/build/Runtime/Allocator/UnityDefaultAllocator.cpp Line: 120) bool UnityDefaultAllocator::AllocationPage<(RequestType)0>(void const*) const+268) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) UnityDefaultAllocator::RegisterAllocation(void const*)+148) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) UnityDefaultAllocator::Allocate(unsigned long, int)+64) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) MemoryManager::Allocate(unsigned long, unsigned long, MemLabelId const&, AllocateOptions, char const*, int)+308) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) QueueAllocator::QueueAllocator(unsigned int, MemLabelId)+60) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) AsyncUploadManager::ScheduleAsyncRead(AtomicNode*)+280) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) AsyncUploadManager::ScheduleAsyncCommand(AtomicNode*)+48) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) AsyncUploadManager::ScheduleAsyncCommandsInternal()+56) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) AsyncUploadManager::ScheduleAsyncCommands()+56) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) AsyncUploadManager::AsyncResourceUpload(GfxDevice&, int, AsyncUploadManagerSettings const&)+416) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) GfxDeviceWorker::RunCommand(ThreadedStreamBuffer&)+26744) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) GfxDeviceWorker::RunExt(ThreadedStreamBuffer&)+44) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) GfxDeviceWorker::Run()+136) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) GfxDeviceWorker::RunGfxDeviceWorker(void*)+4) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) Thread::RunThreadWrapper(void*)+512) (BuildId: d0b079d6aac2d769613621db6d01af8abf84a793) A1：Unity发行日志说2019.4.15修复了这个问题，我们升级完之后基本不闪退，但是在vivo的安卓12手机上还是有问题，然后又升级到2019.4.34，目前没看到闪退。 感谢大秋衣@UWA问答社区提供了回答 A2：可参考： https://issuetracker.unity3d.com/issues/android-il2cpp-empty-project-crashes-on-launch-with-using-memoryadresses-from-more-than-16gb-of-memory-messages 感谢Arboo@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62173e64e7172c2775f926d3 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
一．优化最终效果及消耗 8人同屏，最高画质，耗时1.1ms（高通 骁龙710）。因为项目未上线原因，只能用测试图进行相关说明。 二、原反射脚本源码 1. 源码 using UnityEngine; using System.Collections; [ExecuteInEditMode] public class MirrorReflection : MonoBehaviour { public Material m_matCopyDepth; public bool m_DisablePixelLights = true; public int m_TextureSize = 256; public float m_ClipPlaneOffset = 0.07f; public LayerMask m_ReflectLayers = -1; private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table public RenderTexture m_ReflectionTexture = null; public RenderTexture m_ReflectionDepthTexture = null; private int m_OldReflectionTextureSize = 0; private static bool s_InsideRendering = false; public void OnWillRenderObject() { if (!enabled || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial || !GetComponent<Renderer>().enabled) return; Camera cam = Camera.current; if (!cam) return; // Safeguard from recursive reflections. if (s_InsideRendering) return; s_InsideRendering = true; Camera reflectionCamera; CreateMirrorObjects(cam, out reflectionCamera); // find out the reflection plane: position and normal in world space Vector3 pos = transform.position; Vector3 normal = transform.up; // Optionally disable pixel lights for reflection int oldPixelLightCount = QualitySettings.pixelLightCount; if (m_DisablePixelLights) QualitySettings.pixelLightCount = 0; UpdateCameraModes(cam, reflectionCamera); // Render reflection // Reflect camera around reflection plane float d = -Vector3.Dot(normal, pos) - m_ClipPlaneOffset; Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d); Matrix4x4 reflection = Matrix4x4.zero; CalculateReflectionMatrix(ref reflection, reflectionPlane); Vector3 oldpos = cam.transform.position; Vector3 newpos = reflection.MultiplyPoint(oldpos); reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection; // Setup oblique projection matrix so that near plane is our reflection // plane. This way we clip everything below/above it for free. Vector4 clipPlane = CameraSpacePlane(reflectionCamera, pos, normal, 1.0f); Matrix4x4 projection = cam.projectionMatrix; CalculateObliqueMatrix(ref projection, clipPlane); reflectionCamera.projectionMatrix = projection; reflectionCamera.cullingMask = ~(1 << 4) & m_ReflectLayers.value; // never render water layer reflectionCamera.targetTexture = m_ReflectionTexture; //GL.SetRevertBackfacing(true); GL.invertCulling = true; reflectionCamera.transform.position = newpos; Vector3 euler = cam.transform.eulerAngles; reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z); reflectionCamera.depthTextureMode = DepthTextureMode.Depth; reflectionCamera.Render(); // copy depth Graphics.SetRenderTarget(m_ReflectionDepthTexture); m_matCopyDepth.SetPass(0); DrawFullscreenQuad(); Graphics.SetRenderTarget(null); // Graphics.Blit(m_ReflectionTexture, m_ReflectionDepthTexture, m_matCopyDepth); reflectionCamera.transform.position = oldpos; //GL.SetRevertBackfacing(false); GL.invertCulling = false; Material[] materials = GetComponent<Renderer>().sharedMaterials; foreach (Material mat in materials) { mat.SetTexture("_ReflectionTex", m_ReflectionTexture); mat.SetTexture("_ReflectionDepthTex", m_ReflectionDepthTexture); } // // Set matrix on the shader that transforms UVs from object space into screen // // space. We want to just project reflection texture on screen. // Matrix4x4 scaleOffset = Matrix4x4.TRS( // new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f)); // Vector3 scale = transform.lossyScale; // Matrix4x4 mtx = transform.localToWorldMatrix * Matrix4x4.Scale(new Vector3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z)); // mtx = scaleOffset * cam.projectionMatrix * cam.worldToCameraMatrix * mtx; // foreach (Material mat in materials) // { // mat.SetMatrix("_ProjMatrix", mtx); // } // Restore pixel light count if (m_DisablePixelLights) QualitySettings.pixelLightCount = oldPixelLightCount; s_InsideRendering = false; } // Cleanup all the objects we possibly have created void OnDisable() { if (m_ReflectionTexture) { DestroyImmediate(m_ReflectionTexture); m_ReflectionTexture = null; } if (m_ReflectionDepthTexture) { DestroyImmediate(m_ReflectionDepthTexture); m_ReflectionDepthTexture = null; } foreach (DictionaryEntry kvp in m_ReflectionCameras) DestroyImmediate(((Camera)kvp.Value).gameObject); m_ReflectionCameras.Clear(); } private void UpdateCameraModes(Camera src, Camera dest) { if (dest == null) return; // set camera to clear the same way as current camera dest.clearFlags = src.clearFlags; dest.backgroundColor = src.backgroundColor; if (src.clearFlags == CameraClearFlags.Skybox) { Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox; Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox; if (!sky || !sky.material) { mysky.enabled = false; } else { mysky.enabled = true; mysky.material = sky.material; } } // update other values to match current camera. // even if we are supplying custom camera&projection matrices, // some of values are used elsewhere (e.g. skybox uses far plane) dest.farClipPlane = src.farClipPlane; dest.nearClipPlane = src.nearClipPlane; dest.orthographic = src.orthographic; dest.fieldOfView = src.fieldOfView; dest.aspect = src.aspect; dest.orthographicSize = src.orthographicSize; } // On-demand create any objects we need private void CreateMirrorObjects(Camera currentCamera, out Camera reflectionCamera) { reflectionCamera = null; // Reflection render texture if (!m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize) { if (m_ReflectionTexture) DestroyImmediate(m_ReflectionTexture); m_ReflectionTexture = new RenderTexture(m_TextureSize, m_TextureSize, 16); m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID(); m_ReflectionTexture.isPowerOfTwo = true; m_ReflectionTexture.hideFlags = HideFlags.DontSave; m_ReflectionTexture.filterMode = FilterMode.Bilinear; if (m_ReflectionDepthTexture) DestroyImmediate(m_ReflectionDepthTexture); m_ReflectionDepthTexture = new RenderTexture(m_TextureSize, m_TextureSize, 0, RenderTextureFormat.RHalf); // m_ReflectionDepthTexture = new RenderTexture(m_TextureSize, m_TextureSize, 0, RenderTextureFormat.R8); m_ReflectionDepthTexture.name = "__MirrorReflectionDepth" + GetInstanceID(); m_ReflectionDepthTexture.isPowerOfTwo = true; m_ReflectionDepthTexture.hideFlags = HideFlags.DontSave; m_ReflectionDepthTexture.filterMode = FilterMode.Bilinear; m_OldReflectionTextureSize = m_TextureSize; } // Camera for reflection reflectionCamera = m_ReflectionCameras[currentCamera] as Camera; if (!reflectionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO { GameObject go = new GameObject("Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox)); reflectionCamera = go.GetComponent<Camera>(); reflectionCamera.enabled = false; reflectionCamera.transform.position = transform.position; reflectionCamera.transform.rotation = transform.rotation; reflectionCamera.gameObject.AddComponent<FlareLayer>(); go.hideFlags = HideFlags.HideAndDontSave; m_ReflectionCameras[currentCamera] = reflectionCamera; } } // Extended sign: returns -1, 0 or 1 based on sign of a private static float sgn(float a) { if (a > 0.0f) return 1.0f; if (a < 0.0f) return -1.0f; return 0.0f; } // Given position/normal of the plane, calculates plane in camera space. private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign) { Vector3 offsetPos = pos + normal * m_ClipPlaneOffset; Matrix4x4 m = cam.worldToCameraMatrix; Vector3 cpos = m.MultiplyPoint(offsetPos); Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign; return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal)); } // Adjusts the given projection matrix so that near plane is the given clipPlane // clipPlane is given in camera space. See article in Game Programming Gems 5 and // http://aras-p.info/texts/obliqueortho.html private static void CalculateObliqueMatrix(ref Matrix4x4 projection, Vector4 clipPlane) { Vector4 q = projection.inverse * new Vector4( sgn(clipPlane.x), sgn(clipPlane.y), 1.0f, 1.0f ); Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q))); // third row = clip plane - fourth row projection[2] = c.x - projection[3]; projection[6] = c.y - projection[7]; projection[10] = c.z - projection[11]; projection[14] = c.w - projection[15]; } // Calculates reflection matrix around the given plane private static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane) { reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]); reflectionMat.m01 = (-2F * plane[0] * plane[1]); reflectionMat.m02 = (-2F * plane[0] * plane[2]); reflectionMat.m03 = (-2F * plane[3] * plane[0]); reflectionMat.m10 = (-2F * plane[1] * plane[0]); reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]); reflectionMat.m12 = (-2F * plane[1] * plane[2]); reflectionMat.m13 = (-2F * plane[3] * plane[1]); reflectionMat.m20 = (-2F * plane[2] * plane[0]); reflectionMat.m21 = (-2F * plane[2] * plane[1]); reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]); reflectionMat.m23 = (-2F * plane[3] * plane[2]); reflectionMat.m30 = 0F; reflectionMat.m31 = 0F; reflectionMat.m32 = 0F; reflectionMat.m33 = 1F; } static public void DrawFullscreenQuad(float z = 1.0f) { GL.Begin(GL.QUADS); GL.Vertex3(-1.0f, -1.0f, z); GL.Vertex3(1.0f, -1.0f, z); GL.Vertex3(1.0f, 1.0f, z); GL.Vertex3(-1.0f, 1.0f, z); GL.Vertex3(-1.0f, 1.0f, z); GL.Vertex3(1.0f, 1.0f, z); GL.Vertex3(1.0f, -1.0f, z); GL.Vertex3(-1.0f, -1.0f, z); GL.End(); } } 相关链接： https://github.com/unity3d-jp/unitychan-crs/blob/master/Assets/UnityChanStage/Visualizer/MirrorReflection.cs 2. 该脚本主要功能 在场景中获取当前生效的相机，根据反射平面进行镜像，并对设定的渲染层进行逐帧拍照，并将其传给反射平面进行显示。配合自带的半透明反射材质效果，叠加到底层材质上使用。 3. 主要问题 优化之前，该反射效果大概耗时10ms以上。主要问题如下： 反射效果均为实时反射，使用Layer控制。要看到反射效果，只能将该层物体纳入反射层。 反射表面为独立于真实反射表面的半透明材质，既耗性能效果也无法保证。 项目角色的面数极高（单角色万面左右），极限同屏9个角色外加宠物。除了本身的角色渲染外，同时还存在描边、压片阴影等效果。如果反射直接采用相机拍照的方式生成，额外的性能消耗极大。 项目角色本身渲染支持5盏像素光，带法线贴图，卡通光照图等各种复杂渲染效果，如果反射直接采用相机拍照的方式生成，额外的性能消耗极大。 该脚本本身也存在一些问题： 1）会根据场景中正在生效的相机，逐个生成反射相机。 2）逐帧获取反射材质，进行参数传递。 3）反射层的设置需要美术每次使用时都设置一次，容易设置过多或漏设置。 三、优化方向 针对以上问题，实时反射主要有两大优化方向，美术效果优化及性能优化。 一、美术效果优化 1.将反射效果与反射表面物理属性关联。 引入反射表面法线贴图的支持，对反射效果增加真实的法线扭曲效果。 方法：弃置默认的半透明反射表面。将反射图直接指定给反射表面的渲染材质，增加法线效果，使用法线贴图的xy通道对反射图的UV进行偏移（这里还增加了法线扭曲强度及遮罩等的调节）。 2.引入反射表面粗糙度设置。 可以调节反射效果的清晰度（模糊策略，后面性能优化详述）。 方法：不考虑性能，这里可以向脚本中传参，使用高斯模糊来进行粗糙表面的反射模糊效果。 3.引入反射深度，实现反射跟随距离从有到无的柔和过渡。 方法：在渲染反射图时，传入衰减系数，根据顶点到地面高度的距离进行衰减，衰减强度写入反射图的A通道。 二、性能优化 概述： 性能优化方面主要集中在如下几个方面： 减少数据量，降低CPU负担。 降低DrawCall，增加C/GPU传输率。 降低渲染复杂度，加速GPU渲染。 优化脚本，减少GC及重复操作。 基于以上考虑，性能优化方向归为两类：美术端优化和程序端优化。 美术端优化 1.动静分离 将反射效果拆分为静态物体的与烘焙反射和动态物体的实时反射两部分组成。这样可以大大降低实时运算量。 2.预烘焙部分 根据场景需要的反射清晰度，预先烘焙尺寸尽可能小的反射图。对于非常粗糙且并不清晰的反射场景，可以共用一张场景反射图。 或者昼夜、雨晴等同场景不同状态下的反射图共用。通过调节材质参数（例如反射颜色、粗糙度及反射强度）来实现不同状态效果。 通过BoxProjection方法，修正与烘焙CubeMap的场景对齐问题。 3.实时部分 同静态与烘焙相似，可以根据场景的反射清晰度，实时生成尽可能小的反射图来使用。 （1）程序开放尺寸调节： （2）代码写入支持尺寸配置： 降低实时反射的DrawCall，例如：尽可能地合并角色部件，忽略较小部件不进行反射等。 降低实时反射的数据量和计算量，例如：使用低面数的Lod来渲染反射，降低反射效果要求减少灯光数量，效果数量等。 反射表面优化策略 同一个场景，只能存在一个反射表面。 将反射表面的材质作为高配材质由美术特别对待。 将尽可能多的参数暴露给美术，尽可能地适配高性能参数。 程序端优化 同样从概述中的几个方面进行考虑来进行优化。 1.去除不必要的渲染批次及效果 反射效果中因为清晰度有限，描边效果可以剔除，阴影效果也可以不进行渲染。 2.降低反射渲染的计算复杂度 反射效果中，只需要表现出基本的明暗关系即可，故只保留1盏平行光效果。光照计算也从像素光照更改为顶点光照。 一个角色渲染需要基本色纹理，法线纹理，粗糙金属性纹理，身体材质区分纹理，光照纹理和buff效果纹理。在反射中，精简效果到只需要基本色纹理和buff效果纹理即可。 要实现以上优化方案，就需要在进行反射渲染时，将动态物体的材质进行替换渲染。可用策略如下： （1）更换材质 需要每帧用反射材质进行替换渲染。如果是后处理描边、风格化单色等效果，这类方法比较适合。但角色材质多样，参数各不相同，用这种方法需要自己维护动态物件的反射列表，比较繁琐。 （2）Lod Int控制 就是使用SubShader的Lod参数进行控制。这样，把各个材质的反射Shader集中到一个Lod下进行计算即可。最简单的一种方式，但后来经过测试，实时切换Lod的消耗过大，不太实际。 （3）宏开关 比较简单，集成性、通用性好，且切换消耗小。最初我们使用的优化方案。但因为存在角色的多材质及多Pass渲染，后期对优化效率仍然不足够（无法优化掉描边和阴影）。 核心代码： （4）使用CommandBuff 可控性和自由度最高。但问题同（1），需要自己维护角色列表，对于频繁发生角色更换的场合，维护成本会较高，但仍可实现。可以由逻辑层在发生角色更换时，更新角色列表到反射程序中进行处理即可。 部分核心代码： （5）使用CameraReplaceShader 我们项目最终优化采用的方法。优点是可以实现替换渲染，且不需要自己维护角色列表。缺点就是重写反射渲染的Shader。 核心脚本代码： 这里简单解释一下SetReplacementShader命令。 他是将该相机中进行渲染的所有Shader，根据后面设定的replacementTag进行替换。 例如上面代码中，我们使用了“MirrorReflect”的Tag，所以，在Shader中，凡是含有“MirrorReflect”Tag的SubShader都会被替换为reflectShader中，相同“MirrorReflect”Tag的SubShader。 如上举例，即可在相机进行反射渲染时，将含有相同“MirrorReflect”Tag的SubShader 进行对应替换渲染了。 程序策略性优化 1.合并静态与实时反射 只需要根据反射图的A通道中，存储的反射衰减值进行动静反射混合即可。 2.开放美术调节参数 如前所述的一些性能相关设置，可以开放给美术进行择优。 3.反射清晰度 引入粗糙度的概念，使用模糊来遮盖反射图分辨率不足的问题。可以进一步降低反射分辨率。 这里的模糊策略采用了MipMap的方式来避免高斯模糊带来的消耗。 程序逻辑层性能优化 1.减反射相机数量 将反射相机个数从逐相机生成的多反射相机优化为单反射相机，逐帧对位的方式。 2.减GC 将申请RT，生成相机，获取反射材质，传递材质参数等可一次性进行的操作，移出OnWillRenderObject()。 3.优化刷新率 增加刷新率设置，将逐帧渲染改为可配置刷新率渲染。目前默认为1/2刷新率。 4.反射层程序端写死 避免美术出错及加速配置。 5.开关策略 当反射平面不可见时，关闭反射功能，将所有资源进行释放（逻辑层控制）。 6.平台匹配 适配不同性能的平台。目前我们仅高配平台开启实时反射效果。中配置平台仅开启静态反射效果。 最后：优化总结 这是侑虎科技第1072篇文章，感谢作者罗汉铭供稿。罗汉铭：朝夕光年无双工作室 技术美术专家，目前就职于字节跳动101技术中心，08年加入上海昱泉国际，从事TA相关工作，参与了《流星蝴蝶剑》、《射雕三部曲》项目的制作。之后加入完美世界引擎组，从事ARK1.0-ARK2.0的相关开发工作，并支持《最终兵器》、《射雕英雄传OL》项目的开发；后转向Unity，参与了《射雕英雄传3D》手游的开发工作。 作者主页：https://www.zhihu.com/people/lucifer-3-91 作者在 UWA DAY 2021 大会中的《航母战斗群中的巡洋舰 - 为项目保驾护航的技术美术们》议题演讲已经上线UWA学堂，欢迎前往观看。 再次感谢罗汉铭的分享，如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。（QQ群：793972859）
1）Unity优化点概述 ​2）Unity Assets下的Shader加载到内存需要满足什么样的条件 3）URP内置Shader生成的变体太多问题 4）常见的Unity地形编辑插件 这是第277篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Unity Q：Unity有多少优化点？ A：大方向上可以从CPU、内存和GPU这三个方向切入。细分一下可以从CPU、内存、渲染、资源优化、耗电优化、网络优化、卡顿优化和优化工具的选择掌握这几个点入手： 一、CPU优化 缓存计算结果 预处理 限帧法 主次法 多线程 引擎模块（动画、物理、粒子、导航） 逻辑优化 二、内存优化 缓存法 内存池 资源管理器 控制GC 逻辑优化 Shader变体数量优化 三、渲染优化 SetPassCall渲染状态切换频次控制 DrawCall数量控制 带宽负载 显存占用 GPU计算量 四、卡顿优化 降帧法 摊帧法 限制数量法 逻辑优化 IO优化 使用进度条 五、资源优化 纹理优化 UI优化 字体优化 模型优化 场景优化 粒子优化 材质优化 指定标准美术规范 Shader变体数量优化 六、耗电优化 上面说到的优化点，或多或少都会影响到手机的耗电，也是优化耗电的措施，除此之外还有： 动态调整限帧 动态调整画质 七、网络优化 减少无用字段 降低字段精度 避免重复发送 网络异步化 压缩无效字节 压缩协议包 以上说的这些要点，大部分摘抄归纳自《移动游戏性能优化通用技法》。 强烈建议多花些时间认真阅读一下这篇文章，然后以这篇文章作为指南，再去仔细研究里面提到的优化细节该如何展开。 感谢马三小伙儿@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61a0a56ed8413e18eb297ac3 Shader Q：只要在Assets中的Shader，是不是只要开始运行就会直接加进内存？ A：在Unity工程里面，Assets目录下的资源要进内存，要经历这么几个步骤： 首先是要这个资源被打包进包体里面，有这么几种情况进入包体： 这个资源在Resources目录下，无论是否被引用到； 这个资源直接被场景引用，比如场景中的一个材质球使用了Shader A，这个场景最终进包体了。场景进入包体，有两种情况：一种是这个场景是被加入BuildSettings里面了，另外一种情况是被打包AssetBundle了。被场景引用，还有另外一种情况，就是被场景中的脚本引用了； 资源被主动或者被动进AssetBundle了； 这个资源被Unity某些设置项引用了，最终打包的时候这个资源也会进包体，比如游戏启动时候的Flash纹理，或者大家比较熟悉的Always Included Shaders设置项。 上面说的4种情况应该是一个资源进包体的所有情况了。 资源进包体后，和进入内存是两回事，要进内存还需要被加载进内存。加载进内存的方式会因为前面4种进包体的方式相对应，各不相同： 需要调用Resources.Load进行加载； 需要调用SceneManager.Load进行场景加载，如果是场景打包进入AssetBundle中，那么需要先加载这个场景的AssetBundle，再SceneManager.Load加载场景； 直接AB.LoadFrom加载AssetBundle，再AB.LoadAsset加载资源； 通常在设置项设置的资源，Unity引擎会自动进行加载内存的管理，不同的设置项，时机不同。 感谢小ben@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/619cce5ed8413e18eb241eb5 Shader Q：打包时URP的Shader变体竟然有2w个，我有什么办法优化下吗？有什么办法可以查看用了哪些内置Shader的变体吗？ A1：变体一般分为multi_compile和shader_feature。multi_compile会把所有可能变体的可能性都生成一遍，shader_feature在Build的时候只会打包用到的Keyword。 建议替换成shader_feature，但是有一些代码启动的Keyword在Build的时候Unity不知道，需要手动加入Shader Variant Collection中。 UWA有一个本地资源检测工具可以看，然后内置的Shader可以去Unity官网上下载到本地，然后再进行修改。 感谢萧小俊@UWA问答社区提供了回答 A2：当然减Feature是肯定的，当减不掉的时候可以试试这个：继承IPreprocessShaders然后实现public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList data) 进行 data.RemoveAt(i); 代码地址： https://docs.unity3d.com/ScriptReference/Build.IPreprocessShaders.OnProcessShader.html 感谢夏霖锐@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/619dc0fdd8413e18eb255f6c Terrain Q：Unity地形的解决方案有什么？是用插件吗？用什么插件？ A：首先看地形要做什么，如果要做大世界动态加载的，建议还是找美术使用3D软件制作，然后程序来处理。如果只是单纯需要一个地形来承载游戏玩法，可以考虑用一些开箱即用的插件。 如果要快速生成一个还不错的写实风格的地形可以考虑使用： Gaia 2 - Terrain & Scene Generator 如果要LowPoly，可以看看这个插件： Low Poly Ultimate Pack 2D/2.5D游戏可以看看这个插件： Ferr2D Terrain Tool 普通2D地形可以考虑使用Unity自带的Tilemap来实现，然后最新版的Unity好像重构了本身的Terrain system。 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/619cdd33d8413e18eb2434c2 20211129 更多精彩问题等你回答～ Unity增量打包AssetBundle没变化的资源也会被重新打包 在模型有UV2的情况下开启Generate Lightmap UVs 如何实现AAB包的增量更新 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）DLL混淆问题 ​2）Unity主程如何做项目管理 3）将ASTC格式转换为png格式 4）iOS 14在iPhone正常，但在iPad上会卡住 这是第276篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Scripting Q：如果不用收费软件和加固，我们自己使用免费软件，如何对DLL进行混淆。 注：之前是用Microsoft.NET/Framework64/v3.5/csc进行打DLL，但是今天我回顾一下发现，DLL并没有混淆。 A1：若在Unity上使用，可以参考开源的方案：Unity Obfuscator Demo 简介 这是一款基于Mono.Cecil库，对C#编译后程序集进行IL代码注入实现的轻量Unity混淆插件，混淆发生在编译后，开发过程无需关心。此插件已在带Lua热更方案的商业MMO手游项目上验证过可行性。 主要功能 对命名空间、类、属性和字段的命名混淆 插入垃圾代码 在原代码中插入对垃圾代码的调用 特色功能 命名混淆支持随机字符串或从词库抽取名字 支持随机种子，可实现重复Build后混淆结果一致 支持黑白名单，排除部分在混淆后不能正常工作的代码，确保项目正常运行 混淆后输出混淆前后的名字对应关系文件 支持多DLL 感谢马三小伙儿@UWA问答社区提供了回答 A2：可以看一下AssetStore上的插件，我之前有个项目也用过： https://assetstore.unity.com/packages/tools/utilities/obfuscator-free-89420 免费版基本上可用： https://assetstore.unity.com/packages/tools/utilities/obfuscator-48919 收费版支持IL2CPP，支持程序集定义文件。 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61922be9d8413e18eb14a43f Unity Q：Unity主程如何做项目管理？比如资源管理、代码管理和工程管理。可以从多个维度讨论下吗？ A1：资源还是比较简单的，首先按照功能或者需求进行文件夹分类，资源的格式可以写一些AssetPostprocessor的代码来强制转换，整个工程的资源管理自动化。 开发的人员比较多，又对代码质量有一定要求，可以考虑用Git，不要用SVN，主程负责Review所有PR的代码，然后定期进行UT和BVT。 工程管理也可以依赖于Git的Tag和Branch来管理，尽量保证每个版本都是ZBB。 主程要做的事情还是蛮多的，简单写了一下，欢迎大家来讨论。 感谢萧小俊@UWA问答社区提供了回答 A2：身为一个合格的主程，我觉得应该具备以下素质： 1.首先是技术方面，就当独当一面，一马当先。 强大的技术支撑是要具备的，这里其实也可以细分，一个是专业性和广度。先提升某一方面的专业度，同时了解学习新技术，在不同领域的知识要有相对的了解。 2.再之就是管理方面，抓主线，控支流。 应该有好的项目框架。框架是为了更好的迭代和规范项目。一切都是为了开发跟成品服务的。所以不管代码规范，资源管理，甚至是使用习惯，都要遵循简单易用，通俗易懂为原则。比如开发了一个工具，功能非常强大，但是没人会使用它。那意义就大打折扣了。 技术上没有捷径，全靠努力跟积累。管理上需要自己思维上变通，多做尝试。但是有一点我觉得要注意，不能什么都面面俱到，主程是要把控大方向的，不能什么都要干预，适当放手，要培养人，不是带人。这样团队才有创新力，团队成员才会有成就感。而不是个人成就。管理是门大学问，不像技术可以有固定的学习思路。需要自己不断探索，才会有自己的风格和魅力。 感谢廖武兴@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61947fe3d8413e18eb17e003 Texture Q：.astc类文件如何转换为.png文件，Windows操作系统。 A：ARM提供了压缩ASTC和解压的工具。 对应命令： 压缩：astcenc-sse2.exe -cl origin.png example.astc 6x6 -medium 解压：astcenc-sse2.exe -dh example.astc example.png 可以参考：https://github.com/ARM-software/astc-encoder 感谢范世青@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61925149d8413e18eb14e0cb iOS Q：在iOS上遇到了一个比较棘手的问题，我们的游戏在iPhone上能正常跑，但是在iPad上会卡在进入主城的加载界面中，具体的表现是这样的： 1. 测试的iPad型号，2台A12处理器，1台M1处理器 2. iOS版本，全部都是iOS 14.8 3. 结果： 其中一台iPad A12处理能够正常运行，其余两台IPad都在加载中卡住。 4. 出错的表现为：在进入加载界面之后，会卡住在界面，但其实CPU逻辑是在跑，只是GPU因为渲染错误导致无法更新的画面信息，主要依据是在xCode Capture的时候，是能够正常看到主界面的已经在渲染，点击到相应的屏幕位置，Caputure也能看到UI能正常响应，只要退出Capture继续运行，还会回到加载界面。 5. 报错的Log： Execution of the command buffer was aborted due to an error during execution. Ignored (for causing prior/excessive GPU errors) (IOAF code 4) 不知道大家有没有遇到过类似的问题，或者说有没有什么建议，谢谢！ A：可以参考梅辰写的《解决iOS GPU报错导致游戏卡死IOAF code 4》 感谢马古斯@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6193beedd8413e18eb16cbdf 20211122 更多精彩问题等你回答～ Unity增量打包AssetBundle没变化的资源也会被重新打包 在模型有UV2的情况下开启Generate Lightmap UVs 如何实现AAB包的增量更新 封面图来源于：Unity Obfuscator Demo Unity代码混淆插件使用demo。 https://lab.uwa4d.com/lab/5e94d57e0f247485d923d7d2 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）Lua与C#传参 ​2）Unity发布iOS版本出现屏幕问题 3）安卓的View成为Unity界面的一部分 4）Unity升级导致项目启动时间过长 5）Unity中Cascaded Shadows在移动端的设置 这是第258篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Lua Q：游戏中是先在Lua中String.format好再传入C#，还是在Lua里把参数传入C#，在C#里用String.format好？ 即一个是Lua加载文字表，String.format后传给C#显示，另一个是C#读表，Lua把参数传给C#，再string.format使用，哪个方式更好些，或者有没有其他更好的方式？ A1：搬运群里大佬的问答：第二种比较好，因为传参越简单越好，参数可以打包传。 感谢Tao@UWA问答社区提供了回答 A2：可以参考一下这篇文章：《用好Lua+Unity，让性能飞起来——Lua与C#交互篇》，总之Lua与C#传参，优先考虑int、float和double等类型，其次bool、string及object等类型，同时尽可能降低交互次数。 感谢羽飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60e41d96e58b4719dd3e2916 iOS Q：Unity发布iOS版本在iOS 11之后出现屏幕显示问题：半屏被压缩，另一半黑屏。 A：直接在编辑器中设置这几个参数，不要在代码中设置这个，7p就不会出现这个问题： Screen.autorotateToLandscapeLeft = true; Screen.autorotateToLandscapeRight = true; Screen.autorotateToPortrait = false; Screen.autorotateToPortraitUpsideDown = false; Screen.orientation = ScreenOrientation.AutoRotation; 感谢许家胜@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5ab9ef650b827e2c0bfdd1b6 Unity Q：加载了一个安卓的Activity，会遮挡住Unity的界面，如何让安卓的View，成为Unity界面的一部分呢？Unity界面的一部分使用安卓原生SDK的界面实现。 A：安卓混合Unity的界面，已经完成了。 https://blog.csdn.net/Yzw_92_4_11/article/details/68952529 感谢题主韩飞@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60e6caa7e58b4719dd3e296b Editor Q：查看了Unity的editor.log发现里面： RefreshInfo: InitialScriptRefreshV2(NoUpdateAssetOptions) RefreshProfiler: Total: 0.000ms InvokeBeforeRefreshCallbacks: 1115393.587ms ...... OnSourceAssetsModified: 340.325ms ...... ImportAndPostprocessOutOfDateAssets: 1032441.185ms (82533.117ms without children) ImportManagerImport: 82501.381ms (0.000ms without children) ...... 有没有大佬知道这个是在处理什么东西？科学上网了也没有查到相关信息，这里看耗时就很长时间了1115393.587ms。 A：参考以下几点： 检查一下项目是不是放在机械硬盘上，建议放NVME的固态上。因为高版本的数据结构更复杂了，不用固态来不及序列化。 检查一下是不是开了PlasticSCM。 感谢萧小俊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60e2afece58b4719dd3e28ed Rendering Q：Unity的Shadow Cascade功能在移动平台中是否生效？ 在场景使用了Unity自带的实时阴影，具体参数如下： 用Frame Debug观察阴影绘制过程： Shadowmap为1024＊1024，场景远处会有黑色的阴影覆盖。 当Cascade设置为2则远处阴影消失： 用Frame Debug观察阴影绘制过程： Shadowmap还是1024＊1024，但一分为二，各占1024＊512。 以上是在PC上验证的，证明Cascade确实起作用。但当在Unity编辑器中的BuildSettings中切换到Android平台时，无论怎么修改Cascade的值，场景远处均有阴影，用Frame Debug观察阴影绘制过程如下： 似乎在安卓平台中这个参数不起作用，查阅Unity官服文档也并没说明Shadow Cascade不支持移动平台。 请问有同样遇到这种问题的吗？ A：Graphics设置那里需要在对应的Tier里面启用Cascaded Shadows。 感谢题主x21@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60e7fa6d038436106ddf2955 20210712 更多精彩问题等你回答～ 1.如何实现AAB包的增量更新 2.关于Unity出AAB包的问题 3.RTS 手游技术难点 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
我们曾在四年前对于Unity的主流模块的性能优化知识点逐一做过讲解，俗称“小白版”。随着这几年引擎本身、硬件设备、制作标准等等的升级，UWA也不断更新优化规则和方法并持续输出给广大开发者。作为"升级版"的性能优化手册，【Unity性能优化系列】将力图以浅显易懂的表达，让更多开发者可以受用。本期我们来继续分享物理模块相关的知识点。 在Unity自带的物理引擎中，物理模块的耗时主要来自FixedUpdate.PhysicsFixedUpdate和逻辑代码中的射线检测、碰撞检测等。FixedUpdate.PhysicsFixedUpdate函数的耗时构成主要有两部分：一个是Physics.Processing，一个是Physics.Simulate。一般来说，我们要关注这两个函数的堆栈，通过堆栈函数的调用次数、耗时占比来进一步定位原因。常见的影响性能的因素和优化对策有这些： 一、项目是否确实需要物理模块 首先，我们需了解Unity上Auto Simulation选项的开关机制。在Unity 2017.4以前无该选项，引擎会根据项目是否存在物理内容自动开启或关闭；在Unity 2017.4及以后该选项开放，默认为开启状态。 很多项目往往没有关注到物理模块开关与否，造成不必要的性能浪费。建议研发团队根据项目情况考虑是否开启物理模块，亦可考虑通过其它方案替代物理模块作用，以节省物理部分的耗时。 二、控制物理调用次数 在UWA的真人真机测评报告的物理模块中，我们可以看到物理更新次数的统计。 物理调用次数需着重关注以下两个参数： 1）Fixed Timestep 其决定了FixedUpdate的更新间隔，该值越大，每帧物理更新调用次数越少。但物理更新频率过低可能会造成部分机制异常，建议团队在可接受范围内尽量调高Fixed Timestep数值。 2）Maximum Allowed Timestep 这里我们需要先知道物理系统本身的特性，即当游戏上一帧卡顿时，Unity会在当前帧非常靠前的阶段连续调用N次FixedUpdate.PhysicsFixedUpdate，Maximum Allowed Timestep的意义就在于限制物理更新的次数。 Maximum Allowed TimeStep决定了单帧物理最大调用次数，该值越小，单帧物理最大调用次数越少。在Fixed Timestep为20ms的前提下，将Maximum Allowed Timestep从333ms下调为100ms，那么无论卡顿情况如何，该帧最多只会调用5次，而不是最多17次。 另外建议大家先优化其他模块，这样可以减少下一帧的物理更新次数。 三、Contact数量是否合理 Contact数量异常通常有以下两种情况： 1）Contact数量过多 需考虑是否存在不必要的物理碰撞检测，例如盾牌与地面的碰撞。 2）Contact数量为0且存在物理耗时 Contact数量为0代表项目中没有碰撞或者Trigger，在不使用布料模拟等其他物理特性的情况下，可以考虑将物理模块关闭，即关闭AutoSimulation。 四、减少Raycast使用 Raycast，也就是我们常说的射线检测，其耗时与射线数量成正比。要控制Raycast的耗时，最简单的办法就是控制射线数量。 但部分项目确实需要较多的Raycast，例如弹幕游戏中Raycast数量往往较多，这种情况下可以通过Job System下的RaycastCommand将Raycast的耗时从主线程转移到子线程上，进而减少其耗时。 五、Auto Sync Transforms 勾选Auto Sync Transforms后，发生Physics Query时，Unity会将Rigidbody/Collider的Tranform变化同步到物理引擎，如Position，Scale等。另外勾选AutoSimulation时，Unity会在每次物理更新的时候自动同步一次Rigidbody和Collider，所以当关闭AutoSimulation后，如果项目中使用了射线检测或者NGUI，通常需要Auto Sync Transforms进行勾选，否则会发生射线检测结果不准确或者UI事件不响应的情况。 同步操作在一般情况下的性能开销很低，通常只在超大场景，且有大量物体均加载到场景中，才会出现较高的耗时问题。 六、逻辑代码相关 这里的逻辑代码指的是MonoBehavior脚本中的void FixedUpdate函数，在Profiler中通常会显示为xxx.FixedUpdate，此函数的调用次数会受到物理更新次数的影响。 比如场景中有10个挂载了Monster.cs的小怪，且该脚本在void FixedUpdate中写了逻辑代码。那么在当前帧物理更新次数为3时，在Profiler中会看到这一帧的Monoster.FixedUpdate调用了10*3=30次。 通常我们建议尽量将代码写入到Update函数中，这样可以减少对应逻辑的执行次数，从而减少逻辑代码的耗时。 以上就是在优化物理模块性能时需要关注的一些问题和对应的方法，如何操作还需要大家结合项目实际情况。当然我们UWA已经开发的GOT Online和本地资源检测都已经提供了很丰富的检测功能，希望能成为大家优化物理模块的神助攻。 【Unity性能优化系列】 《Unity性能优化系列—渲染模块》 《Unity性能优化系列—加载与资源管理》 《粒子系统优化——如何优化你的技能特效》 《Unity性能优化系列—Lua代码优化》 《Unity性能优化系列 — 资源内存泄漏》 《Unity性能优化 — 动画模块》 《Unity性能优化 — UI模块》
1）iOS导出AssetBundle需要30个小时的解决办法 ​2）Unity主程如何构建知识树 3）TMP中设置CullTransparent不能使Alpha为0的Text被Culling 4）Spine动画贴图在线性空间中出现黑边线 5）Spine数量太多导致Update性能损耗很大 这是第256篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） AssetBundle Q：现在游戏资源大概870MB（Android平台导出的所有AssetBundle），在Windows（i5-7500 CPU@3.40GHz，GTX1050，16GB内存，SSD硬盘）下导出全量的Android资源耗时大概是2小时，i9机器上1小时，6年前的i3差不多4小时。 在导出代码相同的情况下，在Mac Mini（2018，i5@3GHz，Intel630，16GB DDR4内存，SSD硬盘）大概花了38小时，换了Macbook Pro（2018，i9@2.9GHz，Redeon560X 4G，16GB DDR4内存，SSD硬盘），也要30小时，不知道问题出在哪里，请教各位大神，有没有遇到过类似问题？也怀疑是PVRTC的问题，把所有资源重新导成了ASTC也没改善。 A：看看是不是这个问题： https://forum.unity.com/threads/case-1192489-building-asset-bundles-decompresses-textures.742757/ 简单来说就是加个-nographics参数打包。 经过题主测试：最终生成在Mac Pro i9上面，40分钟不到。 感谢小埃拉@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60d3ee2de58b4719dd3e26cd Unity Q：如果以MMORPG游戏为准，Unity主程需要构建什么样的知识树？ A1：这个问题很难回答，也不敢回答。外面好多大神，除了国内的，还有国外的。主程序除了知识体系，还有管理体系、方案分析、人员成本控制、运营和产品的把控体系。单从知识树上说，推荐Milo Yip的知识树+御三家（Unreal，Unity和寒霜）任意一款引擎源码的完整阅读：https://cloud.tencent.com/developer/article/1005446。 感谢马古斯@UWA问答社区提供了回答 A2：这个问题很宽泛，是一个很大的问题，即便把范围缩小到MMORPG的范围也包含了很多东西。其实楼上马古斯回答得已经很好了，Milo的那个图谱基本涵盖了核心技术的内容。论管理，UWA学堂有篇浅显的文章，讲我之前创业的时候做的一些管理工作，有兴趣可以看看《创业团队的技术管理》。 简单分享一些我认为一个MMORPG游戏主程应该掌握的技术的内容： 1. 编程语言 这个很容易理解，是程序的基础。 常用的静态语言：C++、C#、Go 常用的动态语言：Lua、Python 然后因为通常游戏开发需要多种语言配合，所以还需要混合编程的概念和技巧。 这其中每种语言的语法和特性需要了解之外，一般会涉及到内存管理。 2. 编程规范 同样无需多言，作为主程，需要制定团队的编程规范，并且需要借助LuaCheck等静态检查等工具让规范可以更好地执行和保证。 3. 编程范式 这块我认为包含两大部分：一块是搭建整个GamePlay框架的框架方式，传统Entity继承、组件化、ECS是三种比较常规的；还有一块就是设计模式以及它们的应用。 4. 数据结构与算法，以及三维数学 默认是3D游戏，这块的基础知识还是需要的。 5. 游戏引擎 要有自己熟悉和掌握的游戏引擎，包括客户端与服务端。 6. GamePlay基础 除了前面提到的编程范式之外，GamePlay还有很多内容： 3C 游戏状态管理 常规的技能结构等等 7. 物理系统 如何设计碰撞检测、玩家控制、车辆系统、布娃娃、柔体效果。 8. 动画系统 骨骼动画原理、蒙皮的基本概念、动作融合、Rootmotion、动画重定向、角色模型和动作制作规范等等。 9. 特效系统 10. 渲染 了解基本的渲染管线 熟悉PBR 了解非真实渲染 各种后处理效果 11. 音频技术 FMOD、Wwise、引擎自带音频功能的使用 材质音效区分、P1/P3区分 混音回响等功能支持 12. 大世界场景制作和管理流程 如果是MMORPG，很可能需要大世界制作的技术，基于World Mashine、Houdini等PCG的生产管线如何建立，如何支持场景同时编辑，如何解决大世界的场景资源加卸载管理、超远视距的阴影如何实现、昼夜天气系统、HLOD等等等等。 13. 性能优化 常规的性能优化方式、以及如何持续监控发现性能问题并寻找方案解决，这里又包含CPU、GPU、带宽、内存和磁盘空间等等方面。 14. 游戏更新机制 客户端Patch系统 服务器热更新 客户端代码的Hotfix 15. 网络 通讯协议如何定义 优化网络流量 优化弱网下的游戏表现 加密 限流 16. 服务器存储 MySQL和Mongo等常规数据库的使用 17. 游戏AI 可能在客户端或者服务端，需要提供便利的编辑能力 基于深度学习的AI方案 18. 服务器稳定与安全 并发安全 进程监控 常规的运维知识 负载均衡 19. 常规的服务器技术 AOI管理 服务端物理 体素 同步方案 20. IDE与调试工具 常用的VS Code、Visual Studio等IDE，以及对应开发模式下的相应调试工具。 21. 版本管理软件 22. 编辑器开发 随手列，有些凌乱，一些技术内容本身就很复杂，继续拆分可以做得很细，比如在3C里面，如何做到主机级别的体验，就不是一个人能搞透彻的。 当然，肯定不止我列举的这些技术点，还有很多其它的和游戏类型相关的技术内容。 对于一款MMORPG项目的主程，我认为合格的标准是起码了解上面列举的80%以上的技术内容。当然每个人都有要自己擅长的领域，只要有足够的学习能力，能找到合适的人，技术上的内容都可以学习和弥补。只是主程本身就是要为项目的技术问题兜底，并要做技术方案的决策，因此对于技术宽度还是有较高要求的。 我自己的体验是：主程工作中，能有30%-40%的代码时间就很难得了，通常的工作是一天的沟通和会议。所以，主程的眼中，要有“事情”，更要有“人”——如何保持团队活力，如何激发团队战斗力等等，都是技术之外需要思考的难题。 感谢贾伟昊@UWA问答社区提供了回答 A3：前面两位大佬回答很全面了，学到了很多，膜拜！！！这里再补充一些，作为参考，希望有所帮助： 1. 技术方面 首先得是一个高级或者资深Unity开发，掌握不错的软件开发及计算机相关基础知识。 对Unity使用比较熟悉, 大的模块使用以及底层原理有一定的了解，比如动作，特效，GUI，Avatar，场景，Timeline和资源管理等等。 有一定的MMO开发经验，知道MMO游戏里面常见的概念以及实现方案，比如场景，位面，分线，AOI，属性同步，AI，Entity，技能，Buff和体素等等。 了解常用的性能优化工具，优化方案。 不断拓展技术视野，对技术方案的选型，决策会有帮助。 2. 管理方面 制定落地代码规范，了解什么样的代码规范是好的规范，适合项目的规范。 制定落地资源规范，能根据项目需求，以及性能预算制定美术同学认可的资源规范，并提供流程和工具帮助他们落地。 制定并不断优化开发流程，提升产出效率。 涉及的技术方面很多，一个人的精力毕竟有限，很难面面俱到，所以招人、识人、用人和找正确的人做正确的事也很重要。 如果是从纯技术岗升上来，一些基本的管理可能建议也要了解一些。 3. 产品方面 不是硬性要求，但是建议自己项目和同品类的竞品游戏，能有一定程度的游戏体验： 了解游戏里面的常见玩法。 体验游戏里面不同层级玩家的感受，了解他们的需求。 了解这类游戏需要做的技术突破点有哪些。 这样跟策划沟通产品需求、讨论优化方案，比较容易达成一致，会高效很多。 比如题主提到的MMORPG类型，目前《天涯明月刀》、《天谕》、《倩女幽魂》和《一梦江湖》等都是可以体验一下。 最后，我觉得一个好的心态也很重要。一个项目开发过程中，会有困难、挫折、甚至失败，遇到这些情况，保持正向积极的心态，同时给组员正向的影响，从而给大家信心，带领大家取得更好的成果。 感谢呼噜怪@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60d1eb54e58b4719dd3e26bc UGUI Q：Unity 2020，CanvasRenderer中设置CullTransparentMesh属性可以让Alpha为0的元素不绘制网格，但是TMP为什么不行？ 查看了UGUI的源码和TextMeshPro的源码发现都设置了网格，且都不为空，UGUI里是canvasRenderer.SetMesh(workerMesh)；TMP里是 m_canvasRenderer.SetMesh(m_mesh)；而CanvasRenderer源码显示是这样的public extern bool cullTransparentMesh { [MethodImpl(MethodImplOptions.InternalCall)] get; [MethodImpl(MethodImplOptions.InternalCall)] set; }，怎么做才能让TMP元素是透明时像Text一样剔除网格绘制？ A：TMP网格中有不用的顶点颜色不是为0，也就是设置Color中Alpha不能设置未使用顶点的，导致传递给底层绘制的Mesh里面有颜色不为0的顶点，无法剔除，同时又有更大的问题，顶点信息不能清零，导致如果一个TMP里有100个字符，有400个顶点信息，重新赋值给一个文字，还是400个顶点信息，只是396个顶点不用来绘制罢了，所以TMP慎用，要么改源码，要么等官方修复。 感谢题主cwf@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60d29dd9e58b4719dd3e26bf Spine Q：我们使用的Unity版本是2017.4.3f1，在Gamma空间下面做的Spine动画都没有问题，现在前后到Linear空间下面出现Spine动画好多小的接缝的地方出现黑边，如下图中的鼻子，其实鼻子在Spine中是一个小图，贴到脸上面的，现在Linear下面就会看到周围模糊的黑边，预计是这个鼻子贴图周围的Alpha值影响的，但是现在具体是什么原因还不确定，但是在Gamma空间下面都是正确的。 Gamma空间下面的图，鼻子的地方是正常的： Linear空间下面的，鼻子的地方就有一圈黑边： 以下是两个清晰一点的局部图，对比一下： A：第一，Spine关闭预乘Alpha重新导出； 第二，Shader勾选Straight Alpha Texture。 参考以下信息：http://zh.esotericsoftware.com/forum/Spine-JSON-12274?p=54752#p54752 感谢动感光波@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5b74f0e11e88b37d34e652de Spine Q：Spine数量太多导致Update性能损耗很大，如下图，SkeletonAimation.Update的耗时太长了，有什么有效的解决办法吗？ A1：转到GPU。 感谢付丶火柴@UWA问答社区提供了回答 A2：以下请参考： Spine多不代表就卡，主要在于顶点的多少； 还有就是在Unity的C#代码，因为这个函数导致卡是很常见的，其实这个卡就是Spine做裁剪的时候的遮罩，也就是遮罩的顶点太多了，因此让你的Spine动画师去优化； 总结起来就是不该被遮罩的顶点不要遮，顶点能少就少。 感谢夏霖锐@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60b085bd6bb31032f9791991 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）