版本管理、运行模式资源管理
多人开发游戏时,需要对项目的版本进行管理,通常会将整个工程上传SVN或者Git。但这并不是很必要,资源在导入Unity的时候,会自动生成很多中间资源,这些资源是不需要上传的。只需要将Assets、ProjectSettings文件夹下的的所有文件以及.meta文件上传即可
.meta文件
.meta文件是Unity自动生成的。每个游戏资源都会有一个对应的.meta文件,它会标记资源在引擎中的一些设置信息。也就是我们在Unity中选中一个资源时,在Inspector中设置的导入信息,资源在用户无感知的情况下被Unity优化了。
当我们在Edit——Project Settings——Version Control中,选择Mode为Visible Version Files,就能在文件资源管理器中看到meta文件用来版本管理。
我们随便打开一张贴图对应的.meta文件,每个.meta文件都会记录guid
这个重要信息。guid
就是用来关联资源与游戏对象的引用的。比如场景中有个模型,引用了这张贴图,那么模型对应的.prefab文件就会引用这个guid
;如果模型不是Prefab,只是保持在场景中,那么场景的.unity文件就会引用这个guid
。所以说,Unity中所有的资源的引用关系都是这样来计算的。
也就是说各种贴图、材质、模型、脚本、文件夹等都有对应的.meta文件,每个.meta文件都记录着对应资源的guid以及一系列导入信息,每个.prefab、.mat、.unity、.asset文件都记录着需要引用资源的guid
再回到这个贴图资源。引擎内可以设置每张贴图的压缩格式、大小和mipmap等,这些信息都会保存在.meta文件中,Unity会根据这些参数重新压缩这个贴图。最终呈现给玩家的是压缩过的。开发者只要设置一些参数,Unity就实现了无感知的优化。
所以.meta文件一定要上传到SVN中,否则团队无法统一对资源的设置
多工程
游戏项目会有多个角色进行参与,如程序员、策划员和美术人员等。分工程很容易,但是合并两个工程就比较麻烦了。在Project视图中选择需要导出的资源,单击鼠标右键——Export Package,可导出一个包。打开新的工程,把刚刚导出的包导入,即可实现两个工程的同步。
同步文件
首先,需要保证美术资源都放在一个最顶层的文件夹下。资源可以按子文件夹归类,但是引用关系只能在最顶层的文件夹中。
同步文件其实就是同步目录,此时使用Export Package命令就不太方便了,可以使用代码直接复制文件夹。因为美术资源都放在同一个顶层文件夹下,资源的依赖只维持在此文件夹内,所以不会引起资源依赖的改变。
新建SynchronizeArtFiles
脚本
1 | using UnityEditor; |
SVN外链
合并资源还有一种更方便的方式,那就是添加SVN外链。客户端的SVN仓库在Asset文件夹下外链一个美术人员的SVN仓库文件夹,那么美术人员在自己的SVN仓库提交资源时,客户端只要更新就行了,不需要考虑资源的合并。
但是如果SVN打版本分支的时候,外链也必须打分支,不然未来美术人员更新了资源,老的客户端SVN仓库就会把新的资源外链下来。
游戏就是使用这种方法来管理客户端和美术资源的,这样确实方便。
运行模式引用资源
运行模式和编辑模式是完全不同的,编辑模式下可以放成千上万的资源,但是运行环境下要的是优化,在此时Unity主要的资源目录只有Resources和StreamingAssets
我们在Project窗口中放入一个贴图,新建一个Material,然后再放入一个Cube Prefab,
- 贴图被Material引用
- Material被Cube Prefab引用
- Cube Prefab被Scene引用
最后我们在Build Settings里面把当前的Scene添加进去,那么根据这条引用链,响应的资源都会被打入游戏包中。
Resources
Resources文件夹下的资源无论是否有引用关系,都会被强制打在游戏包中。
在编辑模式下,Resources文件夹可以有多重子目录,打包后,Unity会自动将它们合并在一起,接着在代码中动态读取这些资源,并且加载它。
我们可以通过Resources.Load<T>(path)
来加载各类游戏资源
如果可以找到,则此方法返回path中的资产,否则返回 null。
请注意,路径不区分大小写,并且不得包含文件扩展名。 Unity 中的所有资产名称和路径都使用正斜杠,因此在路径中使用反斜杠将不起作用。
该path应用于项目的Assets文件夹中名为Resources的任何文件夹。可以使用多个Resources文件夹。如果您有多个Resources文件夹,则不能重复使用相同的资产名称。
例如,一个项目可能有名为Assets / Resources/
和Assets / Guns / Resources/
的 Resources 文件夹。path不需要在字符串中包含 Assets 和 Resources,例如在Assets/Guns/Resources/Shotgun.prefab
加载 GameObject 只需要 Shotgun 作为path。此外,如果Assets / Resources / Guns / Missiles / PlasmaGun.prefab
存在,则可以使用Guns / Missiles / PlasmaGun
作为path字符串来加载它。
不要把不需要运行时加载的资源或者已经废弃的资源放入Resources文件夹
Resources文件夹下的资源尽量不要直接引用在场景中,否则这个资源会被场景和Resources打成两份,也就是打包出来后StreamingAssets文件夹内还有一份重复的资源
删除对象
Object.Destroy(Object obj,float t=0.0f)
和Object.DestroyImmediate(Object obj, bool allowDestroyingAssets = false)
Object.Destroy
会等一帧再彻底移除,因为有可能在这一帧后面还有地方在操作这个对象。
Object.DestroyImmediate
会立即移除,如果此对象在被移除后在别的地方被引用,就会报错。
第一个参数obj可以是Component,使用此方法后GameObject上挂载的对应Component就会移除;也可以是GameObject,使用此方法后GameObject、GameObject挂载的Component、GameObject的子对象都会被移除。第二个参数t表示从调用此方法开始过多久才会执行,单位是秒。
1 | using UnityEngine; |
删除资源
前面我们讲过游戏对象和游戏资源的关系,游戏对象删除了,它引用的资源并没有删除。我们在Profiler工具中,我们选择Memory——把Sample改为Detailed——打开Gather object references。在Asset里面,能查看当前内存中的需要被引用的内容(下图是编辑器模式下的Profiler)。我们可以使用Resources.UnloadAsset()
以及Resources.UnloadUnusedAssets()
方法强制卸载资源。其中Resources.UnloadUnusedAssets()
是异步方法,可以返回AsyncOperation
来检测方法是否完成
1 | using UnityEngine; |
UnityEngine.AsyncOperation - Unity 脚本 API (unity3d.com)
GC
在C#中,可能还会有很多临时对象引用这个游戏资源,这很可能会导致Resources.UnloadUnusedAssets()
无法释放掉。因此,在卸载无用资源前,需要保证C#完成垃圾收集工作,而且有时候进行一遍垃圾回收工作是没用的,最好调用两遍GC()
和Resources.UnloadUnusedAssets()
新建GCImplement
脚本,封装一个内部的GC类,调用两次Resources.UnloadUnusedAssets()
和GC.Collect()
以进行充分的垃圾回收。
1 | using UnityEngine; |