尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity Layer机制深度解析:从碰撞检测到渲染优化的5大实战场景

Unity Layer机制深度解析:从碰撞检测到渲染优化的5大实战场景 1. 项目概述为什么Unity新手总在Layer上栽跟头如果你刚开始用Unity大概率遇到过这种场景明明给角色和墙壁都加了碰撞体角色却穿墙而过或者场景里明明有盏灯但某个物体死活就是不被照亮。一通折腾下来发现罪魁祸首往往不是脚本写错了而是那个不起眼的“Layer”设置没搞对。Layer中文叫“层”是Unity里一个基础但极其重要的概念它像是一个个透明的标签你可以把游戏对象GameObject分门别类地放进去。它的核心作用就两个选择性交互和选择性渲染。听起来简单但新手最容易犯的错就是“乱用”——要么所有东西都堆在“Default”层里要么随意创建新层却理不清它们之间的关系导致碰撞失灵、灯光无效、射线检测不到性能还莫名其妙地变差。我自己带过不少新人项目几乎每个项目初期都要花时间重构一遍Layer的使用逻辑。乱用Layer的后果轻则功能异常需要你花大量时间Debug重则破坏整个物理或渲染系统的效率在项目后期变成难以收拾的技术债。这篇文章我就结合碰撞检测、灯光剔除、射线检测等5个最典型的实战场景帮你把Layer那点事彻底理清楚。无论你是刚入门的新手还是已经踩过一些坑的开发者都能从这里找到一套清晰、可复用的Layer管理思路让你告别混乱写出更健壮、性能更好的代码。2. Layer核心机制深度解析不只是个标签在深入实战前我们必须把Layer的底层机制吃透。很多教程只告诉你怎么设置却不解释为什么这才是混乱的根源。2.1 Layer的本质与LayerMask位运算的魔法Unity中一共有32个Layer0-31。其中0-7是Unity内置的如Default TransparentFX Ignore Raycast等8-31留给我们自定义。你可能会问为什么是32个这背后是计算机效率的考量。Layer不是一个简单的枚举列表它的核心搭档是LayerMask。当你看到代码里出现LayerMask.GetMask(“Ground”)或者Physics.Raycast(..., layerMask)时它内部处理的其实是一个32位的整数int。每一位bit对应一个Layer。例如第0位bit 0为1代表Default层被包含。第8位bit 8为1代表我们自定义的、索引为8的层比如叫“Enemy”被包含。LayerMask的本质是一个位掩码Bitmask。当我们说“检测与Enemy层的碰撞”时并不是传递一个字符串“Enemy”而是传递一个只有第8位是1其他位都是0的二进制数。LayerMask.GetMask(“Enemy”, “Ground”)这个函数的作用就是把“Enemy”假设是第8层和“Ground”假设是第9层这两个层名转换成一个二进制数其中第8位和第9位是1结果就是一个十进制的数字比如 2^8 2^9 256 512 768。关键理解所有与Layer相关的API无论是物理系统的Raycast、OverlapSphere还是渲染相关的Camera.cullingMask其底层都是在进行高效的位运算。检查一个对象是否在某个LayerMask中就是检查该对象所在Layer对应的那一位在掩码整数中是否为1。这是O(1)时间复杂度的操作速度极快。2.2 碰撞矩阵Layer Collision Matrix物理交互的总开关这是新手最容易忽略也最容易引发诡异Bug的地方。在Unity编辑器中打开Edit - Project Settings - Physics或Physics 2D你会看到一个庞大的32x32的矩阵表格这就是碰撞矩阵。它的规则很简单行和列的交点被勾选代表这两个层之间的物体会发生碰撞检测如果不勾选即使它们都有碰撞体也会相互穿透物理引擎会完全忽略它们。很多新手会在这里踩两个大坑盲目创建新层后忘记配置矩阵你创建了一个“Bullet”层给子弹一个“Enemy”层给敌人。如果你没有在碰撞矩阵中勾选“Bullet”行和“Enemy”列的交点那么你的子弹就会华丽地穿过敌人没有任何碰撞事件发生。你会怀疑是不是碰撞体没加对或是脚本没写好但其实根源在这里。过度勾选导致性能浪费另一个极端是把所有层之间的碰撞都勾上。想象一下你的UI元素层比如一个世界空间的提示板和背景装饰物层比如远处的山脉也进行碰撞检测这会产生大量毫无意义的物理计算严重消耗CPU资源。正确的做法是像设计数据库表关系一样仔细规划你的碰撞矩阵。只勾选那些在游戏逻辑上确实需要发生物理交互的层组合。例如“Player”层需要和“Ground”、“Enemy”、“Item”层碰撞但不需要和“UI”或“IgnoreRaycast”层碰撞。2.3 渲染层剔除Camera Culling Mask看不见的就不画Camera组件上有一个Culling Mask属性它同样是一个LayerMask。它的作用是告诉摄像机“你只需要渲染这些层上的物体其他层的物体即使在你视野里也直接跳过剔除。”这个功能在优化和实现特定效果时非常有用性能优化你可以创建一个“FarDistance”层把那些极其遥远、细节看不清的背景物体比如远山、星空盒放进去。然后为主摄像机取消勾选这个层这样这些物体就不会进入渲染管线节省了宝贵的GPU时间。再专门创建一个渲染远景的、视野范围更大的、画质更低的副摄像机只渲染“FarDistance”层来实现远景。特效分层渲染比如你想做一个只在主角身上显示的热成像效果。你可以把主角放到“Player”层然后创建一个新的摄像机其Culling Mask只勾选“Player”层并设置其渲染到一个单独的RenderTexture上再进行后期处理最后叠加到主画面上。实操心得不要用Culling Mask来实现游戏对象的“显示/隐藏”。GameObject.SetActive(false)或Renderer.enabled false是更合适的选择。Culling Mask是渲染阶段的批量过滤用于宏观层面的管理和优化而不是微观的对象控制。3. 实战场景一精准的碰撞检测体系设计这是Layer最经典的应用。一个混乱的碰撞体系会让游戏逻辑漏洞百出。3.1 场景构建玩家、敌人、子弹与地形假设我们有一个简单的射击游戏包含以下对象玩家角色Player敌人Enemy玩家子弹PlayerBullet敌人子弹EnemyBullet可行走的地面Ground仅供装饰的灌木丛Decoration无碰撞触发任务的NPCNPC只有触发器3.2 Layer定义与碰撞矩阵配置首先我们需要创建并定义清晰的Layer。在Edit - Project Settings - Tags and Layers中我们定义如下自定义层假设使用8-15层Layer 8: PlayerLayer 9: EnemyLayer 10: PlayerBulletLayer 11: EnemyBulletLayer 12: GroundLayer 13: Decoration(用于纯视觉装饰)Layer 14: NPC(用于触发器交互)Layer 15: IgnoreAll(我们自定义的一个“忽略一切”层后面会讲用途)接下来配置Physics项目设置中的碰撞矩阵。我们的设计逻辑是Player 应该与Ground行走、Enemy受伤害、EnemyBullet受伤害、NPC触发对话碰撞。不应与PlayerBullet避免自伤、Decoration、IgnoreAll碰撞。Enemy 应该与Ground、Player、PlayerBullet受伤害碰撞。不应与EnemyBullet、Decoration、NPC、IgnoreAll碰撞。PlayerBullet 应该与Enemy、Ground击中地面消失碰撞。不应与Player、PlayerBullet、Decoration、NPC、IgnoreAll碰撞。EnemyBullet 应该与Player、Ground碰撞。逻辑同上。Ground 与所有需要立足的实体Player, Enemy和所有子弹碰撞。Decoration和NPC 它们通常只有触发器Collider withIs Trigger true不需要物理碰撞。因此在碰撞矩阵中取消勾选Decoration和NPC与其他所有层的物理碰撞。它们的交互通过触发器事件OnTriggerEnter来处理这不受碰撞矩阵控制只要求双方都有碰撞体组件。IgnoreAll 这是一个“黑洞”层在碰撞矩阵中取消它与其他所有层包括它自己的勾选。任何放到这一层的物体物理世界将完全无视它。根据这个逻辑去矩阵里仔细勾选。配置好的矩阵应该是一个有清晰区块的图案而不是一片全绿。3.3 代码中的使用如何正确传递LayerMask在脚本中我们需要在发射子弹或检测碰撞时指定正确的LayerMask。public class PlayerShoot : MonoBehaviour { public GameObject bulletPrefab; public Transform firePoint; // 定义子弹能击中的层Enemy 和 Ground public LayerMask bulletHitLayers; void Update() { if (Input.GetButtonDown(“Fire1”)) { GameObject bullet Instantiate(bulletPrefab, firePoint.position, firePoint.rotation); // 假设子弹脚本里有一个设置Layer的方法 bullet.GetComponentBullet().SetLayerAndMask(LayerMask.NameToLayer(“PlayerBullet”), bulletHitLayers); } } } public class Bullet : MonoBehaviour { private int _bulletLayer; private LayerMask _hitMask; public float speed 20f; public float lifeTime 2f; public void SetLayerAndMask(int layer, LayerMask hitMask) { // 设置子弹对象自身的Layer gameObject.layer layer; _bulletLayer layer; _hitMask hitMask; Destroy(gameObject, lifeTime); } void Update() { // 移动逻辑... transform.Translate(Vector3.forward * speed * Time.deltaTime); } void OnCollisionEnter(Collision collision) { // 碰撞检测逻辑物理引擎已经根据碰撞矩阵帮我们过滤了 // 能进入这个回调说明对方一定是在_hitMask指定的层中 Debug.Log($”Bullet hit: {collision.gameObject.name} on layer {collision.gameObject.layer}”); // 处理击中效果比如造成伤害 if (((1 collision.gameObject.layer) _hitMask) ! 0) // 再次位运算确认通常可省略 { // 应用伤害等逻辑 } Destroy(gameObject); } }在Inspector中你可以通过下拉菜单为PlayerShoot脚本的bulletHitLayers变量勾选Enemy和Ground层非常直观。避坑技巧在代码中创建LayerMask时我强烈推荐使用LayerMask.GetMask(“Enemy”, “Ground”)这种方式而不是硬编码数字如768。因为层索引可能会在项目开发中调整硬编码数字会让代码变得极其脆弱且难以维护。GetMask方法通过层名来获取安全得多。4. 实战场景二灯光剔除Light Culling Mask与性能优化灯光是性能杀手尤其是实时光Realtime Light。每一盏实时光都会让需要被它照亮的物体执行一次光照计算。滥用灯光剔除帧率说掉就掉。4.1 问题场景全场景照亮与性能危机假设你做了一个室内场景有一盏华丽的吊灯实时光用来照亮餐桌。但默认情况下这盏灯会照亮场景里所有被渲染的物体包括墙外的街道、地下室的车库——这些根本看不到的地方。GPU在辛勤地为这些不可见的物体计算光照性能被白白浪费。4.2 解决方案使用Light Culling Mask进行精准照明Light组件和Camera一样也有一个Culling Mask属性。它的意思是“这盏灯只照亮这些层上的物体。”操作步骤创建新层例如 “InteriorLighting”。将需要被这盏吊灯照亮的物体餐桌、椅子、部分地板分配到 “InteriorLighting” 层。注意一个物体可以同时属于多个层吗不可以一个GameObject只能有一个Layer属性。所以你需要仔细规划。对于这个例子这些家具可能本来就在“Furniture”层你可以选择将“Furniture”层加入灯的剔除掩码或者为需要特殊照明的物体单独设层。选中吊灯光源在Inspector中将其Culling Mask设置为只勾选 “InteriorLighting” 层可能还包括“Default”如果有些基础物体你没动过层。立刻这盏灯的计算量就大幅下降了因为它不再需要处理场景中其他95%的物体。4.3 混合使用静态烘焙与动态剔除的结合对于大型场景最佳实践是静态物体Static尽可能使用光照烘焙Light Baking。将静态物体的光照信息提前计算并“烘焙”到贴图上运行时零开销。动态物体Dynamic和关键区域使用实时光Culling Mask。比如玩家手中的手电筒动态光其Culling Mask可以只包含“Enemy”、“Interactable”等层这样它只会照亮怪物和可交互物品营造紧张氛围的同时性能消耗极小。分层渲染对于需要复杂光照效果的角色可以将其单独放在一个层如“Character”用一盏专用的、参数精细的灯光只照亮这个层而场景主光则排除这个层。这样可以实现角色和场景光照的相对独立控制避免互相干扰。注意事项修改物体的Layer会影响其所有受层影响的组件包括碰撞和渲染。如果你只是希望某盏灯不照亮某个物体但该物体仍需与其他物体碰撞那么应该使用灯的Culling Mask而不是去改物体的Layer。物体的Layer是其全局身份而灯的Culling Mask是光源的局部过滤器。5. 实战场景三射线检测Raycast的精确过滤射线检测是游戏逻辑中无处不在的技术从鼠标点击拾取到武器瞄准都离不开它。不指定LayerMask的射线检测就像在黑夜里乱开枪。5.1 常见错误不做过滤的全场景检测新手常写的代码Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { // 点击到了任何有碰撞体的东西 }这段代码会检测从摄像机出发射线路径上第一个碰到的任何层的碰撞体。这会导致你点击UI时可能选中了后面的3D物体或者你想选中敌人却选中了它脚下的地面。5.2 精准过滤使用LayerMask限定检测目标假设我们想实现一个“点击选中敌人”的功能。敌人已经在“Enemy”层。地面在“Ground”层。UI元素世界空间的UI在“UI”层。我们不希望点击地面或UI时选中敌人。正确的做法是public LayerMask enemyLayerMask; // 在Inspector中只勾选Enemy层 void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 只检测Enemy层的碰撞体 if (Physics.Raycast(ray, out hit, Mathf.Infinity, enemyLayerMask)) { Debug.Log(“选中敌人: ” hit.collider.gameObject.name); // 显示选中效果、获取敌人引用等 } else { // 没有点击到敌人可能是点击了空地、UI或其他 } } }5.3 复杂过滤排除特定层与多层级检测有时需求更复杂“检测除了玩家和UI之外的所有物体”。这时可以利用位操作符~按位取反。int playerLayer LayerMask.NameToLayer(“Player”); int uiLayer LayerMask.NameToLayer(“UI”); // 创建一个包含Player和UI层的掩码然后取反 LayerMask maskToExclude (1 playerLayer) | (1 uiLayer); LayerMask finalMask ~maskToExclude; if (Physics.Raycast(ray, out hit, Mathf.Infinity, finalMask)) { // 现在射线会忽略Player和UI层击中其他任何层如Ground, Enemy, Item等 }还有一种常见需求是“检测多个特定层中的第一个物体”并按优先级处理。例如射线先检测“Item”可拾取物品如果没检测到再检测“Ground”用于移动指示。这可以通过按顺序进行多次Raycast并指定不同的LayerMask来实现或者使用Physics.RaycastAll获取所有击中点再排序筛选。实操心得对于需要频繁进行的射线检测如每帧执行的瞄准线一定要指定尽可能精确的LayerMask。Physics.Raycast的底层会遍历场景中所有在掩码内的碰撞体并进行昂贵的相交测试。一个精确的掩码能极大减少遍历数量提升性能。永远不要在不必要的时候使用无掩码或全包含掩码~0。6. 实战场景四摄像机分层渲染与后期处理除了灯光摄像机也可以通过Culling Mask实现强大的分层渲染效果这是实现高级画面技巧的基础。6.1 场景构建主世界、UI与特效层想象一个游戏主角身上有一个持续的能量护盾特效半透明、发光。如果这个特效和场景一起渲染可能会因为深度排序Z-fighting或与场景光混合产生奇怪的效果。我们可以通过分层渲染来优雅地解决。6.2 实现分层渲染与叠加创建层与摄像机创建两个新层“MainWorld” 和 “ShieldEffect”。将场景中所有普通物体地形、建筑、NPC放在“MainWorld”层。将主角的能量护盾特效物体一个特殊的粒子系统或模型放在“ShieldEffect”层。注意主角本体可能仍在“Player”层这没关系一个物体只能属于一个层但特效可以是独立的子物体。在场景中创建两个摄像机Camera_Main 主摄像机。其Culling Mask只勾选“MainWorld”和“Player”如果需要渲染主角本体。不勾选“ShieldEffect”。它的Depth设为 0。Camera_Shield 特效专用摄像机。其Culling Mask只勾选“ShieldEffect”。将其Clear Flags设置为Depth only这样它不会清除主摄像机渲染的画面只会在自己的深度缓冲区上绘制。它的Depth设为 1大于主摄像机。配置渲染Camera_Shield会将自己渲染的“ShieldEffect”层物体叠加到Camera_Main已经渲染好的画面上。你还可以为Camera_Shield单独添加后期处理效果如Bloom、颜色校正让护盾特效看起来更炫酷且不影响主世界画面。6.3 应用扩展小地图、武器描边与水下效果这个模式可以扩展出很多玩法小地图创建一个只渲染“MiniMap”层的摄像机将其视图正交投影Orthographic渲染到屏幕一角或一个独立的UI纹理上。武器高亮描边将需要描边的武器放在“Outline”层。用一个专门的摄像机使用替换着色器Replacement Shader渲染这个层生成一张只有武器轮廓的图再进行图像处理生成描边最后叠加到主画面。水下扭曲效果创建一个只渲染水下区域的摄像机对其渲染画面做扭曲变形后期处理再叠加到主画面模拟水下视觉。注意事项使用多摄像机分层渲染会增加Draw Call。因为同一个物体如果被多个摄像机看到就会被渲染多次。因此要严格控制分层渲染的范围和对象数量避免性能过度开销。通常用于少数关键特效或UI而不是大批量场景物体。7. 实战场景五自定义“IgnoreAll”层与触发器管理我们之前提到了一个自定义的“IgnoreAll”层。这个层是一个强大的管理工具。7.1 为何需要“IgnoreAll”层有些游戏对象需要碰撞体组件但永远不希望参与物理碰撞。例如一个仅用于触发剧情对话的区域触发器。一个用于声音传播的体积触发器。一个用于AI导航识别的区域标记触发器。一个视觉上复杂但用简化的碰撞体代理Proxy代替的装饰物。如果你把这些物体的碰撞体留在“Default”层它们会与所有勾选了“Default”的层发生物理碰撞产生不必要的物理计算甚至可能导致角色被空气墙卡住。7.2 创建与使用“IgnoreAll”层在层设置中创建一个新层命名为“IgnoreAll”或“TriggerOnly”。在Physics碰撞矩阵中找到“IgnoreAll”这一行和这一列取消所有勾选。确保它不与任何层包括它自己发生物理碰撞。将所有仅用作触发器的碰撞体对象的Layer设置为“IgnoreAll”。在需要进行触发器检测的代码中如玩家进入对话区域你需要显式地指定检测这个层。因为物理碰撞被禁用了但触发器事件OnTriggerEnter仍然有效前提是双方都有碰撞体且至少一个是刚体。public class DialogueTrigger : MonoBehaviour { void Start() { // 确保这个触发器对象在IgnoreAll层 gameObject.layer LayerMask.NameToLayer(“IgnoreAll”); // 确保碰撞体是触发器 GetComponentCollider().isTrigger true; } void OnTriggerEnter(Collider other) { // 当其他碰撞体进入时触发 // 通常需要检查进入者是否是玩家 if (other.gameObject.CompareTag(“Player”)) { StartDialogue(); } } }而对于玩家的移动碰撞检测其LayerMask不应该包含“IgnoreAll”层这样玩家在物理上就会穿透这些触发器区域只触发逻辑事件不受物理阻挡。7.3 管理复杂触发器体系在一个大型项目中触发器可能有多种类型对话触发器、伤害区域、存档点、传送门等。你可以进一步细分方案A全部放在“IgnoreAll”层在OnTriggerEnter里用Tag或自定义组件来区分类型。方案B为不同类型的触发器创建不同的层如“TriggerDialogue”、“TriggerDamage”。在碰撞矩阵中这些层同样设置为不与任何层物理碰撞。在代码中你可以通过检测碰撞对象的Layer来快速判断类型这比比较Tag或GetComponent可能更快一些。避坑技巧对于任何触发器务必确保其Collider的Is Trigger属性被勾选。同时触发器不需要通常也不应该附加Rigidbody组件除非你需要它受物理力影响或移动。对于静态触发器不加刚体即可。对于动态移动的触发器比如一个移动的伤害区域可以添加一个Rigidbody并设置为Kinematic运动学。8. 常见问题排查与性能调优实录即使理解了原理在实际开发中还是会遇到各种稀奇古怪的问题。这里记录几个我踩过的坑和解决方案。8.1 问题一射线检测Raycast什么都检测不到可能原因及排查步骤LayerMask错误这是最常见的原因。检查你代码中Physics.Raycast传入的LayerMask参数。是否不小心传入了0意味着不检测任何层或者掩码计算错误使用Debug.Log(LayerMask.LayerToName(...))或直接打印掩码的整数值来调试。碰撞体缺失或禁用确保目标物体有Collider组件并且该组件是启用Enabled状态。距离问题检查Raycast的maxDistance参数。如果设得太短射线可能还没碰到目标就结束了。可以暂时设为Mathf.Infinity测试。起始点问题射线的起点是否在另一个碰撞体内部如果是Raycast可能一开始就“击中”了自己所在的碰撞体。可以使用Raycast(..., out hit, ..., layerMask, QueryTriggerInteraction.Ignore)来忽略触发器或者确保起点在空处。2D vs 3D混淆你在用Physics2D.Raycast检测3D物体或者反过来。确保API匹配。8.2 问题二设置了Culling Mask但灯光/摄像机还是渲染了不该渲染的物体可能原因及排查步骤子物体层继承Unity中子物体会默认继承父物体的Layer。你可能只改了父物体的层但子物体还留在原来的层。在Inspector中选中父物体在Layer下拉菜单旁边有一个选项“Yes, change children”可以批量修改子物体层。多个光源/摄像机影响场景中可能有多个光源或摄像机。你只修改了其中一个的Culling Mask但其他光源/摄像机仍然在渲染那些物体。需要检查所有相关的光源和摄像机。静态合批Static Batching如果物体被标记为Static并参与了静态合批它们的渲染可能会被合并合批后的渲染调用可能不受单个物体层的控制而是以某种组合形式进行。对于需要精细分层控制的对象谨慎使用静态合批或将其排除在合批之外。8.3 问题三物理碰撞表现异常该撞的不撞不该撞的撞了可能原因及排查步骤碰撞矩阵未配置绝对的第一嫌疑犯。立刻去Edit - Project Settings - Physics检查相关层的交叉点是否勾选。碰撞体尺寸或位置碰撞体可能太小、太大或者位置偏移导致视觉上看起来应该碰撞实际上物理形状没有相交。使用场景视图的“线框”模式或开启Gizmos显示碰撞体来检查。刚体属性至少一方需要有Rigidbody组件才能触发碰撞回调OnCollisionEnter。双方都是静态碰撞体不会产生碰撞事件。触发器Is Trigger则不受此限但需要至少一个刚体来驱动事件。层级缩放Scale如果物体或其父节点有非均匀缩放Scale的x, y, z值不同可能会导致碰撞体形状计算异常。尽量避免非均匀缩放如果无法避免考虑使用MeshCollider但性能开销大或调整碰撞体形状来匹配。8.4 性能调优建议最小化碰撞矩阵这是最重要的性能优化点之一。定期审查你的碰撞矩阵取消所有不必要的勾选。特别是“UI”、“IgnoreRaycast”、“TransparentFX”等系统层确保它们只与真正需要交互的层碰撞通常很少。善用“Ignore Raycast”层Unity内置了“Ignore Raycast”层第2层。将那些永远不需要被射线检测到的物体如纯视觉粒子效果、远背景放到这一层。在你的大部分射线检测代码中默认排除这一层。区分物理与触发器层如前所述使用自定义的“IgnoreAll”或“TriggerOnly”层来管理纯触发器物体将它们从物理碰撞世界中彻底移除。灯光Culling Mask是救星对于点光源、聚光灯这种影响范围有限的实时光务必使用Culling Mask将其影响范围限制在必要的几层内。对于方向光Directional Light由于它影响整个场景Culling Mask的优化效果相对较小但依然可以用来排除一些绝对不需要照亮的物体如UI层。避免频繁的、无掩码的Overlap检查Physics.OverlapSphere这类函数如果不指定LayerMask会检查范围内所有碰撞体开销巨大。务必为其指定最精确的掩码。管理好Unity的Layer就像整理好一个杂乱的工具箱。一开始可能会觉得多此一举但一旦建立起清晰的规范你会发现Debug效率、代码可读性和项目运行性能都会得到质的提升。这五个实战场景几乎覆盖了Layer 90%的应用场景从物理到渲染从检测到优化。下次创建新对象时先别急着往Default层里丢花两秒钟想想它应该属于哪个“部门”你的项目会因此感谢你。
返回列表