大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是:blog.csdn.net/qinyuanpei。终于在各种无语的论文作业中解脱了,所以马上抓紧时间来这里更新博客。博主本来计划在Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪这篇文章后再写一篇《Unity3D游戏开发之从《魂斗罗》游戏说起(下)》,不过目前博主的项目进度有些缓慢,所以想等项目稳定下来以后再和大家分享。作为大家等待博主更新博客的回报,我们今天来说一说Unity3D中的游戏场景异步加载。那么什么是游戏场景异步加载呢?异步加载就是系统在加载数据(如地图数据、模型数据、玩家数据等)时可以执行其它的任务,其实就是我们熟悉的多线程啦。相反地,在单线程中,我们通常需要等待数据加载完成以后才能继续执行任务。我们可以从《仙剑奇侠传》、《古剑奇谭》等游戏中找到这样的例子。比如《仙剑奇侠传四》中的五毒兽勇气读条效果:
再比如《古剑奇谭》中融入了游戏特色介绍的读条效果:
从上面的这些例子我们可以看出,在游戏设计中通过合理的游戏场景加载方式,减少玩家等待的时间,对于一个游戏来说是设计的时候需要好好考虑的一个问题。在Web设计中一个好的Web设计通常要考虑要将用户等待页面响应的时间降到最低,这样才不会导致用户流量的缺失。在一个注重用户体验的时代,无论设计什么样的产品,带给用户最为极致的体验才是最重要的。我们注意到这两款游戏都采用了异步加载的方式,因为在加载游戏的过程中,系统在执行界面GUI元素的绘制工作。那么,好了,在了解了异步加载的概念和异步加载的原理以后,我们就来实现今天的内容,如图是我们今天要来实现的效果图,我们希望实现在游戏加载的过程中,可以在进度条下方交替显示不同的提示内容:
那么具体怎么实现呢?在Unity3D中提供了一个LoadLevelAsync()方法,该方法可以用于异步加载场景资源。该方法返回一个 AsyncOperation类型的返回值,该返回值有两个重要的属性:1、 isDone:可以查询是否加载完成;2、progress:一个介于0-1之间的值,可以表示加载的进度
好了,通过这两个属性和这个方法,我们就可以实现今天的内容啦,一起开始吧!
首先创建一个场景,我们这里使用系统默认的GUI系统,如图:
这个界面就是我们今天的加载界面,在界面加载的过程中,我们可以在背景下方显示游戏中的Tips和游戏进度。通常情况下,我们需要三个界面来完成一个完整的界面加载效果。第一个界面称为触发界面,主要负责响应用户的触发操作。第二个就是我们今天这里在讲的加载界面,主要负责读条及UI刷新。第三个界面就是我们最终要呈现给玩家的界面。这比如在《仙剑奇侠传》游戏中,当玩家靠近房屋时会在门口显示一个箭头的效果,这样可以提示玩家即将进入场景加载的触发区域,当玩家进入这个范围时,就会触发加载界面的显示,这样我们就能看到五毒兽勇气的读条效果了。当读条结束后,会进入第三个界面,这是我们最终要显示给玩家的界面。。好了,这就是异步加载游戏场景的一个完整的过程了。我们下面来编写脚本实现今天的功能,对于加载过程这个界面,我们主要用到我们的LoadLevelAsync()方法:
using UnityEngine; using System.Collections; public class Loading : MonoBehaviour { //异步对象 private AsyncOperation mAsyn; //绑定Tip的GUIText private GUIText mTip; //Tip集合,实际开发中需要从外部文件中读取 private string[] mTips=new string[] { "异步加载过程中你可以浏览游戏攻略", "异步加载过程中你可以查看当前进度", "异步加载过程中你可以判断是否加载完成", "博主不理解轩6的读条为什么那样慢", "难道是因为DOOM不懂Unity3D", }; //更新Tip的时间 private const float UpdateTime=2.0F; //上一次更细的时间 private float lastTime=0.0F; void Start () { mTip=GameObject.Find("GUITips").GetComponent<GUIText>(); StartCoroutine("Load"); } IEnumerator Load() { mAsyn=Application.LoadLevelAsync("Main"); yield return mAsyn; } void Update () { //如果场景没有加载完则显示Tip if(mAsyn!= null && !mAsyn.isDone) { //如果达到了更新的时间 if(Time.time-lastTime>=UpdateTime) { lastTime=Time.time; mTip.guiText.text=mTips[Random.Range(0,5)]+"("+(float)mAsyn.progress * 100+"%"+")"; } }else { Application.LoadLevel("Main"); } } }
这里的代码很好理解,如果加载完毕了就加载最终界面,否则就随机显示Tips和加载的进度。这里博主为了节省时间,使用的是前一篇文章中的场景,如图:
而对于触发加载事件的界面,我们只需要使用普通的加载方式就可以了:
using UnityEngine; using System.Collections; public class TriggerToLoad : MonoBehaviour { void OnGUI() { if(GUILayout.Button("加载Loading场景",GUILayout.Height(30))) { Application.LoadLevel("Scene2"); } } }显然,这是一个当用户点击按钮时加载场景的方法,所以不再做太多的解释。由于这个方法只支持Pro版本的Unity3D,因此博主的免费版Unity3D注定是无法运行了,所以这里无法给大家做程序演示,希望大家谅解啊。在Unity3D异步加载场景的问题中,有两个常见的问题:
1、使用 AsyncOperation的progress获取加载进度,加载进度不变
2、无法捕捉到场景加载完成的事件:使用DontDestroyOnLoad可以解决
好了,今天的内容就是这样了,博主的能力有限,能够理解到的只有这么多啦,不管怎么样,还是希望能和大家一起成长啊,呵呵。
转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/30836567
IEnumerator LoadSceneAsync(string levelName) { ///当前进度 int currentProgress = 0; ///目标进度 int targetProgress = 0; AsyncOperation op = Application.LoadLevelAsync(levelName); //加载前面的0.9 op.allowSceneActivation = false; while(op.progress < 0.9f) { //计算目标进度 targetProgress = (int)op.progress * 100; //在当前进度和目标进度间进行平滑 while(currentProgress < targetProgress) { ++currentProgress; //在这里处理加载相关的逻辑 yield return new WaitForEndOfFrame(); } } //加载剩下的0.1 targetProgress = 100; //在当前进度和目标进度间进行平滑 while(currentProgress < targetProgress) { ++currentProgress; //在这里处理加载相关的逻辑 yield return new WaitForEndOfFrame(); } op.allowSceneActivation = true; }
好了,这块儿基本没什么好说的了,再次提醒不读代码直接抄代码的各位,我本身不是什么大神,我写的代码一直很渣,我从来没打算给大家一个开箱即用的编程体验,因为编程的过程本身就是迷惑的,需要你一步步地去探索。如果你只是把我写的代码复制到编辑器中,却不知道该怎么用这个脚本,脚本中哪里有什么错误,我觉得我真的没有必要在这样无私地奉献下去了,我自己从开源社区中收益良多,所以我自己愿意将自己了解到的、探索过的东西分享给大家,可是这并不是各位懒惰的理由对吗?许多新手觉得我对新手的要求特别严苛,大概新手们都觉得如果我会了我还会来问你?可问题是新手不懂得该怎么提问甚至不懂得怎样思考,我做本科毕业论文的时候虽然面对的是自己不喜欢的专业,可我比班里每一个同学都认真,即使到了论文答辩的时候大家的结果不会由太大的差别,可是我挺喜欢这个过程的,因为它让我知道了怎么样去思考,毫无疑问,向别人求教是要做功课的,如果不懂得怎么提问,可以经常上上知乎,如果你希望的是我直接把代码写好给你、甚至帮你调试代码寻找Bug,抱歉我没有这样的义务。有个读者从上周开始就向我频繁地索要一篇文章中没有给出源代码的一个IO类的代码,我自认我已经将这个类的作用解释的特别清楚了,它没有做特别复杂的事情,仅仅是对System.IO这个命名空间的简单封装,我觉得任何一个有编程基础的人都应该熟悉IO部分。可是将近一周过去了,这个读者还是没有自己实现这部分功能,试问就算我把代码给你了,你难道就会认认真真地看完然后从中有所收获?呵呵,我真不想为某些人的懒惰而愤怒了。我愿意分享技术是我的自由,我不愿提供完整的代码同样是我的自由,强迫是没有任何意义的,除了让你变得更加无知。好了,这次更新就是这样子!