摘要:
Unity(也称Unity3D)是一套包括图形、声音、物理等功能的游戏引擎,提供了一个强大的关卡编辑器,支持大部分主流3D软件格式,使用C#或JavaScript等高级语言实现脚本功能。Bolt是一款专门为Unity而设计的功能强大的可视化编程插件,使得各界人士只要会画流程图,就能编写游戏。本文尝试基于Unity使用Bolt设计一款以抗美援朝为主题的3D游戏,但由于能力与精力所限,只完成了部分FPS游戏逻辑设计。
Unity;Bolt;抗美援朝;游戏
本文尝试基于Unity使用Bolt设计一款以抗美援朝为主题的3D游戏,但由于能力与精力所限,只完成了部分FPS游戏逻辑设计。
本文第二部分将具体介绍角色操控部分逻辑实现,第三部分将具体介绍载具设置部分逻辑实现
在player游戏对象中新建一个流机器,新建一个宏,名称为Player,保存在Macro文件夹中。
进入游戏时,使用Cursor: Set Lock State单元将鼠标隐藏。
我们希望玩家进入游戏后,可以通过移动鼠标控制角色视角。首先将Main Camera放置于Player对象之下。在Player流机器中,首先做一个状态判断,Boolean型Object级变量Entered用于指示玩家状态,若为True则说明角色已进入载具。这里使用一个Branch单元,仅当角色未进入载具时执行后续逻辑。
建立Graph级变量MouseX与MouseY,用于储存当前Camera角度。通过Input: Get Axis单元在每次屏幕刷新时获取鼠标的X、Y移动角度,更新MouseX与MouseY的值。由于我们希望玩家垂直移动鼠标时不能无限移动视角,因此对MouseY设置上下限,确保其在-75-75之内。
此时,进入游戏,移动鼠标时角色视角也会随之改变,且垂直移动鼠标时角色视角存在限制。
角色跳跃之前,首先要对角色的状态进行判断。如果角色踩在地面上,则可以跳跃;如果角色未踩在地面上(跳跃到半空中时),则不能跳跃。这样可以避免玩家进行多重跳。使用Physics: Sphere Cast 单元进行跳跃前的检测,将结果储存在Boolean型Graph级变量Grounded中。
如果角色既未进入载具,且踩在地面上(Entered为False且Grounded为True),则在按下空格时应该使角色进行跳跃。这里使用On Keyboard Input 单元响应键盘输入,当Space即空格键被按下时,对角色施加一个Force,模式设为Impulse。
在游戏中,我们希望玩家使用WASD键控制角色移动。当玩家未进入载具时,通过Input: Get Axis单元获取水平输入轴Horizontal与垂直输入轴Vertical的值,分别与Object级变量Speed相乘,得到角色速度的x、z轴分量。用Get Velocity单元与Get Y单元获取角色速度的y分量,从而构建速度向量,并用其更新角色的Velocity。
此时,玩家进入游戏后应该可以对角色进行一些基本控制,包括转动视角、前后左右移动、跳跃.
我们希望玩家移动时按住Shift键可以进行冲刺。这里我们通过改变Object级变量Speed的值来实现。我们用On Keyboard Input单元响应Shift键的按下与松开,并设置正常情况下移动(没有按住Shift键)时Speed的值为5,而冲刺状态下移动(按住Shift键)时Speed的值为10。
我们希望长按C键时使角色卧倒。卧倒的效果具体体现在两个方面,一是角色视角的高度降低,二是角色移动速度减慢。这里使用On Keyboard Input单元响应C键的按下与松开。首先建立一个Object级变量NormalHeight储存角色的正常高度(Float型,默认值为3).玩家未进入载具时,当C键松开,使用Set Height单元设置角色的高度为正常高度,同时将变量Speed的值设为5;C键按下时,使用Set Height单元设置角色的高度为正常高度的一半,同时将变量Speed的值设为2.
此时,玩家在游戏中可以通过长按C键进行卧倒。
此时,玩家已经可以操纵角色进行移动、冲刺或者卧倒。但角色的手中并没有枪支。在设置枪支之前,我们希望首先在角色视角中心添加一个十字准星。利用PS工具制作一张背景透明大小64x64的白色十字图案,在Player—Main Camera下新建一个Raw Image,并将制作好的图案添加进去,调整图片大小与透明度,完成准星的设置.
准星设置好后,我们开始设置枪械。在player游戏对象下添加一个预构件。该预构件为一把步枪,我们不断调整其位置,在角色视角中模拟角色手持步枪的效果。
此外,我们希望玩家可以通过按下X键在手持枪械/背起枪械状态之间切换。首先建立一个Boolean型Object级变量GunOn,用来指示枪械状态。玩家未进入载具时,每次按下X键时都会对GunOn的值进行切换。如果GunOn为真,则切换为角色手持枪械,使用Game Object: Set Active单元将枪械激活,反之切换为背起枪械,使用Game Object: Set Active单元将枪械禁用
当角色手持枪械时,应该可以通过点击鼠标左键开火射击。这里我们使用RayCast单元进行射击检测,并将最大射程设置为300。玩家按下鼠标左键时,首先在枪口激活开火特效,接着激活RayCast单元向角色正前方发射一束不显示的射线,当射线最先击中的游戏对象的Tag为Enemy时,代表击中敌人,进行相关响应。
在Player对象的流图中,我们可以通过Self单元访问Player对象。为了在Player对象外部方便地对其进行访问,我们可以设置一个Application级变量,将Player对象赋值给该变量。这样,当我们需要在其他游戏对象的流图中对player对象进行访问时,就可以通过访问该变量简介对Player对象进行访问。
设置了一个小彩蛋。首先建立三个Graph级变量Q、E、P,其中Q和E储存着两个预设好的坐标,当玩家按下Q键或者E键时,角色会被传送至对应的坐标位置,并将传送前的位置坐标保存到变量P中。接着按下P键,玩家就会被传送回上一次传送前的位置
当角色与载具足够接近,且视角面向载具时,屏幕中应该出现提示语提醒玩家按下相应的按键以进入载具。在Player—Main Camera下添加一个UI,命名为“EnterText”,将其置于角色视角中央,并显示“按F进入”的字样。
当然,此时进入载具的提示语会与十字准星一样,始终显示在屏幕中央。接下来我们设计相关逻辑,控制提示语的显示与隐藏。
在Player对象的流机器中,我们增添一个RayCast单元。设置射线的方向为角色视角方向,射线最大长度设为15.当射线命中一个游戏对象,检查该对象的Tag,如果Tag为Gun,则说明该对象是一个可进入的载具,这时通过Set Active单元将“EnterText”激活,即在角色视角中心显示提示语,并为“EnterText”对象建立一个Object级变量CurrentGun,将射线命中的载具对象赋值给该变量。当玩家因为移动或旋转视角导致射线未击中一个载具时,将“EnterText”隐藏,并将CurrentGun变量设置为NULL。
首先为EnterText对象添加一个嵌入流机器,当EnterText对象处于激活状态时,玩家可以通过按下F键,在CurrentGun变量储存的载具对象上触发一个“ManEnter”事件。
为载具对象AntiAirGun建立一个状态图,该状态图包含NoManON与ManOn两个状态。初始状态为NoManOn,即角色未进入该载具。当玩家按下F,状态切换为ManOn,并将角色的坐标设置为驾驶室的坐标,将角色视角设置为炮管的方向。当玩家再次按下F,状态重新切换为NoManOn,并将角色坐标设置为进入载具之前的坐标。
当角色进入载具,即AntiAirGun的状态为ManOn时,角色位于载具的驾驶室,此时枪械应该处于收起状态,移动鼠标应该用于控制炮管方向,点击鼠标左键应该用于载具开火。
首先实现枪械的收起,将Player对象的变量GunOn设置为False,并使用Set Active单元将枪械对象Gun隐藏。接着刷新角色的坐标与视角方向,使其始终与炮管保持一致。
我们希望通过移动鼠标控制炮管的方向,这部分逻辑与2.2类似。
当玩家已经进入载具后,应该可以通过点击鼠标左键控制载具开炮。这部分逻辑在Player对象的流图中实现。我们用On Button Input单元捕捉鼠标左键的点击,接着判断角色是否处于载具之中。如果Entered为True即玩家处于载具中,那么在CurrentGun的炮口处激活开火特效,并建立一个炮弹对象Shell,该对象在2s后自动销毁。
炮弹对象拥有一个流机器Shell,当该对象被创建时,获取炮口位置与炮管方向作为为炮弹的初始位置与初始速度方向,并将炮弹速度设置为100.使用On Collision Enter单元对炮弹进行碰撞检测,当炮弹击中某一目标时,在碰撞处激活爆炸特效并销毁炮弹。
由于能力与精力有限,我并没能完成一个完整的以抗美援朝为主题的3D游戏,而只是使用Unity和Bolt实现了一些FPS游戏的基础逻辑。虽然课程已告一段落,但我今后会继续完善这个项目,争取将其制作成一款具有一定可玩性的完整的3D游戏。
十分感谢徐志平老师这个学期以来对我的耐心指导。作为一名医科专业的大一学生,第一次接触Unity这样的游戏引擎,第一次接触Bolt这样基于图的插件,第一次以一个开发者而不是玩家的角度审视一款游戏,感觉十分奇妙、有趣。最开始时选《游戏开发基础》这门课只是出于好奇,本打算听一节课就退课,但没想到一切这么有趣,就坚持了下来。再次感谢徐老师为我打开了一所通往新世界的大门。
希望对我来说结课只是起点而非终点,期待自己在这条路上走的更远。