[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果

      大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是:blog.csdn.net/qinyuanpei。终于在各种无语的论文作业中解脱了,所以马上抓紧时间来这里更新博客。博主本来计划在Unity3D游戏开发之从《魂斗罗》游戏说起(上)——目标追踪这篇文章后再写一篇《Unity3D游戏开发之从《魂斗罗》游戏说起(下)》,不过目前博主的项目进度有些缓慢,所以想等项目稳定下来以后再和大家分享。作为大家等待博主更新博客的回报,我们今天来说一说Unity3D中的游戏场景异步加载。那么什么是游戏场景异步加载呢?异步加载就是系统在加载数据(如地图数据、模型数据、玩家数据等)时可以执行其它的任务,其实就是我们熟悉的多线程啦。相反地,在单线程中,我们通常需要等待数据加载完成以后才能继续执行任务。我们可以从《仙剑奇侠传》、《古剑奇谭》等游戏中找到这样的例子。比如《仙剑奇侠传四》中的五毒兽勇气读条效果:

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第1张图片

再比如《古剑奇谭》中融入了游戏特色介绍的读条效果:

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第2张图片

 从上面的这些例子我们可以看出,在游戏设计中通过合理的游戏场景加载方式,减少玩家等待的时间,对于一个游戏来说是设计的时候需要好好考虑的一个问题。在Web设计中一个好的Web设计通常要考虑要将用户等待页面响应的时间降到最低,这样才不会导致用户流量的缺失。在一个注重用户体验的时代,无论设计什么样的产品,带给用户最为极致的体验才是最重要的。我们注意到这两款游戏都采用了异步加载的方式,因为在加载游戏的过程中,系统在执行界面GUI元素的绘制工作。那么,好了,在了解了异步加载的概念和异步加载的原理以后,我们就来实现今天的内容,如图是我们今天要来实现的效果图,我们希望实现在游戏加载的过程中,可以在进度条下方交替显示不同的提示内容:

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第3张图片


      那么具体怎么实现呢?在Unity3D中提供了一个LoadLevelAsync()方法,该方法可以用于异步加载场景资源。该方法返回一个 AsyncOperation类型的返回值,该返回值有两个重要的属性:1、 isDone:可以查询是否加载完成;2、progress:一个介于0-1之间的值,可以表示加载的进度

    好了,通过这两个属性和这个方法,我们就可以实现今天的内容啦,一起开始吧!

    首先创建一个场景,我们这里使用系统默认的GUI系统,如图:


[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第4张图片


     这个界面就是我们今天的加载界面,在界面加载的过程中,我们可以在背景下方显示游戏中的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和加载的进度。这里博主为了节省时间,使用的是前一篇文章中的场景,如图:

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第5张图片

      而对于触发加载事件的界面,我们只需要使用普通的加载方式就可以了:

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可以解决
   

    好了,今天的内容就是这样了,博主的能力有限,能够理解到的只有这么多啦,不管怎么样,还是希望能和大家一起成长啊,呵呵。

   

  参考文章:

    Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条
    利用Loading界面异步过渡游戏场景


  每日箴言:勇者不是没有眼泪的人,而是含着眼泪微笑奔跑的人。


[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第6张图片

   喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。

   转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/30836567


  2015年11月30日更新:

       有朋友问起这篇文章中的效果无法实现,这是因为这篇文章写作时间较长,而且当时博主所使用的Unity3D版本是免费版本,所以异步加载这块的内容并没有经过深度地测试。既然大家问起这个问题,就在这里补充着说一下吧,这个问题在网上已经有了比较好的解决方案。首先,我们要明确的一点是异步加载和同步加载不同,异步加载是非阻塞的,所以在这个过程中我们可以使用一个进度条或者加载界面来过渡场景的加载过程。其次,Unity3D的异步加载有一个Bug是当进度加载到0.9的时候就停止了加载,并且会在这里卡顿一下,因此我们不能以1.0作为加载完成的一个判断标志。Unity3D中的AsyncOperation是我们在处理场景异步加载时的一个类,这个类中封装了场景加载相关的信息,如场景是否加载完成的isDone和当前场景加载的进度progress。这里,我们想重点说说allowSceneActivation这个属性,这个属性可以决定当场景加载到0.9的时候是否由Unity3D自动完成剩余部分内容的加载,默认这个属性为true,所以当我们加载到0.9的时候,场景会稍微地卡顿一下。那么,好了,我们怎么解决这个问题呢?这里分成两部分来处理,即前面的0.9由Unity3D来进行加载,而剩下的0.1则由我们完成手动加载。我们一起来看下面的代码:

 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;
    }


在这段代码中,我们实际上是利用allowSceneActivation属性控制了场景加载的过程,前面的0.9是正常的加载过程,而剩下0.1却是为了掩人耳目而补充的加载过程。这里我们还使用了一个平滑的过程,这是因为如果直接以目标进度来设置加载相关的逻辑如进度条,那么表现出来的情况就是加载的进度不均匀,因为加载的进度是跳跃性的。可是实际上加载的进度能百分之百的控制为均匀吗?答案是不行的,因为加载过程中的资源本身就不是均匀的啊,所以本质上进度条就是游戏资源加载过程中的遮羞布,它本身就带有很强的欺骗感,比如进度都已经记载了99%了,结果最后的1%却加载了好长好长的时间,这科学吗?这显然不科学啊。这是博主重新找到原始项目后重新编写代码实现的场景加载进度条效果:

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果_第7张图片
       

       好了,这块儿基本没什么好说的了,再次提醒不读代码直接抄代码的各位,我本身不是什么大神,我写的代码一直很渣,我从来没打算给大家一个开箱即用的编程体验,因为编程的过程本身就是迷惑的,需要你一步步地去探索。如果你只是把我写的代码复制到编辑器中,却不知道该怎么用这个脚本,脚本中哪里有什么错误,我觉得我真的没有必要在这样无私地奉献下去了,我自己从开源社区中收益良多,所以我自己愿意将自己了解到的、探索过的东西分享给大家,可是这并不是各位懒惰的理由对吗?许多新手觉得我对新手的要求特别严苛,大概新手们都觉得如果我会了我还会来问你?可问题是新手不懂得该怎么提问甚至不懂得怎样思考,我做本科毕业论文的时候虽然面对的是自己不喜欢的专业,可我比班里每一个同学都认真,即使到了论文答辩的时候大家的结果不会由太大的差别,可是我挺喜欢这个过程的,因为它让我知道了怎么样去思考,毫无疑问,向别人求教是要做功课的,如果不懂得怎么提问,可以经常上上知乎,如果你希望的是我直接把代码写好给你、甚至帮你调试代码寻找Bug,抱歉我没有这样的义务。有个读者从上周开始就向我频繁地索要一篇文章中没有给出源代码的一个IO类的代码,我自认我已经将这个类的作用解释的特别清楚了,它没有做特别复杂的事情,仅仅是对System.IO这个命名空间的简单封装,我觉得任何一个有编程基础的人都应该熟悉IO部分。可是将近一周过去了,这个读者还是没有自己实现这部分功能,试问就算我把代码给你了,你难道就会认认真真地看完然后从中有所收获?呵呵,我真不想为某些人的懒惰而愤怒了。我愿意分享技术是我的自由,我不愿提供完整的代码同样是我的自由,强迫是没有任何意义的,除了让你变得更加无知。好了,这次更新就是这样子!




你可能感兴趣的:(游戏,异步,unity3d,游戏开发,游戏设计)