转载自:http://www.cnphp6.com/archives/62868?utm_source=tuicool&utm_medium=referral
Unity游戏开发中,单个Scene解决所有问题似乎不可能,那么多个Scene之间的切换是必然存在。如果仅仅是切换,似乎什么都好说,但是在场景比较大的时候不想让玩家等待加载或者说场景与场景之间想通过一些画面、动画表现出一些让玩家期待的东西,大家就要去认真考虑。这篇文章主要介绍两种增加切换中如何播放画面或者动画等等,提高玩家的浸入感,当然你也可以做成无缝的场景。
1 操作基础函数
LoadLevel | Loads the level by its name or index. 加载场景,加载之前你需要把场景在Build Settings添加,场景加载后,场景中的激活物体会调用MonoBehavior:OnLevelWasLoaded().使用该方法的时候,之前场景中的物体将会被直接删除。 |
LoadLevelAdditive | Loads a level additively. 该方法不销毁当前场景中的物体,新场景中的物体将会被添加进来,这个方法在一些连续加载的场景中非常有用哈。 |
LoadLevelAdditiveAsync | Loads the level additively and asynchronously in the background. 在后台异步记载场景 unity会在后台线程中加载所有的场景资源,这就允许你在加载新场景过程中播放一个进度条或者通过异步加载为玩家创造一个流程的世界 该方法会返回AsyncOperation结构,结构中isDone表示是否完成,progress给出当前的播放进度。注意的是在编辑器中后台线程的性能要低于游戏中。 |
LoadLevelAsync | Loads the level asynchronously in the background. 在后台异步记载场景 unity会在后台线程中加载所有的场景资源,这就允许你在加载新场景过程中播放一个进度条或者通过异步加载为玩家创造一个流程的世界 该方法会返回AsyncOperation结构,结构中isDone表示是否完成,progress给出当前的播放进度。注意的是在编辑器中后台线程的性能要低于游戏中。 |
是否销毁
LoadLevel和LoadLevelAsync,在加载完成后之后将会立刻销毁原先场景中的物体,而LoadLevelAdditive和LoadLevelAdditiveAsync加载后将会保留原先的场景中的物体,这种方式可以实现无缝融合的场景,只需要你在适当的位置加载后面的场景,不过你还是要考虑资源的释放问题。
同步还是异步
public class ExampleClass : MonoBehaviour
{
IEnumerator Start()
{ AsyncOperation async = Application.LoadLevelAsync("MyBigLevel"); yield return async; Debug.Log("Loading complete"); } }
2 切换画面实现方法
场景切换中实现画面或者动画的方式有很多种,我现在看到的主要有两种,这里大致做下两种方式:
第一种方式:增加新场景
增加新场景的方式可以参考雨松的博客:Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条(三十一),增加新场景的优点是增加了很多的灵活性,你在中间过渡场景中增加视频播放、图片或者是GUI,缺点就是如果你想在实现通用性,就需要顶层做一些数据辅助,比如加载前的场景中设置好切换时播放的图片到顶层数据管理模块中,在浸入切换场景后在获取这些数据,根据这些数据来切换播放的动画等等。
左图为测试工程中三个场景,Pre为当前场景,Middle为切换场景,Next是需要加载的场景。右图为middle场景中的存在的物体,挂有实现脚本。案例逻辑:Pre场景中直接进入middle场景,在middle场景中预先加载Next场景,确保新场景加载完之后不会立刻显示Next的场景,而是等待用户的触摸或者点击操作
具体的操作过程:
(1)在Pre场景中使用同步加载方法直接进入middle场景,因为Middle场景非常小,所以这个过程会非常快
Application.LoadLevel("Middle");
(2)Middle场景的逻辑
public class NewBehaviourScript2 : MonoBehaviour { public Texture BackImage = null; private AsyncOperation async = null; void Start () { //此物体在下一个场景中不会被销毁 DontDestroyOnLoad(this); //开始加载场景 StartCoroutine("LoadScene"); } //异步加载 IEnumerator LoadScene() { async = Application.LoadLevelAsync("Next"); yield return async; Debug.Log("Complete!"); } void OnGUI() { //切换场景中的背景,可以是图片或者动画,或者~~ GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), BackImage); //加载过程中显示进度,也可以是进度条 if (async != null && async.isDone == false) { GUIStyle style = new GUIStyle(); style.fontSize = 48; GUI.Label(new Rect(0, 200, Screen.width, 20), async.progress.ToString("F2"), style); } //在加载结束后,弹出是否下个场景,模拟手游中"触摸任意位置进入游戏" if (async != null && async.isDone == true) { if (GUI.Button(new Rect(100, 100, 100, 100), new GUIContent("跳起进入下一个场景"))) { Destroy(this); } } } }
A 场景中添加一个占满全屏的背景图,当然也可以使用动画、视频等,场景加载后马上开始加载下一个场景(异步),根据异步加载进度进行进度条、动画等等进度控制
B 注意:MiddleObj物体被设置为DontDestroyOnLoad(this),也就是在新的场景中不销毁,即使在新场景加载后也会占满全屏的背景仍然存在,直到物体被销毁
C MiddleObj物体销毁的方式有两种,一种是手动方式,就像下面代码中使用的,点击Middle场景中的按钮销毁自己 ,另外一种自动方式是在新场景(Next场景)中的MonoBehavior:OnLevelWasLoaded()函数中销毁该物体。
void OnLevelWasLoaded(int level) { GameObject obj = GameObject.Find("MiddleObj"); if (obj != null) { GameObject.Destroy(obj); } }
雨松在实现新加载场景中先将场景中的所有物体隐藏,场景切换之后再慢慢的显示出来,而不是通过DontDestroyOnLoad的方式,也是一种办法哈。如果想要把这种方式设计为通用模式,可以增加一个数据管理器,在加载前提前设置好需要的图片、动画资源等内容,OK ,就可以在多个场合使用啦。
第二种方式使用prefab
这种方式和第一种方式原理上其实类似,只不过不使用新的场景,而是在Pre场景中增加类似于上面Middle场景中的MiddleObj物体中的逻辑,并且设置为DontDestroyOnLoad。这种方式中可以参考的就是SceneManager场景管理插件,SceneManager的使用可以参考:
http://blog.csdn.net/onerain88/article/details/12303511
http://www.haogongju.net/art/2499058
具体使用我就不多说了哈,咱先看看其实现的关键原理:SceneManger使用异步加载新场景,进入加载之后,就会根据选择使用不同的屏幕效果,并且这个效果是不会在新场景中消失的,直到需要的效果目标达到,就会显出出先场景。SceneManger提供了一些列的屏幕效果Prefab以及场景管理方式,很不错的插件哈。
测试代码下载:http://pan.baidu.com/s/1o67dAiA 密码:l976 ,内带有SceneManager 插件(花15大洋买的)