GMS2官方教程系列3/8——制造敌人
本节教程是讲解如何生成敌人和以及销毁实例等操作,不过在最开始我们先介绍一下如何设置游戏的帧数,游戏的帧数决定了游戏画面的流畅程度,一般每秒的帧数(FPS)与画面流畅性成正比,当然消耗的资源也会同步增加,但是在性能足够的情况下,用些许性能换取更流畅的画面是值得的,因为这样可以给用户以更好的游戏体验。
在右侧的资源树底部有个“Option”的选项,双击其中的“Main”就会弹出一个设置框,其中可以看到有个“Game Frames Per Second”就是游戏的FPS设置项了。
另外在“Main”选项下方还有个“Windows”的选项,该选项是指针对“Windows”平台编译发布游戏时可以根据平台特性进行一些定制化设置(如果发布iOS、Android或MacOS等平台也会有对应的选项),现在我们要在“Windows”的平台设置中选择“Graphics”图像设置项,在其中勾选“Interpolate colours between pixels”的选项,这个选项可以提供像素差值平滑过渡的功能,使得图形边缘过度更自然,对非像素风格的游戏而言是很有必要的。
在完成以上的设置之后可以运行一下游戏看看效果,应该能发现移动起来更加“丝般顺滑”了:)然后我们开始正式学习敌人的生成,首先还是要创建一个敌人的精灵(并关联图片)和对象(关联刚创建的敌人精灵),这个操作应该已经非常熟练,就不再配图赘述了。(也可以从视频中观看)然后我们在敌人的对象中添加一个“Create”事件,然后在事件中添加以下内容:
hp = 5 ;
spd = 1.5 ;
这里的“hp”就是我们给敌人定义的血量,稍后如果HP降为0敌人就会被摧毁,下面的“spd”则是我们给敌人定义的运动速度,可能一些朋友会觉得奇怪,为什么不用上节课中的“speed”属性,那个“speed”是对象自带的一个速度属性,在这我们不想修改其自身属性,因此单独定义了一个变量,然后会在后续的过程中使用这个值来使敌人进行运动,也可以通过这个学习新的移动的方法。然后我们再给敌人对象添加一个“step”事件,让它真正动起来的代码就要在这里写了:
简单的说,我们希望敌人能追着玩家控制的飞船跑,那我们的代码里就需要判断玩家控制的飞船是否存在,如果存在,就要朝着它所在的方向前进了。
if (instance_exits(obj_player))
{
move_towards_point(obj_player.x,obj_player.y,spd);
}
image_angle = direction;
其中"instance_exits"是内置的一个函数,用来判断某个对象是否存在,因此在其参数括号内填入的就是我们需要判断的对象“obj_player",这意味着只有飞船存在时才会执行下方花括号中的命令。而花括号中也是一个新的内置函数“move_towards_point(obj_player.x,obj_player.y,spd)”,这个函数是命令当前对象朝着某个点前进的,它需要三个参数,前两个是目标点所处的X、Y值,最后一个参数则是这个前进动作的速度,在这里,我们命令它以玩家控制的飞船对象的x、y坐标点为目标,以之前定义的spd速度前进运动。最后我们还设置了敌人对象的图片角度(使其等于运动方向),这样才能保证敌人是一直面朝玩家控制的飞船进行运动。因此我们可以这样来理解上述代码
假如(后面括号中的对象存在(obj_player对应的对象)){ 当前对象朝着括号中的前两个参数作为xy坐标的点以第三个参数为速度前进(obj_player的x坐标,obj_player的y坐标,spd对应的值)}当前对象的图片角度 = 当前对象的运动方向
在完成以上内容后可以去场景编辑器中,用第一节课中学到的在场景中添加玩家对象的方法,同样添加一些敌人的对象,然后就可以启动游戏运行测试一下了,此时你会发现你放置的敌人都会朝着你控制的飞船前进,直到与飞船重叠为止。
但是还有几个问题,首先敌人碰到我们的飞船也没有发生任何事情,敌人好好的,我们的飞船也好好的,另外我们对着敌人倾泻子弹,但是子弹纷纷穿过了敌人,没有造成任何伤害。这是因为我们还没有创建相关的事件和逻辑代码。我们之前给敌人在创建时就设置了一个叫hp的属性,并且hp默认为5,这个hp就是敌人的血量,我们希望当hp变成0时敌人会自动摧毁,那我们首先要在敌人的step事件中添加一段新的代码,就是要时刻检测自身的hp属性,如果变成0就要自我摧毁,只需要一行代码即可实现:
if (hp <= 0) instance_destroy();
就是当hp小于或等于0时,运行一个内置的函数“instance_destroy",这就是把当前的实例销毁的意思。那我们要怎么让敌人的hp降低呢,我们希望当子弹打到敌人身上时这个hp降低,所以我们需要判断子弹有没有撞到敌人,这里我们需要在子弹对象中添加一个碰撞事件,而碰撞的对象则是敌人对象:
创建好碰撞事件后,我们在对应的代码编辑器中填入以下内容:
with(other){
hp = hp - 1 ;
}
instance_destroy() ;
有了之前学到的知识为基础的话,大概能看出来大概是让敌人的血量hp持续降低,然后把自己也销毁了的意思。那这个“with”又是什么呢?因为我们目前在编辑的是子弹对象,而hp则是敌人对象的属性,一般来说在子弹对象中是不能直接去操作编辑其他对象的属性(变量)的,而这个with就是把二者关联起来的方法,一旦关联后就可以跨对象编辑属性了。在with后的参数可以填写具体的对象名称“obj_enemy”,也可以直接填写一个通用的“other”,视不同的情况和个人习惯而定。然后最后的instance_destroy则是让子弹在接触到敌人以后自我销毁,因为我们不希望子弹在击中敌人以后还继续穿透飞行,要让子弹有一种被消耗掉的感觉,因此在接触到敌人以后就让其自己销毁掉了。那完成以上两段代码的编辑以后,我们就知道当子弹接触到敌人以后,敌人的hp会减1,而当敌人的hp降低到0时,就会自动消灭,这就基本上满足了我们对子弹击中敌人这个事件的预期。然后我们要编写当敌人接触到玩家控制的飞船以后的事件代码:
如上图,因为是敌人接触玩家控制的飞船,因此在敌人的事件列表中新增了一个碰撞事件,碰撞对象为“obj_player”,然后其中的代码只有一行:
game_restart() ;
这个代码的意思就是英文字面意思,游戏重新开始,这意味着当敌人接触到玩家控制的飞船以后游戏就会重新开始,这样就可以让玩家重新挑战这个游戏了,虽然很简单,但是基本能实现一个小的闭环了。如果完全按照以上的内容进行编写,你就可以启动游戏进行测试了,你可以让飞船定住不动等着敌人冲过来观察游戏是否重新开始,也可以控制飞船发射子弹去攻击敌人,看敌人是否按照代码所写的被摧毁了,而子弹是不是也在击中敌人以后原地消失了。在以上一切正常运行的情况下,还是会觉得有一点不对劲,因为好像子弹还没打到敌人的身上,子弹就消失了,然后敌人跟我们的飞船还有一点距离的时候游戏就已经重置了,这是什么原因呢?这里就要再给大家介绍一个概念——碰撞遮罩,打开编辑器窗口,选择我们的飞船精灵双击可以看到以下内容:
上面图像上的黑色半透明就是碰撞遮罩,碰撞遮罩就是一个物体的碰撞检测范围,而目前这个区域明显是比飞船本体要大的,所以才会发现还没有完全接触但碰撞事件已经触发的情况,这个遮罩的范围可以用鼠标选中四个角来进行手动调节,或在左侧进行调整,在左侧的“Type”下拉选项中还可以调整遮罩的形状,包括圆形、菱形甚至直接匹配图片的边缘(但是会消耗较多的性能),然后我们依次把飞船、子弹和敌人的遮罩调整至下图的样子:
然后我们再次启动游戏进行测试,此时就不再会有之前那种看画面还没接触到但是碰撞时间已经被触发的现象了。
至此,本节内容已经结束,这一段介绍了如何自定义变量,并通过变量去控制实例销毁,以及如何重置游戏让玩家可以在失败之后反复挑战,另外还有如何通过控制碰撞遮罩来使得碰撞事件看起来更加自然合理。