GameMaker Studio 2中的对象编辑器
对象编辑器(Object Editor)
对象(Object)是我们在游戏中使用的以及用来做各种操作的一切的基础组件。大部分情况下对象都会有一个对应的精灵(Sprite),以方便我们在游戏场景中可以看到对象本身,但也有些情况下它们会被放到幕后而不显示出来,只是作为控制器来处理一些特殊的事件或用于计时之类的操作,对象可以被赋予一些行为操作,也可以与其他对象发生交互事件,事实上你在游戏中看到的大部分内容都是基于不同对象之间的交互所产生的结果。而我们说“基于”是因为我们其实并没有吧对象直接放进游戏场景中,游戏中你看到的是对象的实例,一个实例可以理解成是对象资源的副本(或克隆体)。你需要牢记这一点,因为实例和对象本身是不一样的,它们各自拥有自己的一套函数来作用于它们本身。为了搞明白这一点,请仔细思考一下GameMaker Studio 2制作的游戏。所有的角色、怪物、球、墙壁等等都只是你在资源树中所创建的对象。你可以在“场景编辑器”中把对象放进对应的游戏场景,以对象为模版创建出实例。实例会根据编辑器中书写的代码实现相应的调整,比如缩放尺寸或着色,或其它基于对象模版的其它属性。所以当我们说有些东西会影响或改变一个实例时,我们其实是说一个对象在这个场景内的一个实例会发生变化。只有那一个实例会变化,其余实例则不受影响。但是,如果我们对对象本身进行操作和修改,那意味着我们修改了资源树中的对象本身,这种操作会影响所有由这个对象所产生的实例。因此,对象其实就是实例的模版,而实例才真正参与到我们的场景中来用于制作游戏。要在游戏中创建对象,你只需要在对象资源文件夹上点击鼠标右键然后选择“创建(Create)”选项即可,这会打开下面的窗口:
详情(Details)
这这个窗口中你可以给对象起个名字,名称中不能包含空格或非法字符(仅允许字母和数字以及下划线“_”符号),并且应该简单明了容易记住,以提高之后使用过程中的辨识度,毕竟你可能会创建大量的对象。比如很多人喜欢用前缀或后缀来区分不同的资源类型,好比“obj_Ship”和“Ship_obj”之类。接下来你可以为对象选择一个用于显示的精灵,在精灵选项上点击鼠标左键会弹出一个素材选择器(Asset Explorer),里面会显示出资源树中所有的精灵组件:
当你选好指定给对象的精灵后,点击按钮就可以直接修改精灵各项属性,如果你没有事先准备给该对象实用的精灵,也可以点击图标来直接新建一个精灵并直接指定给这个对象。你还可以单击编辑图像的按钮来启动图像编辑器直接编辑精灵的图像内容。
碰撞蒙版(Collision Mask)
在精灵区域下方有一个按钮可以指定对象的碰撞蒙版。通常情况下对象的碰撞蒙版默认是与其精灵的保持一致的,但在一些特殊情况下,你可能希望设置一个不同的蒙版形状。比如说你做一个上下游戏时可能角色的精灵用了矩形碰撞蒙版,但是使用圆形的碰撞蒙版会更加平滑,这种情况下你可以单击“碰撞蒙版(Collision Mask)”然后选择一个新的精灵资源,此时这个对象的实例会表现为之前指定的精灵资源的造型,但是基于新设置的这个精灵资源的碰撞蒙版来检测碰撞事件。
属性(Properties)
这里涵盖了对象的四项基本属性
可见性(Visible)
这个属性代表了当游戏场景启动时,该实例是否可见,大多数情况下都是默认可见的,但有的时候也会用的不可见资源——比如,你可以用不可见的对象设置路径点来控制怪物的移动,或做一些计时操作。隐形的对象仍然可以正常执行各事件中的代码,如果设置了碰撞蒙版则也能正常与其他对象实例发生碰撞触发事件……你只是看不到它们,它们不尽兴任何绘制的操作。默认情况下这个选项是选中的。
固体(Solid)
当你勾选了固体的选项后,就意味着这个对象会在触发碰撞事件之前就完成碰撞行为。这其实是个很简单功能,它所做的只是在实例发生碰撞事件的瞬间,在执行任何碰撞事件中的代码之前就把它放回碰撞发生前所处的位置上,因此需要谨慎使用。
持久化(Persistent)
持久化——一个持久化的对象是不会随着游戏场景的变化而消失的,它会一直存在并跟着跳转到新的游戏场景中。如果你不明确的销毁或通过代码要求它执行销毁操作,它永远不会消失。这意味着一旦在场景中使用一个持久化对象,它就可以贯穿全程在游戏中进行使用,而且持续执行属性中各类事件里的代码,“创建(Create)”事件除外,因为这个事件只有当对象实例首次生成时才触发,变换游戏场景并不会处罚这个事件。
使用物理特性(Uses Physics)
当你勾选“使用物理特性(Uses Physics)的选项时,GameMaker Studio 2 就会使这个对象具备物理特性。勾选这个选项后会打开一个新的窗口,在该窗口中你可以定义该对象的物理属性,其生成的实例都会继承这些属性。关于这块的内容在后文中“物理特性”一节会详细说明。
选项(Option)
选项部分可以定义对象的各项属性,以及游戏场景内生成的实例的行为方式。在这里你可以添加事件——即游戏代码的“构建模块”,设置对象是否具有父类,如果需要制作带物理属性的游戏也可以在此进行配置。
事件(Event)
那么事件到底是什么?基本而言,就是在游戏过程中,你通过代码实现的触发某些操作的触发条件。在GameMaker Studio 2中,从一个游戏场景开始到结束,会不停循环监测游戏步骤进程(步骤进程是一个单位的游戏时间,单位属性可在游戏场景的速度设置中控制)来判断是否有事件被触发,然后你就可以把你的代码或拖拽代码块(DnD Action)放到这些事件中运行,让我们用一个包含事件和代码的例子来说明以上内容:
你在我们的示例中可以看到列表中有很多响应事件,但当你首次创建对象时这个列表是空白的,你必须清楚自己需要哪些事件,以及这些事件被触发时实例该作出怎样的反应。要往这列表中添加事件,你需要点击事件列表底部的”添加事件(Add Event)“按钮,会弹出下面这个窗口:
这是一个对象可以响应的基础事件列表,其中有一些事件还有更细化的子事件,比如你选择按键事件后可以进一步选择具体响应哪一个按键。一旦选择了响应事件,会在右侧创建出一个代码窗口,你可以在此编辑代码,给对象赋予针对该事件所需要作出的响应操作。在任何事件上单击鼠标右键都可以看到以下选项:
- 添加事件(Add Event)——从事件列表添加新事件
- 剪切/复制/粘贴事件(Cut/Copy/Paste Event)——剪切、复制(到粘贴板)或者粘贴所选事件,可以使用标准的快捷键:Ctrl + X ,Ctrl + C ,Ctrl + V
- 复刻事件(Duplicate Event)——复制所选事件内所有的内容并直接塞进你指定的新事件中
- 更换事件(Change Event)——这会提示你重新选择一次触发事件,选择以后触发事件会被替换成新的事件
- 转换为拖放/代码模式(Change to Drag&Drop/GML)——把当前事件的编程模式在拖放模式或GML代码模式之间进行切换
删除事件(Delete Event)——删除当前事件,还可以用Shift和鼠标左键一次选择多个事件同时删除
你还要注意,你可以重新给事件命名,或至少写个备注,备注会在编辑器旁显示,备注方式如下,把这行内容写到编辑器第一行即可:
/// @description Your text here (这里写备注)
比如你可以这样命名一个计时事件:
/// @description This is the AI Fight alarm
然后你会看到事件编辑器里是这样显示的:
请注意,以上的事件只是简要描述了工作原理,所有可用事件的详细信息需要去对象事件列表详细查看。
父类(Parent)
在使用GameMaker Studio 2操作对象时,你可以设置父/子类的层级关系。点击对象编辑器中“父类(Parent)”按钮就可以打开以下窗口,其中可以选择其它对象并赋予层级关系
因此,在游戏项目中所有的对象都可以分配一个父对象,这有什么用呢?当一个对象拥有自己的父对象时,它可以共享父对象的所有代码、事件以及行为。这种共享称为“继承”,拥有父类的对象则称为“子对象(子类)”。子类不仅共享父类的代码,而且当你在父类上对代码进行查看和修改操作后子类将自动同步这些变化,这样可以节省大量的时间和精力。
如果觉得上面说的太复杂,你可以换一种理解方式,父类就是把一些拥有相同属性、特征的对象聚合起来,把这些共有的特性集中在一起,又不影响其独有的属性特征,如果还是不太理解,来看看下面这个例子…
如下图所示,假如你有一个“玩家”的对象和四种不同的“敌人”的对象。现在如果玩家对象与任何一个敌人对象发生接触时就会死亡,通常情况下你需要给玩家对象写四个不同的碰撞事件来处理跟四种不同的敌人的碰撞事件。但是如果我们给所有的敌人创建同一个父类对象,那么我们就可以给玩家对象与这个父对象写一个碰撞事件,那么当玩家对象与任意一个敌人对象发生接触时都会沿用父对象中的碰撞事件来触发玩家死亡事件。很方便对不对!
在左侧,我们做了四个单独的碰撞事件,而右侧我们只有一个,因为我们创建了“父类对象”,并且指定给了所有敌人对象为父类,要注意不用把所有的事件和代码都写到父对象里去。
我们再举一个例子,如果你要在一个游戏中创建10种不同样子的对象,但是这10种对象的行为操作以及事件完全一致。此时你可以先创建一个父对象,并在其中写入将所有行为操作相关的事件代码,然后分别创建10个对象并把父对象设置为这个对象,只需设置不同的精灵资源而不需要处理任何的事件代码就行了。当你把这些对象放到游戏场景中创建实例后,你会发现它们看似完全不同却又能执行完全相同的操作事件,因为他们都继承了父类的事件代码。
最后,你还可以混合使用该技巧。让我们用最后一个例子来说明这一点……比如说你想创建两个怪物,一个是上下移动的,另一个则是左右移动的,但你希望这两种怪物拥有完全相同的生命值以及在与玩家发生碰撞事件后能触发完全相同的伤害事件。在这种情况下你会发现两者大部分时间都是一致的,只有少数的一两个行动不同,此时我们可以把一个对象设置为另一个的父对象,同时我们也可以定义子对象的个别事件。这些事件会覆盖父类的相同事件代码,这代表当满足该事件触发条件时,会执行新的事件代码而不是父类中的相关操作。你也可以通过“inherited”函数来强制调用原父类中被覆盖的相关代码。
上图中左侧是一个写有5个事件的父对象,右侧则可以看到对应的子对象,这个子对象中只定义了2个事件,但是他会继承父类的事件,所以他其实拥有与之一致的5个相同的事件。区别在于,子对象中“步骤(Step)”和“绘制(Draw)”这两个事件将覆盖父类中对应的事件。
如图所示,不管你在何处使用父对象,同样会作用于其衍生的对象(子对象),无论你针对任何个特定的实例进行应用都会起效。当你在代码中使用“with()”语句时也会有相同效果,当你针对父对象调用诸如“instance_position”、“instance_number”等函数时,同时也会对其子对象生效。最后,这个功能对变量的修改也是有效的,比如上图的例子中,如果把第一个对象的速度设置为10,那第二个对象的速度也会变成10,因为它是前者的子对象。
在多数情况下,创建一个最基础的基类作为父类是个好习惯,在这个基类中写入所有默认的通用代码,但绝不要在游戏中用它创建实例。比如碰撞检测、引用变量等都可以写进父类而不用在子类中单独设置。你同时应该了解父类也可以拥有自己的父类。当然,绝不可能把A设置成既是B的父类又是B的子类这种循环结构,只能创建层级对象结构,比如A是B的父类,而B是C的父类这种。这种方式对于保持游戏结构十分有效,强烈建议大家学习并使用这种机制。
物理(Physics)
当你首次创建一个新的对象资源时,你会发现这里有一个“使用物理特性(Use Physics)”的选项默认是未勾选的。一旦选中这个选项将彻底改变这个对象实例在游戏场景中的行为,因为这个选项会开启对象的物理属性,这意味着传统的运动和碰撞方法已经全部失效(前提是当前场景也开启了物理属性)。当你选中该选项后,会出现以下物理编辑器窗口:
在开始编辑对象各物理属性值之前,最后首先设置好对象的碰撞形状(Collision Shape)。在普通的碰撞系统中,碰撞事件是基于定义给对象的精灵的碰撞蒙版的,而开启了物理特性以后就不再如此了。我们需要自己重新给对象定义一个“碰撞形状”(我们称这个属性为“装置”),这个形状可以是一个圆形也可以是矩形,或是你自定义的一个多边形,单击修改碰撞形状(Modify Collision Shape)按钮就可以打开下面这个窗口
这看起来很像是路径编辑器,并且功能也大致相同,但是基于你所设定的蒙版不同,也会有如下一些限制:
- 如果你设定了一个圆形蒙版,则只能拉动路径点来放大或缩小这个圆
- 如果你设定的是矩形蒙版,则可以移动四个较重的任意一个,但整个其它三个点会自动调整位置以确保矩形造型不变
- 如果你自定义了一个多边形蒙版,这个蒙版至少需要3个点,至多只能有8个点,并且所有的角必须是向外凸起的(参见下图右侧为错误示例)
一旦你设置好碰撞形状,你就可以通过修改以下的参数来定义你的对象的基本物理属性了:
密度(Density)
密度是指物体单位体积的质量,这可以用于表示该物体在世界中所占据的空间以及质量的多寡。因此气球的密度很小,因为它质量很小却占据了很大的空间,而铅棒的密度很大,因为其质量大而占据空间小。在GameMaker Studio 2的物理世界中,会根据你输入的密度以及形状的面积大小来自动计算质量。这会直接影响对象的惯性以及碰撞后发生的力反馈,因此如果你创建一个很小的对象却赋予很高的密度,就会产生质量(比如铅棒),但如果你定义了一个很大的形状却密度很小,其质量就会很小(比如气球)
弹性(Restitution)
在物理世界中,弹性指的是“物体或系统在经过弹性形变后恢复到原始状态”,而在GameMaker Studio 2中的刚体(rigid bodies)是无法发生形变的,因此实际上弹性指的是刚体到底有多“弹”。这个设置会影响对象与其它对象发生碰撞时“弹跳”的次数及力度,同时会与重力和摩擦力一起作用于实例之上共同起效。
碰撞组(Collision Group)
默认情况下,所有物理对象的碰撞组都是0,这意味着这些对象都能正常进行交互操作,但也表示必须在发生碰撞事件的情况下才能发生交互,否则。但是你也可以制定对象属于某个特定的“碰撞组”。当你给对象指定碰撞组后(比如把三个对象都放进“2”号组),GameMaker Studio 2会把这些对象生成的实例都定义为持续触发碰撞事件,即便这些对象的碰撞事件里是空白的,它们也会遵循它们所处的游戏场景的物理属性。反之,当你给你的对象设定了负数的碰撞组(比如给四个对象分配到“-1”组),就是在告诉GameMaker Studio 2 这些对象永远不会发生碰撞事件,它们之间的所有碰撞事件都会被忽略。
注意:使用碰撞组功能会明显提升物理系统对性能的消耗,只有在绝对必需的情况下才该是用这个功能,并且应当用尽少的组来完成。
线性阻尼(Linear Damping)
阻尼可以降低对象实例的速度,并且不同于摩擦力,因为摩擦力只有在两个实例接触时才起效。而且阻尼比摩擦更容易实现,但要注意的是阻尼并不是摩擦力的替代品,这两个效果可以也应该一并使用。
角阻尼(Angular Damping)
如果你在现实世界中旋转任何物体时,除非有电机或在真空环境中,否则都会随着外力(比如与周围空气的摩擦力)的影响而逐渐变慢。我们可以用这个选项来模拟这种效果,降低实例的旋转速度,如果不设置这个值,所有旋转的实例都会永远旋转下去。
摩擦力(Friction)
摩擦力是不同材料元素之间抵抗相对滑动的力,而在GameMaker Studio 2的物理世界中,则是指两个实例相碰撞以后造成的动量损失。因此当两个实例碰撞时,受摩擦力影响,这个值越大损失的栋梁也越大。最后,这里还有三个额外的开关选项可以用来作用于你的对象,分别是:感应器,启动唤醒和动态化,这些功能的作用如下:
感应器(Sensor)
选中该项以后,GameMaker Studio 2会“忽略”对象的物理属性,但仍可正常返回与周围其它对象的碰撞事件。通过这种方式,你可以在一个场景内创建一个不带有物理属性的实例,但使其仍然可以与其它实例触发碰撞效果,比如使玩家去开一扇门,或是与场景内其它对象进行交互产生行为。
要注意当两个对象初次接触触发碰撞事件以后感应就会自动关闭,这意味着这两个对象后续的重合不会触发碰撞事件中的事件。如果他们在接触后停止了重合行为,之后又重新接触重合会另外单独触发一次碰撞事件。
启动唤醒(Start Awake)
在默认情况下这个选项是选中的,这意味着对象所生成的实例在场景内初始化状态就是唤醒状态。通常情况你会愿意让对象在游戏一启动就处于唤醒状态(即立刻能被场景内的物理特性所影响),但有时候由于一些特殊的需求这可能并不是你所需要的效果。为此,你可以取消这个选框然后直接创建未唤醒的实例,直到你设置的事件触发唤醒它以后,才会受整个场景内的物理特性作用影响。
动态化(Kinematic)
在物理类游戏中,偶尔有一些对象实例你不希望它受重力或其它动态对象的碰撞所影响(平如横版动作游戏中的那些浮在空中的平台)。对于这类对象,如果直接把密度设置为0只意味着这个对象在物理模型中是静态的,但是它也会变的无法与其它对象产生交互作用。但只要针对静态对象选中了这个选项,即便它不会受碰撞或重力的影响,也可以通过变量来使其运动或旋转起来。