作为一个有意于游戏行业的cs科班生,大学已经快三年过去却还没有学习多少相关知识,实在有些惭愧。今年的疫情导致寒假超级加倍,那也就没有理由继续怠惰了,正好有一个相关的课设可以选择做游戏,就来记录一下自己团队的学习,以及分享交流一些心得。
关于之前一些开发经历的话,除去上大学之前用war3的worldeditor做的地图,之前是用krkr2引擎搭配国内的一个叫做nvlmaker的框架做了一款galgame(当然美术资源都是来自网络,因此有版权问题没有在任何平台发布),剧情原创,改编自本人尝试过写的轻小说,但是剧情没有做完,原因是太过于偏向工作量,并没有功能实现上的困难,于是把前几章的内容做完就直接打包了(会在讲完后放出工程文件链接,仅供学习)。这边推荐想要做galgame的去使用上述框架,官网教程还是相对详细的(另外,截止发文的两个月前的最新版本框架似乎有点逻辑上的问题,可以尝试去下之前的版本)。
游戏效果如下:
那么回到正题,我为什么选择先去熟悉unity3d,原因无非两点,第一,unity3d使用广泛,市面上的大量游戏都是用这个引擎作为基础制作的,如炉石传说、王者荣耀等,第二,上手难度没有那么高,这边是指入门难度,实际上要精通还是非常困难的。游戏制作语言为c#,即使之前并没有相关的学习经历,但是如果有c++的基础,在unity里还是基本上没有障碍的。
首先,跑酷游戏是相对简单的一个游戏模式,这也是我选择这个作为入门项目的原因。
在开始之前,除了unity的gameobject、component等基本概念外,你必须理解unity的主线程运作,因为代码是放在线程里运行的,必须严格遵循生命周期。下图是unity官网给出的unity3d的主线程。我们来逐一分析一下。
Reset函数,这个方法是在只编辑器模式下,脚本附着时执行,不需要运行,我觉得严格意义上不算生命周期的一部分,且本例中没有使用。
Awake函数,有这个方法的脚本在挂载到gameobject上后,物体激活时执行且仅执行一次该方法,无论该脚本是否为激活状态。一般会在这个函数里面进行一些全局变量的初始化,比如本例中在此设置帧率等。
OnEnable函数,在物体或组件被激活时调用,包括初始为激活状态的物体。
Start函数,在Awake后,第一次update前调用,可以用来设置用于Update函数的初始变量等。
FixedUpdate函数,按照固定时间间隔执行的函数,不受timescale(可以理解为时间流速变量)的影响。
关于yield的用法,实际上是unity中协程的返回方式,这个后面例子里讲。
OnTriggerXXX函数,该方法只有在物体有collider属性并且勾选Istrigger选项,物体发送生碰撞时才会调用。
OnCollisionXXX函数,该方法只有在物体有collider属性并且不勾选Istrigger选项,物体发送生碰撞时才会调用。
由于本例使用CharacterController组件作为角色控制,并且本例没有其他需要碰撞判定的地方,故未使用这两个方法,实际上就如线程所示,OnTriggerXXX函数判定的优先级更高,且会使得该物体不受碰撞的物理引擎控制(会穿过),而不勾选IsTriggerXXX而使用OnCollisionXXX函数会判定碰撞,应当根据情况选择合适组合用作事件触发器。
具体函数如下:
OnMouseXXX函数,该函数是鼠标操作相关函数,原理是射线,调用射线返回的第一个元素,包括OnMouseDown、OnMouseUp、OnMouseOver函数,见名知意。
Update函数,按照每一帧执行一次的函数,由于设备及场景渲染情况影响,帧率会有波动,因此更新频率也会波动,如果要做规律的移动等操作,建议乘上deltatime(这一帧与上一帧的时间间隔)或者在楼上函数里面进行。本函数受timescale的影响,当timescale值为0时不执行,这边是游戏逻辑的重中之重,包括人物控制、场景生成、逻辑判定等重要操作都在这里进行。
LateUpdate函数,要理解这个函数的作用首先需要明白,所有的激活脚本中的生命周期的同一函数都是放在一起运行的,所有的Start函数运行完了再运行所有的Fixedupdate函数,然后是所有的Update。那么,问题来了,如果有些逻辑功能对两个Gameobject的update的顺序有要求,那么都放在Update里面显然会出问题,Lateupdate就是来解决这个问题的,这个函数会在所有的Update函数运行完成之后运行,本例中,该函数用于摄像头的跟随,保证摄像头在物体移动之后移动。
渲染相关函数未使用到,另外由于ui相对简单,本例单纯采用控制Canvas下子物体属性方式完成GUI。
OnApplicationPause函数,在程序因退回桌面、切换网络等暂停时调用,本例未使用。
OnDisable函数,和Onable函数也一样,都是修改了SetActive后立刻执行的方法。
OnDestory函数,和OnDisable函数的区别是,OnDisable函数可以配合SetActive执行多次,而OnDestory函数是在脚本或者GameObject被销毁后调用,调用完后脚本生命周期结束。
OnApplicationQuit函数,在程序退出时执行,可以做一些数据保存工作。
PS:如果要做联机的游戏,那么最好不要利用这个函数来进行断线判断,因为如果进程直接被杀掉就不会执行这个函数,客户端断线情况应由服务端判断。
主线程的基础内容大概就是这些,那么准备工作还有一个很重要的方面,那就是美术资源。一款跑酷类型游戏所需要的最少的资源包括:
1.人物模型及其动画
2.道路
3.各类障碍物
4.奖励道具
5.ui资源
6.音效音频资源
如果是美术大佬可以自行进行绘制ui,模型方面可以采用门槛相对较低的blender建模,然后绑骨制作人物动画,包括站立、走路、奔跑、下滑和跳跃,然后后面用动画状态机和混合树合成流畅的模型动画,这方面就算是初学者也很容易入门,后面会讲到。
如果没有这个能力,并且出于个人学习需要的话,可以选择一些资源共享网站,或者直接将市面上一些unity3d的游戏进行解包提取资源,但这样做的游戏不要以任何方式发布。网站自行寻找,解包方面可以将游戏包在电脑里解压后,用assetstudio进行提取,非unity开发的游戏解包后如果有一些图片声音资源加密可以考虑用idapro静态分析反汇编等方式获取解密算法,或者直接用010editor看看文件头找找线索,这边不做赘述。
本例人物、道路、障碍物、奖励道具模型均来自网络,ui和音频文件来自我所玩的一款手游。
上述资源文件链接(.zip):链接
pc端链接(.exe):链接
安卓端链接(.apk):链接
链接失效可以联系补档,qq 2902560683 Echidna
包含代码的工程文件会在后面几篇更完后放出来。
2020.4.10