Unity3D技术之Unity 3.5 至 4.0 升级指南
游戏对象的活动状态
Unity 4.0 对游戏对象活动状态的处理方式进行了改变。游戏对象的活动状态现在会被子游戏对象继承,即任何不活动的游戏对象会使其子对象也变为不活动状态。我们认为这种新行为比旧行为更合理,且即将发布的新 GUI 系统严重依赖此 4.0 新行为,若无此改变则难以实现。不过,这就需要对现有工程进行一些调整,以使其能与 Unity 4.0 的新行为兼容。以下是具体的更改内容:
旧行为
- 活动状态定义:游戏对象的活动状态由其
.active属性定义,无论该对象是否实际活动。 - 查询与设置:可以查询该属性,也能通过勾选
.active属性进行设置。 - 对子对象的影响:游戏对象的活动状态不会影响子游戏对象的活动状态。若要激活或停用游戏对象及其所有子对象,需调用
GameObject.SetActiveRecursively方法。 - 状态丢失问题:调用
SetActiveRecursively时,子游戏对象的先前活动状态会丢失。当使用SetActiveRecursively停用然后激活游戏对象及其所有子对象时,调用该方法前不活动的子游戏对象会变为活动状态。若要恢复到原有状态,需手动记录子游戏对象的活动状态。 - 预设情况:预设不包含任何活动状态,实例化后会始终保持活动状态。
新行为
- 活动状态定义:游戏对象的活动状态由自身及其所有父游戏对象的
.activeSelf属性共同定义。只有当游戏对象自身及其所有父对象的.activeSelf属性都设为true时,该游戏对象才处于活动状态;只要有一个设为false,则为不活动状态。 - 查询方式:可使用
.activeInHierarchy属性进行查询。 - 状态更改方法:游戏对象的
.activeSelf状态可通过调用GameObject.SetActive方法进行更改。对先前活动的游戏对象调用SetActive(false)可停用该游戏对象及其所有子对象;当所有父对象为活动状态时,对先前不活动的游戏对象调用SetActive(true)可激活该游戏对象,且其子对象也会随之激活。 - 优势体现:这意味着不再需要
SetActiveRecursively方法,活动状态会从父对象继承。同时,调用SetActive停用和激活部分层级视图时,子游戏对象的先前活动状态将得以保留。此外,预设可包含活动状态,并在预设实例化时保留。
示例
假设有 A、B、C 三个游戏对象,其中 B 和 C 是 A 的子对象。
- 调用
C.SetActive(false)停用 C。此时,A.activeInHierarchy == true,B.activeInHierarchy == true,C.activeInHierarchy == false;同样,A.activeSelf == true,B.activeSelf == true,C.activeSelf == false。 - 调用
A.SetActive(false)停用父对象 A。此时,A.activeInHierarchy == false,B.activeInHierarchy == false,C.activeInHierarchy == false;A.activeSelf == false,B.activeSelf == true,C.activeSelf == false。 - 调用
A.SetActive(true)再次激活父对象 A。此时,A.activeInHierarchy == true,B.activeInHierarchy == true,C.activeInHierarchy == false;A.activeSelf == true,B.activeSelf == true,C.activeSelf == false。
编辑器的新活动状态
在 Unity 4.0 编辑器中,为了将这些更改可视化,任何因自身或父对象的 .activeSelf 属性设置为 false 而不活动的游戏对象,在层级视图中会显示为灰色,在检视器中的图标也为灰色。游戏对象自身的 .activeSelf 属性通过其活动复选框体现,可独立于父对象状态进行切换,但只有当所有父对象都处于活动状态时,该游戏对象才会被激活。
对现有工程的影响
为了让用户了解代码中可能受影响的部分,GameObject.active 属性和 GameObject.SetActiveRecursively() 函数已被弃用,但它们仍然可以正常使用。具体如下:
GameObject.active值的读取与GameObject.activeInHierarchy的读取相同,设置GameObject.active与调用GameObject.SetActive()相同。- 调用
GameObject.SetActiveRecursively()与对游戏对象及其所有子对象调用GameObject.SetActive()相同。
在 3.5 版本中,现有场景通过将场景中任何游戏对象的 selfActive 属性设置为先前的 active 属性进行导入。因此,只要不依赖拥有不活动游戏对象的活动子对象(在 Unity 4.0 中不再可行),从 Unity 旧版本中导入的任何工程应能正常工作(虽然会出现编译器警告)。若工程依赖于拥有不活动游戏对象的活动子对象,则必须更改 Unity 4.0 中可用的模型逻辑。
改为资源处理管道
在开发 4.0 版本时,我们对资源导入管道进行了一些重要的内部更改,以提高性能、内存使用率和确定性。在大多数情况下,这些更改对用户没有影响,但有一种情况除外:资源中的对象无法持续到导入管道的最终端,任何先前导入的资源版本将被完全替代。
这意味着两方面的问题:一是在后置处理过程中,用户无法获得资源中对象的正确引用;二是如果使用先前导入版本的资源引用,在后置处理过程中进行存储修改时,这些修改会丢失。
因为不持续而丢失的引用示例
以下是一个小示例:
public class ModelPostprocessor : AssetPostprocessor
{
public void OnPostprocessModel(GameObject go)
{
PrefabUtility.CreatePrefab("Prefabs/" + go.name, go);
}
}
在 Unity 3.5 中,这段代码可以正常工作,但在 Unity 4.0 中,已导入的模型会被完全替代,因此更改先前导入的网格的名称将不会产生任何影响。解决方案是通过其他方式找到网格并更改其名称。在 Unity 4.0 中,应只更改对后置处理程序的给定输入,而不依赖先前导入的相同资源版本。
网格读取/写入 (Read/Write) 选项
Unity 4.0 在网格 (Mesh) 导入设置中新增了“读取/写入已启用 (Read/Write Enabled)” 选项。关闭该选项可以节省内存,因为 Unity 可在游戏中卸载网格数据的副本。
然而,如果在运行时以非统一缩放对网格进行缩放或实例化,则必须在其导入设置中启用“读取/写入已启用 (Read/Write Enabled)”。这是因为非统一缩放要求将网格数据保存在内存中。一般来说,构建时会检测到这一点,但在运行时如果网格被缩放或实例化,则必须手动设置该选项,否则在游戏构建中网格将无法正确呈现。
网格优化
Unity 4.0 中的模型导入器 (Model Importer) 在网格优化方面有了显著提升。“网格优化 (Mesh Optimization)” 复选框在 Unity 4.0 的模型导入器中现已默认启用,它会对网格 (Mesh) 中的顶点进行重新排序,以获得最佳性能。
但工程中可能存在一些后置处理代码或效果依赖于网格的顶点顺序,更改顶点顺序后可能会破坏这些代码和效果。如果出现这种情况,应关闭网格 (Mesh) 导入器中的“网格优化 (Mesh Optimization)” 选项。特别是在使用 SkinnedCloth 组件时,网格优化会导致顶点权重贴图发生变化。因此,在从 3.5 版本导入的工程中使用 SkinnedCloth 时,需要关闭“网格优化 (Mesh Optimization)” 选项以避免影响网格,或者重新配置顶点权重,以匹配新的顶点顺序。
移动输入
对于 Unity 4.0 的移动传感器,在不同平台之间的输入对齐有了更好的表现,这意味着开发者只需编写较少的代码就可以在移动平台上处理典型输入。现在,加速和陀螺输入会按照屏幕方向进行处理,iOS 和安卓 (Android) 平台的处理方法相同。
为了利用这一更改,开发者在处理加速和陀螺输入时,应重构输入代码并移除平台和屏幕方向特定的代码。在 iOS 中,将 Input.compensateSensors 设置为 false 仍可获得旧的行为。