Unity5 官方教程笔记(2D Rogue Like)01 —— Game Manager

笔者作为一个unity新手,很希望能尽快用unity做出自己的游戏。为了督促自己抓紧学习,同时也可以熟悉unity使用过程中的一些技巧,并发现、探讨一些问题,便决定通过撰写博客来达到以上目的,所以就将有以下这个系列的几篇博文。

       以笔者目前掌握的技能来看,制作2d游戏是一个比较合适的选择,于是便选择了官方教程中的2d rogue like作为自己用来学习的项目。如果各位看官在阅读途中发现了什么问题,还希望及时指出。

       附上教程地址:http://unity3d.com/cn/learn/tutorials/projects/2d-roguelike-tutorial

==========以下是正文=========================================

每一个游戏在运行过程中,都会有很多的数值需要被计算以及记录,因此为了方便管理,可以通过一个“游戏管理器”来统一管理这些信息。而这次笔者将尽力介绍一下在这个教程中的“游戏管理器”都做了什么,是如何做的。

       首先是GameManager的代码结构(有部分和官方略有区别,不过整体是一样的)

      

可以看出,GameManager提供的主要功能有:

  1. 控制游戏进程,其中包括控制关卡的开始延迟、每一回合间的延迟、控制是否允许玩家操作等、判断游戏是否结束等;
  2. 初始化游戏信息,其中包括了生成地图等;
  3. 记录游戏当中的一些数值,包括玩家的生命值、当前进行到的关卡级别等;
  4. 持有敌人等部分对象的引用;
  5. 控制游戏的显示状态、比如是应该显示地图还是显示正在加载等辅助信息;
  6. 控制游戏的UI。

同时由于“游戏管理器”控制的都是整个游戏中一些共有的信息,所以我们不会同时需要两个游戏管理器,因此游戏管理器将是一个单例对象,所以它还需要自己管理一些跟单例操作有关的信息。

       接下来说明一下每个方法的具体作用。

       当cs脚本被加载之后,将首先执行Awake()方法,具体内容如下:

      

由于Awake()方法将在所有其它方法之前被执行,因此一般用这个方法来进行一些必要变量的初始化等等。而在本例中,方法首先判断了GameManager的单例是否有值,如果没有则将其指向当前对象;如果有值则将之前值的游戏对象(gameObject)销毁;接下来用DontDestoryInLoad()方法将当前的GameManager标记为再下一个场景创建时不会被销毁,通过这个方法可以保留在上一关中记录的数值;接下来再为enemies(敌方单位)数组以及boardScript(地图管理器)分配内存;最后执行InitGame()方法初始化当前关的内容。

       在这些里面值得注意的有,首先由于unity在每加载新的一关时,会把上一关创建的对象全部销毁,但是由于我们在GameManager中存放了很多数据需要在下一关使用,所以不能让自己被直接销毁,所以需要用DontDestoryOnLoad()方法来保持自己。而BoardManager是用来管理地图生成并且存放地图数据的类,具体内容将会单独作为一篇文章(估计就下一篇吧┗|*`0′*|┛)。

       接下来看InitGame()方法。

      

首先将doingSetup标记修改为true,通过修改这个标记,并在Update函数中进行判断可以使关卡的逻辑更新暂停,避免在游戏初始化的过程中就已经开始运行的情况;接下来是更新ui,使得关卡在初始化的过程中会被“第x天”的信息给覆盖,而不是直接显示下一关的地图,这样如果在需要初始化的内容比较多的情况下还是非常有用的,并且可以给人一种比较明显的关卡过渡的感觉;接下来便是清除管理器之前持有的敌方单位的引用以及最后生成地图。

       在这里值得注意的是用到了Invoke()方法。Unity中的Invoke()方法是用来在调用该方法的指定时间长度之后调用另一个方法的。该方法有两个参数,第一个是需要被调用的方法名,而第二个则是等待的时间长度。在本例里,则是要在“levelStartDelay”秒后调用“HideLevelImage”方法。内容如下:

   

即:将覆盖在关卡地图上的信息去掉,并将doingSetup标记置为false,这样在Update函数中便会开始执行游戏的逻辑。可以看到其中调用了GameObject.SetActive()方法,该方法的内容是将某个GameObject标记为Active或者Inactive,而Unity只会显示被标记为Active的对象。需要注意的是,如果某个对象是另一个GameObject的子对象,且其父级对象未被标记为Active的话,即便其自身被标记为Active也依旧不会在画面中出现,但是可以通过GameObject.activeSelf属性获取到其是否被激活。

OnLevelWasLoaded()方法用来在场景切换结束后进行一些工作,也是Unity提供的API之一。

   

在这里的作用就是把关卡级数+1, 之后再初始化游戏。值得注意的是,笔者查到好像在开始游戏的第一个场景里(点击play按钮之后进入的第一个场景被加载后),该方法并不会被触发,只有在之后的场景被加载完成后才会被触发,因此不用担心没有进行第一关就直接进入了第二关。(即便会被触发,解决方法也是很简单的)。

至于Start()方法,笔者之前是打算在这里面初始化一些游戏的数据的,但是由于他的执行顺序在Awake()和OnLevelWasLoaded()之后,所以会导致很多在前两个方法里面需要的对象还没有初始化便需要被调用,因此便把那些工作放在了Awake()里面,不过这种方式是否算是正确的使用方式可能还需要进一步探索,既然是初学就先以实现功能为主吧(实力偷懒(^o^)/~)。

   

GameOver()方法是用来终止整个游戏的,内容如下:

   

主要作用是隐藏游戏的地图界面并显示最后的分数。可以看到其中将GameManager的enabled属性设定为了false;这样将会停止GameManager的Update()执行,通过这种方式将使整个游戏的逻辑停止更新。

AddEnemyToList()方法,

   

很简单,由创建敌方单位的脚本调用,作用就是让GameManager持有某个敌方单位的引用便于操作。

Update()方法也很简单,如果同时满足“非玩家回合、敌方单位不在移动中、游戏没有在进行设定操作”这三个条件,则开启协程MoveEnemies()。

   

具体协程是什么、是如何实现的,本篇暂且不详细讲解(其实让我讲我也说不清楚),总之只需要知道现在它就是用来使用单线程的方式达到多线程的效果就行了,具体在本例中的MoveEnemies()方法,就是让地方单位可以根据玩家的位置进行移动,同时又不会因为计算使整个游戏在某段时间完全失去响应。

   

在Unity中,通过协程方式执行的方法主要有两个特征:

  1. 返回值是IEnumerator类型且没有参数   (在这里要更正一下,有参数的方法也是可以用作协程的)
  2. 方法中会出现yield关键字

具体携程可以用来干的事情也许会有很多种,不过比较广泛的用法是进行一些需要延时的操作但是在延时的过程中又不至于让程序失去响应。由于Unity的逻辑处理是单线程的,所以如果不通过这种方法来实现延时操作的话,可能会导致线程卡在某个地方导致体验下降。

在方法中,首先标记了enemiesMoving为true,这样就不会使Update()方法再次开启一个协程导致处理混乱。通过yeild return new WaitForSecond()方法,则可以使方法执行到这一句之后暂停一段时间,之后继续从这个地方开始执行。而当在等待的过程中,Unity会优先处理一些其它的工作,待满足回归条件(在这里也就是等待的时间结束)之后,就会继续执行下面的语句。当所有的敌方单位移动结束后,方法将会把playersTurn标记为true,将enemiesMoving标记为false,使系统进入玩家回合。

以上就是整个GameManager的结构。由于第一次写此类文章,结构有些散乱且由于部分内容打算再下一篇文章继续说明,所以导致可能有些方法的内容没有介绍清楚,还请多多包涵。

原文地址:https://www.cnblogs.com/nmsuper86/p/5601778.html