【知识链】Unity -> -> 脚本系统 -> 访问游戏对象 -> 静态属性
【摘要】本文介绍了Unity中游戏对象的静态和动态类型,并说明了如何修改静态属性。
文章目录
第一章 Unity中的静与动
在Unity中,静态(Static)对象和动态(Dynamic)对象有着不同的属性和用途,它们的主要区别在于它们是否能够在运行时移动或变化,以及Unity对它们应用的优化策略。
静态对象是指那些在游戏中不会动的东西,比如地面、建筑物,它们永远固定在那里。Unity可以对这些静态对象进行优化,以提高游戏性能,因为这些东西不会变动。
动态对象则是会动的东西,比如玩家角色、敌人或者会滚动的球。这些对象在游戏运行时会改变位置、大小或方向,Unity需要实时计算它们的变化,所以不能对它们进行像静态对象那样的优化。
简单来说,静态对象是固定不动的,性能更好;动态对象是会动的,但需要更多计算。
需要特别说明的是,在构建应用时Unity会对静态对象进行优化。Unity提供了相应的方法来改变对象的静态属性,但这种改变只是在编辑状态时有效,而对于运行时即使设置了静态属性也不会进行相应的优化。当然运行时静态属性的改变也有其他一些用途,将在本文中一一说明。
第二章 静态和动态对象
1. 静态对象(Static Objects)
定义:
静态对象是指在运行时不会移动、缩放或旋转的对象。它们的变换(Transform)属性在游戏运行期间保持不变。
主要特点:
-
性能优化:
- 静态批处理(Static Batching):Unity可以对静态对象进行静态批处理,这样可以大大减少绘制调用(Draw Calls),提高渲染性能。静态批处理通过将多个静态对象合并为一个网格(Mesh)来减少渲染开销。
- 光照贴图(Lightmapping):静态对象可以在编辑器中进行光照贴图烘焙,这意味着光照信息在构建时就被预先计算并存储在贴图中,减少了运行时的光照计算开销。
- 剔除优化(Occlusion Culling):静态对象可以参与剔除优化,Unity可以预先计算哪些对象在视野之外,从而减少不必要的渲染。
-
物理计算:
- 静态对象通常不会参与物理模拟(如刚体、重力等),因为它们的状态不会发生变化。Unity可以对这些对象应用简化的物理计算,从而提高性能。
场景构建: - 静态对象通常用作场景的不可移动部分,例如地形、建筑物、固定障碍物等。它们为游戏世界提供了稳定的背景。
- 静态对象通常不会参与物理模拟(如刚体、重力等),因为它们的状态不会发生变化。Unity可以对这些对象应用简化的物理计算,从而提高性能。
设置方式:
- 在Unity编辑器中,选择对象后,可以在Inspector面板中勾选 “Static” 复选框,或选择具体的静态类型(如光照静态、导航静态等)。
2. 动态对象(Dynamic Objects)
定义:
动态对象是指在运行时会发生移动、缩放、旋转或其他变化的对象。它们的变换属性在游戏运行期间是可变的。
主要特点:
-
没有静态优化:
-
动态对象不会参与静态批处理或光照贴图烘焙,因为它们的状态是动态变化的,无法预先计算和优化。渲染这些对象时需要单独进行处理,可能会增加绘制调用。
-
物理计算:
- 动态对象通常是物理模拟的一部分,它们可能受到重力、碰撞、力等影响。例如,玩家角色、敌人、可移动的道具等都属于动态对象。
-
实时光照:
- 动态对象通常使用实时光照,这意味着光照计算在运行时进行,适应对象的实时位置和状态变化。
-
交互性:
- 动态对象通常是游戏中可交互的部分,它们响应玩家输入或环境变化,如推拉门、移动平台、掉落的物体等。
常见用途:
- 动态对象用于所有需要在运行时发生变化的场景中,例如玩家角色、NPC、可移动的道具、环境变化的物体等。
3. 对比总结
特性 | 静态对象 | 动态对象 |
---|---|---|
移动/旋转/缩放 | 在运行时不变 | 在运行时会变化 |
性能优化 | 静态批处理、光照贴图、剔除优化等 | 无静态优化,渲染和物理计算开销较高 |
物理计算 | 通常不参与物理模拟或使用简化的计算 | 参与物理模拟,响应力、重力和碰撞等 |
光照 | 光照贴图烘焙(静态) | 实时光照(动态) |
场景用途 | 固定不变的场景元素,如建筑、地形 | 可移动的游戏元素,如角色、道具 |
4. 实际应用
- 选择静态对象:如果你确定某个对象在游戏运行时绝不会移动、旋转或缩放,建议将其标记为静态对象,以便Unity应用各种性能优化。
- 选择动态对象:对于需要在运行时发生变化或与玩家互动的对象,应该保持它们为动态对象。
第三章 如何修改对象的静态属性
在Unity中,可以通过以下几种方法来改变对象的static属性:
1. 在编辑器中手动设置
操作方法:
- 在Unity编辑器中,选中需要设置为静态的对象。
- 在Inspector面板的顶部,有一个名为"Static"的复选框。
- 勾选此复选框后,弹出下拉菜单,你可以选择应用哪些静态标志(例如BatchingStatic、NavigationStatic等)。如果选择了Everything,对象将会被标记为所有类型的静态对象。
用途:适用于手动设置场景中的对象为静态。
2. 在脚本中动态设置(运行时修改)
操作方法:
- 通过C#脚本,在运行时修改对象的静态属性。虽然在运行时可以修改,但实际上在运行时修改对象的静态属性并不会触发Unity的静态优化,静态标志的实际作用在编辑器中设置并在构建时生效。
示例代码
GameObject myObject = GameObject.Find("MyObject");
// 设置对象为静态
myObject.isStatic = true;
// 取消对象的静态设置
myObject.isStatic = false;
用途:适用于在特殊情况下根据游戏逻辑临时改变对象的静态状态,但不推荐用于期望的性能优化场景,因为运行时设置isStatic并不会应用静态优化。
3. 使用 GameObjectUtility.SetStaticEditorFlags(在编辑器脚本中)
操作方法:
- GameObjectUtility.SetStaticEditorFlags方法通常用于编辑器扩展或批量设置多个对象的静态标志。这在你需要创建自定义工具或编辑器脚本时非常有用。
示例代码:
using UnityEditor;
using UnityEngine;
public class SetStaticFlagsExample : MonoBehaviour
{
[MenuItem("Tools/Set Static Flags")]
static void SetStaticFlags()
{
GameObject myObject = GameObject.Find("MyObject");
// 设置对象的静态标志
GameObjectUtility.SetStaticEditorFlags(myObject, StaticEditorFlags.BatchingStatic | StaticEditorFlags.NavigationStatic);
// 取消所有静态标志
GameObjectUtility.SetStaticEditorFlags(myObject, 0);
}
}
用途:适用于在编辑器中批量或程序化地设置对象的静态属性,通常在构建场景或优化时使用。
还有一种比较简便的方法
参考:https://discussions.unity.com/t/how-to-set-use-staticeditorflags-cant-seem-to-set-them-from-script/476886/13
public static void SetStaticEditorFlag(GameObject obj, StaticEditorFlags flag, bool shouldEnable)
{
var currentFlags = GameObjectUtility.GetStaticEditorFlags(obj);
if (shouldEnable)
{
currentFlags |= flag;
}
else
{
currentFlags &= ~flag;
}
GameObjectUtility.SetStaticEditorFlags(obj, currentFlags);
}
使用方法:
SetStaticEditorFlag(gameObject, StaticEditorFlags.LightmapStatic, true)
总结
- 在编辑器中手动设置:最常用的方式,适合单独设置或通过Inspector面板批量设置对象为静态。
- 在脚本中动态设置:可以在运行时修改对象的isStatic属性,但无法利用静态优化。
- 使用 GameObjectUtility.SetStaticEditorFlags:适合在编辑器脚本中批量设置对象的静态属性,特别适用于需要开发自定义工具或进行批量操作时。
第四章 StaticEditorFlags
StaticEditorFlags 是 Unity 中用来标记一个对象在编辑器中静态属性的枚举类型。通过设置这些标志,你可以决定 Unity 如何在构建时优化这些对象。以下是 StaticEditorFlags 的各个类型及其含义:
1. BatchingStatic
- 描述:用于静态批处理优化。将对象标记为 BatchingStatic 后,Unity 会在构建时将多个静态对象合并为一个网格,以减少绘制调用(Draw Calls)。
- 用途:适用于大量的、小而重复的几何体,如建筑物的墙体、柱子等。
2. NavigationStatic
- 描述:用于导航网格优化。将对象标记为 NavigationStatic 后,Unity 会在导航网格烘焙时将其视为不可移动的障碍物或路径的一部分。
- 用途:适用于需要在AI路径规划中被视为不可穿越的静态对象,如建筑物、固定的障碍物。
3. OccludeeStatic
- 描述:用于遮挡剔除优化。标记为 OccludeeStatic 的对象可以被遮挡剔除系统考虑,用于决定哪些对象可以被视野外的其他对象遮挡,从而不进行渲染。
- 用途:适用于较大的、可能会遮挡其他对象的静态物体,如建筑物或大型场景物体。
4. OccluderStatic
- 描述:用于遮挡物优化。标记为 OccluderStatic 的对象可以作为遮挡物使用,用于遮挡视野中的其他对象。
- 用途:适用于可以充当遮挡物的静态对象,如建筑物的墙体、山体等。
5. ReflectionProbeStatic
- 描述:用于反射探针优化。标记为 ReflectionProbeStatic 的对象会在烘焙反射探针时被认为是静态的,因此反射效果更加精确。
- 用途:适用于需要高质量反射效果的静态对象,如水面、玻璃、光滑的金属表面等。
6. OffMeshLinkGeneration
- 描述:用于生成离线网格链接。标记为 OffMeshLinkGeneration 的对象会被用来生成离线网格链接,这些链接可以用作AI路径规划的跳跃、攀爬等动态动作。
- 用途:适用于场景中需要AI做特殊移动的静态对象,如悬崖边缘、楼梯等。
7. LightmapStatic
- 描述:用于光照贴图优化。将对象标记为 LightmapStatic 后,Unity 会在光照贴图烘焙时将其纳入考虑,使其接收并投射阴影、反射光等。
- 用途:适用于需要烘焙光照贴图的静态对象,如地形、建筑物、室内装饰等。
8. ContributeGI
- 描述:用于全局光照贡献。标记为 ContributeGI 的对象会被全局光照系统纳入计算,从而影响整个场景的全局光照。
- 用途:适用于希望在全局光照中起作用的静态对象,如地形、建筑物、大型场景物体等。
9. Everything
- 描述:将对象标记为所有类型的静态对象,即它将同时启用所有上述优化选项。
- 用途:适用于希望对某个对象应用所有可能的静态优化的情况。
小结
- StaticEditorFlags 提供了针对不同优化需求的标志,可以根据对象在场景中的角色和用途选择合适的标志进行设置。通过合理使用这些标志,可以显著提高游戏的性能,特别是在涉及大量静态几何体的场景中。
第五章 运行时修改静态属性
你可以在运行时修改GameObject.isStatic属性。不过需要理解的是,虽然这个属性在运行时是可修改的,但它的用途和效果与编辑器中的静态优化不同。
1. 运行时修改 GameObject.isStatic
在运行时,你可以通过脚本来动态改变一个游戏对象的静态状态,例如:
void Start()
{
// 将该GameObject设置为静态
gameObject.isStatic = true;
// 也可以在运行时将其设置为非静态
gameObject.isStatic = false;
}
2. 运行时修改的影响
虽然你可以在运行时更改 isStatic 属性,但这不会触发编辑器中的静态优化(如静态批处理、全局光照预计算等),因为这些优化操作通常是在构建或预处理阶段完成的,并且在运行时不再重新计算。
运行时的效果:
- 碰撞检测:Unity可以基于该属性来优化物理系统的处理。将对象设置为静态后,Unity可以假设该对象不会在运行时移动,从而减少碰撞检测的开销。
- 渲染优化:在某些情况下,渲染系统可能会基于该属性做出不同的优化决策,比如剔除(Culling)或灯光照射计算。
然而,这些优化在运行时动态修改 isStatic 属性后,不一定会立刻重新计算或应用。例如,静态批处理通常不会因为在运行时改变 isStatic 而重新触发。
3. 编辑器中的静态优化
在编辑器中使用 StaticEditorFlags 来设置对象为静态时,Unity会在场景加载或构建时应用一系列静态优化,例如:
- 静态批处理:减少绘制调用。
- 光照贴图:预计算光照并烘焙到贴图中。
- 导航网格:计算路径时不考虑静态对象的变化。
这些优化通常在运行时不会因为修改 isStatic 而重新计算。
4. 小结
运行时修改 isStatic:可以改变对象的静态状态,可能会影响物理引擎和渲染系统的某些优化,但不会触发静态批处理或光照贴图等复杂的静态优化重新计算。
编辑器中的静态优化:通过 StaticEditorFlags 设置,在构建或场景加载时应用广泛的静态优化,但这些设置通常在运行时不再动态改变。
如果你希望在编辑器中批量设置对象为静态,以便在构建时获得最大化的性能优化,建议使用 GameObjectUtility.SetStaticEditorFlags 或在Inspector中手动设置静态标志。
第六章 本文总结
在Unity中将对象设置为静态,主要目的是为了提高性能。我们可以通过GameObject.isStatic和GameObjectUtility.SetStaticEditorFlags方法在编辑器中设置对象的静态属性。当然在运行时通过GameObject.isStatic修改静态属性,虽然不会触发静态优化,但可以在碰撞检测等场景中减少一部分开销。
第七章 学以致用
练习题 1:理论题
问题: 在Unity中,如何使用脚本动态设置一个对象的StaticEditorFlags?简要描述你需要完成的步骤,并编写一个脚本,使得名为"Environment"的GameObject对象被标记为静态物体,并且只开启光照贴图(LightmapStatic)和遮挡物(OccluderStatic)的静态标志。
目标:
- 测试你对如何在脚本中使用GameObjectUtility.SetStaticEditorFlags的理解。
- 测试你对静态标志的设置及其用途的掌握程度。
练习题 2:实践题
问题: 创建一个Unity场景,场景中包含两个GameObject对象,分别是House和Tree。使用Unity编辑器手动设置它们的静态属性:
- House应该被设置为BatchingStatic和LightmapStatic。
- Tree应该被设置为OccludeeStatic和NavigationStatic。
接着,编写一个编辑器脚本,通过脚本验证这些对象的静态属性是否被正确设置。如果设置正确,在控制台输出相应的消息。
目标:
- 测试你对在Unity编辑器中手动设置静态属性的熟悉程度。
- 测试你对如何在脚本中获取和验证对象静态属性的理解与实践能力。
第八章 参考资料
- https://docs.unity3d.com/Manual/StaticObjects.html
- https://docs.unity3d.com/ScriptReference/GameObject-isStatic.html
- https://docs.unity3d.com/ScriptReference/GameObjectUtility.GetStaticEditorFlags.html
- https://discussions.unity.com/t/how-to-set-use-staticeditorflags-cant-seem-to-set-them-from-script/476886/13