当前位置: 首页 > 科技观察

如何使用Unity3D+C#开发炸弹人游戏_1

时间:2023-03-17 11:19:35 科技观察

简介炸弹人游戏是80年代广受欢迎的2D游戏。本文创建了该游戏的基本Unity3D版本。通过这个游戏,您可以实现以下功能:投掷炸弹并将其放置在特定位置激活炸弹处理和玩家的爆炸碰撞处理炸弹爆炸碰撞处理和炸弹的爆炸碰撞游戏结束准备最初的示例项目游戏构建,然后将其放入您指定的位置。然后,用Unity3D打开这个工程,可以看到Assets文件夹里面有很多子文件夹,如图。下面详细介绍各个文件夹的主要功能:AnimationControllers:存放游戏控制器部分,包括逻辑部分。Materials:包含构建每个关卡场景所需的Block材质。模型:存储播放器、关卡和炸弹模型及其相关材料。音乐:存放游戏的音效文件。PhysicsMaterials:存储玩家的物理材质数据,这些数据是用于实现特定物理属性的特殊类型的材料。在本教程中,它用于让玩家轻松无摩擦地穿越关卡。Prefabs:包含炸弹和爆炸的预制数据。Scenes:对应游戏场景数据。Scripts:包含游戏的启动脚本,添加了大量注释,有助于读者阅读源码。音效:包含与炸弹和爆炸效果相关的音效文件。纹理:包含两个玩家的纹理数据。投掷炸弹如果您还没有打开游戏项目,请快速打开它,然后尝试运行这个程序。如果没有其他问题,你会观察到如图所示的情况:你会注意到游戏中的两个玩家可以通过WASD四字键或者键盘上的四个方向键来驱动,使其沿着游戏地图运动。通常,当按下空格键时,红色玩家会在脚下放置炸弹,而其他玩家也可以这样做——只需按下回车键即可。但是,目前我们还没有实现这个功能。为此,您需要先编写放置炸弹的代码。现在,请使用您喜欢的代码编辑器打开脚本文件Player.cs。该脚本处理所有玩家移动和动画逻辑,还包含一个方法DropBomb,当GameObjectbombPrefab关联时用于检测目的。privatevoidDropBomb(){if(bombPrefab){//Checkifbombprefabisassignedfirst}}为了实现炸弹落在玩家下方的效果,在if语句中加入如下代码:Instantiate(bombPrefab,myTransform.position,bombPrefab.transform.rotation);上面的代码会在玩家脚下生成炸弹(随着玩家移动路径的改变,会生成一连串的炸弹)。现在,运行游戏工程,你会观察到如下图所示的效果:目前,效果不错!但是,有一个小问题:炸弹是如何投下的?如果无论在哪里都可以丢炸弹,那么在需要计算爆炸应该发生在哪里的时候就会出现一些问题。接下来,本教程将带您了解如何实现爆炸的所有细节。炸弹定位下一步是确保炸弹在掉落时会粘在它们的位置上,以便炸弹与地板上的网格很好地对齐。由于在我们的设计中网格上的每个图块都是1x1,因此进行此更改相当容易。打开文件Player.cs并像这样编辑Instantiate()函数:Instantiate(bombPrefab,newVector3(Mathf.RoundToInt(myTransform.position.x),bombPrefab.transform.position.y,Mathf.RoundToInt(myTransform.position.z)),bombPrefab.transform.rotation);注意这里函数Mathf.RoundToInt调用了玩家位置的x和z两个参数值,将每个浮点型值转换为整数值,可以达到炸弹很好对齐的效果gridonthefloor:现在,你可以再次启动项目来运行它,你会观察到当投掷炸弹时,炸弹与格子对齐:虽然在地图上投掷炸弹很有趣,但你知道真正的乐趣在于如何让爆炸发生!让我们为其添加更多功能。创建爆炸效果首先,我们需要创建一个新的脚本文件:(1)从Project视图中选择Scripts文件夹;(2)按创建按钮;(3)选择“C#脚本”;(4)将脚本文件命名为Bomb。现在,将Bomb.cs脚本连接到预制炸弹:(1)选择Prefabs文件夹中的GameObjectBomb;(2)点击“添加组件”按钮;(3)在搜索框中输入“炸弹”;(4)选择刚才创建的脚本Bomb.cs;(5)打开这个脚本文件,然后在它的Start()方法中输入如下代码:Invoke("Explode",3f);此方法使用两个参数,第一个是将要调用的方法的名称,第二个是调用此方法时延迟的时间量。在这个例子中,我想实现炸弹在3秒内爆炸的效果。稍后我们将添加此Explode方法的详细信息。现在,只需在Update()方法下方添加此方法占位符形式(当前为空):voidExplode(){}在生成任何GameObjectExplosion之前,您还需要创建一个公共类型GameObjet对象,用于分配预制Explosion。只需在Start()方法上方定义以下代码:publicGameObjectexplosionPrefab;保存这个文件,然后从Prefabs文件夹中选择预制的Bomb,然后将预制的Explosion拖到“ExplosionPrefab”选项后面的空白处。完成后,返回编辑器。现在开始编写更有趣的代码。在Explode()方法中,添加如下一行代码:Instantiate(explosionPrefab,transform.position,Quaternion.identity);//1GetComponent().enabled=false;//2transform.FindChild("Collider").gameObject.SetActive(false);//3Destroy(gameObject,.3f);//4以上代码实现了以下功能:1.在炸弹位置触发爆炸;2.禁用网络渲染器(meshrender),使炸弹不可见;3.禁用碰撞器,允许玩家在爆炸中移动和行走;4.0.3秒后摧毁炸弹;这可确保在删除游戏对象之前触发所有爆炸。现在,保存脚本Bomb.cs,返回编辑器并再次尝试玩游戏。放下一些炸弹,看看它们爆炸的效果如何,请参考下图。设置爆炸音效要创建所需的爆炸效果,您需要创建一个协程。“补充”协程本质上是一个允许您暂停执行并将控制权返回给Unity3D的函数。在稍后的某个时间点,该函数将从它停止的地方恢复执行。人们经常将协程与多线程混淆。实际上,它们是不同的:协程在同一个线程中运行,并且可以在某个中间时间点恢复执行。要了解有关协程及其定义的更多信息,请查阅相关的Unity文档(http://docs.unity3d.com/Manual/Coroutines.html)。现在,回到代码编辑器,修改脚本Bomb.cs,在Explode()函数下添加一个名为CreateExplosions的IEnumberator:在函数Explode()的Instantiate调用和禁用的MeshRender之间的代码:StartCoroutine(CreateExplosions(Vector3.forward));StartCoroutine(CreateExplosions(Vector3.right));StartCoroutine(CreateExplosions(Vector3.back));StartCoroutine(CreateExplosions(Vector3.back));StartCoroutine(CreateExplosions(Vector3.right));。左边));这里的StartCoroutine调用将为游戏场景中的每个方向触发CreateExplosions。现在到了更有趣的时刻。在方法CreateExplosions()中添加以下代码://1for(inti=1;i<3;i++){//2RaycastHithit;//3Physics.Raycast(transform.position+newVector3(0,.5f,0),direction,outhit,i,levelMask);//4if(!hit.collider){Instantiate(explosionPrefab,transform.position+(i*direction),//5explosionPrefab.transform.rotation);//6}else{//7break;}//8yieldreturnnewWaitForSeconds(.05f);}这段代码看起来很复杂,其实很简单。详细解释如下:1.用for循环遍历你要用爆炸覆盖的每一个单位距离。在这种情况下,爆炸会达到两米的距离。2、RaycastHit对象包含了Raycast命中什么物体,命中到什么地方的所有信息,当然也有可能没命中。3、上面代码中非常重要的一行代码是StartCoroutine的调用,实现了从炸弹中心向你经过的方向进行raycast。然后它将结果输出到RaycastHit对象。I参数表示光线传播的距离。最后,代码使用一个名为levelMask的LayerMask来确保射线只检查当前关卡中的块,而忽略玩家和其他碰撞器。4.如果raycast没有命中任何东西,那么这个方块(Block)就是一个空闲方块。5.在光线投射检查的位置创建爆炸。6.Raycast命中块。7.一旦光线投射到一个块上,它就会跳出for循环。这将确保爆炸不会越过墙壁。8.等待0.05秒,然后继续下一个for循环迭代。这将使爆炸在向外扩展时看起来更有说服力。下图是添加上面代码后的动画效果:注意下图中的红线是raycast。它检查炸弹周围的自由空间距离;如果发现碰撞,它就会爆炸。当它碰到方块时,它不会生成任何东西并停止朝那个方向检查。现在,您应该明白为什么炸弹需要连接到网格中每个单元格的中心。如果炸弹可以飞到任何地方,那么在某些边缘情况下,光线投射会击中方块而不会产生任何爆炸,因为它没有正确地水平对齐。添加遮罩层在Bomb代码中也有一个错误:LayerMask不存在。因此,在explosionPrefab变量声明的下方,再添加一行代码,如下所示:publicGameObjectexplosionPrefab;publicLayerMasklevelMask;LayerMask通常使用raycasts技术来选择性地过滤掉特定的图层。在这种情况下,您只需要过滤掉块部分,因此光线投射技术的作用不大。保存炸弹脚本并返回到Unity编辑器。单击右上角的层按钮并选择编辑层...现在,请单击用户层8旁边的文本框并输入“块”。这定义了一个您可以使用的新层。在hierarchy视图中,选择GameObjectBlocks,如下图:将层更改为刚才创建的Blocks层,如下图:当出现“ChangeLayer”对话框时,点击“Yes,changechildren”按钮,应用到地图上所有分散的黄色长方体块。最后,从Prefabs文件夹中选择预制件Bomb,并将遮罩层更改为Blocks。现在,再次运行游戏场景并投下一些炸弹。您会注意到爆炸看起来比以前好多了:很好地分布在长方体之间!恭喜,您已经通过了游戏中最难的编码部分。剩下的就是为游戏添加一些额外的效果。ChainReaction当炸弹爆炸时它会接触到另一个炸弹,这个相邻的炸弹也会爆炸,这会产生更令人惊讶的效果。好消息是,以上效果并不难实现。现在打开脚本文件Bomb.cs,在CreateExplosions()方法下添加一个新的方法OnTriggerEnter:身体。collider参数是other,对应进入trigger的GameObject的collider。在这种情况下,您需要检查碰撞器对象并确保它是爆炸对象时爆炸。首先,你需要知道炸弹是否爆炸。exploded变量需要先声明,所以在levelMask变量声明下面添加如下声明:privateboolexploded=false;然后,在OnTriggerEnter()方法中添加以下代码:if(!exploded&&other.CompareTag("Explosion")){//1&2CancelInvoke("Explode");//2Explode();//3}这段代码做了三件事:1.检查炸弹是否爆炸;2、检查触发对撞机是否有标签Explosion;3.通过投掷炸弹取消已经调用的Explode调用——如果不这样做,炸弹可能会爆炸两次;4.实施爆炸。现在您已经定义了变量,但还没有进行任何更改。最符合逻辑的地方是在Explode()函数中实现这个操作(应该是在禁用组件MeshRenderer之后),代码如下:...GetComponent().enabled=false;exploded=true;。..现在一切就绪,请保存刚才的文件修改,然后再次运行项目。再扔一个炸弹,在它周围不断的扔炸弹,观察效果:最后剩下的就是处理玩家对爆炸的反应,以及游戏结束的处理逻辑。游戏结束处理打开文件Player.cs。目前没有定义变量来指示玩家是死是活;因此,在脚本顶部添加一个布尔变量,如下所示:publiccooldead=false;此变量用于跟踪玩家是否在爆炸后死亡。接下来,在其他变量声明之后添加以下变量声明:publicGlobalStateManagerGlobalManager;请注意,到目前为止,方法OnTriggerEnter()已经能够检查玩家是否被炸弹击中,但当前的实现只是通过控制台窗口输出此消息。因此,现在请在Debug.Log调用中添加如下代码:dead=true;//1GlobalManager.PlayerDied(playerNumber);//2Destroy(gameObject);//3这段代码实现了以下功能:1.修改变量dead,为了追踪玩家的死亡;2.通知全局状态管理器玩家已经死亡;3.销毁玩家对象GameObject。现在,保存文件并返回到Unity编辑器。您需要将GlobalStateManager连接到两个玩家:(1)在层次结构窗口中,选择两个PlayerGameObjects。(2)将全局状态管理器GameObject拖到它们的全局管理器选项中。再次运行游戏场景,确保至少有一名玩家被炸弹击中,如下图所示。每个遇到爆炸的玩家都会立即死亡。然而,到目前为止游戏还不知道谁赢了,因为GlobalStateManager还没有使用它收到的信息。下面我们来讨论一下这个问题。要定义获胜者,请打开文件GlobalStateManager.cs。为了让GlobalStateManager能够跟踪玩家死亡,还需要定义两个变量。在PlayerDied()函数上方添加以下定义:privateintdeadPlayers=0;privateintdeadPlayerNumber=-1;首先,变量deadPlayers将存储死亡玩家的数量。一旦第一个玩家死亡,变量deadPlayerNumber被修改,这个变量也代表了它是哪个玩家的附加信息。准备好上述变量后,现在添加实际的逻辑。在函数PlayerDied()中添加如下代码:deadPlayers++;//1if(deadPlayers==1){//2deadPlayerNumber=playerNumber;//3Invoke("CheckPlayersDeath",.3f);//4}这段代码的作用是:1.增加一个死亡玩家;2.进一步判断这是否是第一个死亡的玩家...3.设置死亡玩家数为第一个死亡的玩家;4.检查是否有其他玩家也死亡,或者0.3秒后只有一些爆炸粉尘但没有死亡。最后一点时间延迟对于绘制支票很重要。如果你马上查看,你可能不会发现任何人都死了,而0.3秒就足以判断是否所有人都死了。输赢判断现在,请在GlobalStateManager脚本中添加一个新方法CheckPlayersDeath:voidCheckPlayersDeath(){//1if(deadPlayers==1){//2if(deadPlayerNumber==1){Debug.Log("Player2isthewinner!");//3}else{Debug.Log("Player1isthewinner!");}//4}else{Debug.Log("Thegameendinadraw!");}}上述条件语句的作用如下:1.只有一名玩家死亡,则确定他为输家;2.玩家1死亡,则玩家2获胜;3.玩家2死亡,则玩家1获胜;4.双方都死了,则平局。现在,再次尝试保存并运行您的项目,请参考下图:请下载项目代码并详细研究以备不时之需!您已经学习了如何使用Unity3D创建基本类型的游戏,如炸弹人。本文使用一些粒子系统来实现炸弹和爆炸效果。更多信息请参考Unity3D官方文档。最后,强烈建议大家做以下增强:(1)启用炸弹推,这样当炸弹靠近你的时候你可以逃跑,把炸弹推给你的对手;(2)限制可以投掷的炸弹数量;(3)增加重新开始游戏的功能;(4)在场景中添加可以随爆炸破坏的方块(Blocks);(5)可以添加一些有趣的装备;(6)添加更多生命,并使用某种方式(7)创建漂亮的UI元素来显示玩家赢得了什么;(8)探索一些增加更多玩家的方法等。