制作一款打飞机游戏69:编辑器升级
工作内容
首先,会有两个大的改动,我可能会同时处理这两件事情,也可能会一个接一个地处理,然后我会复制所有的结果,因为这两个改动有很多相似之处。
我想深入了解一下Brain Edit和Enemy Edit,在这两种情况下,我都想看到预览效果。也就是说,当我在编辑器中选择一个敌人时,我想看到那个敌人,我想看到敌人的动画,以及一些其他的东西。在Any Edit中也是如此,我想看到动画的播放。
好了,我觉得这个修改已经完成了。我创建了一个窗口,在常规的表格视图上方(我称之为表格视图)。我使用了一个剪辑功能(clip),如果你们忘了这个功能,它可以把所有的绘制限制在屏幕的一个较小区域内,所以屏幕区域之外绘制的任何东西都不会显示出来。
所以我限制了这个区域,这基本上是从精灵文件(sprites did)中复制过来的,然后我做绘制工作。我总是这样做:把应该绘制到屏幕上的当前精灵保存到一个变量中,然后把这个变量绘制到屏幕上,就这样。然后,我释放了剪辑区域,为屏幕的下半部分创建了一个新的剪辑区域,然后使用相机功能将整个表格进一步向下偏移,这样我就不需要修改表格绘制代码了。
这里有一些很酷的功能,我挺喜欢的。首先,我可以改变速度,按W和S键,我可以尝试不同的动画速度,这真的很不错。现在是整数,我不知道是否应该把它变成非整数,但我不知道怎么做。你们总是可以看到当前正在屏幕上绘制的帧。
还有一个很酷的小细节,当我进入编辑模式时,动画会停止,这实际上是自动发生的,因为我们更改了更新函数(update function),而动画实际上是在更新函数中发生的。所以,如果我们更改了更新函数,动画就会停止。在输入更新函数时,它实际上会预览我正在输入的值。比如,如果我把值改成18,我就会看到不同的精灵,这样我就可以更容易地找到我想要的精灵,然后说:“哦,这个不行,我需要修复一下。”但你们也知道,如果我在输入东西并按上下键时,你们可以看到按上下键会使值增减1,所以我可以快速选择我想要的值和精灵。
我考虑过使用之前的敌人创建函数来创建一个敌人对象,但最终我只是获取了一个动画,并像在任何编辑中那样绘制它。所以,这有点假,但我真正关心的预览效果是敌人看起来是什么样的,因为数字并不能告诉我什么。我只想看到敌人,当我在挑选合适的动画时,我只想确保我选对了动画,而且,我也许想预览一下动画的速度。我可能更多地依赖于Enemy Edit来确定哪个动画看起来更好,但这只是为了验证一下。
我还确保了它的鲁棒性,所以如果敌人不存在,或者动画不存在,或者这里有一些无效的值,它就不会崩溃,它只是不会绘制任何东西。它实际上在等待一个有效的动画值出现。
发现问题并解决
我意识到在这种情况下我真正需要的东西是,当我向下滚动时,每一列的标题(也就是标题栏)仍然停留在那里,它们不会随着表格的其余部分一起滚动。我基本上是重新绘制了表格,我只是在表格自己的小剪辑区域中重新绘制了表格的第一行,这个剪辑区域被覆盖在所有内容之上。这有点粗暴,但我喜欢当我向下滚动时,仍然能够阅读到这些小提示。
非常有可能的是,这个功能在稍后会有所扩展,当我需要定义子弹从哪里射出时,也许有一个小小的提醒会是个好主意,提醒我是如何编码地面和碰撞的。但现在,我想我们已经完成了,可以继续下一个了。
下一个是Brain Edit(大脑编辑),我们开始吧。
Brain Edit与问题
好了,我们完成了Brain Edit。我这里有个小待办事项列表,但现在已经删除了。好吧,这是一个UI(用户界面)的改动,我不太确定要把新功能放在哪里。我想有一个复制大脑和删除大脑的功能,那里已经有一个设置按钮了,所以我想也许可以把所有的东西都放在一行里,但那样一行就会变得很长,可能会占用太多空间。
我决定使用一个下拉菜单,但我不太喜欢它,这不是一个理想的解决方案,因为你真的不知道这个按钮里还隐藏着一个菜单。但是,如果你按下显示大脑名称的按钮,它就会弹出这个小菜单,现在设置按钮就隐藏在里面。所以你可以进行设置操作,但现在你也可以复制你的大脑了。
这会创建这个设置的整个副本。子弹仍然保留在那里,这没关系。另一个也包括的东西是从Cowsh Map(奶牛地图?假设为游戏内的一个场景或地图名称)中继承过来的东西,比如等待敌人出现在屏幕上的功能,以及与整个屏幕的碰撞检测。我基本上把Cowsh Map中的所有东西都继承过来了,所以现在它的行为方式和Cowsh Map一模一样。
我对这个工作方式很满意,到目前为止没有什么惊喜,只是有点杂乱无章的代码,因为这是UI,UI从来都不是整洁的代码。但没有遇到什么大问题。
Skidget与后续计划
好吧,我想在Skidget这里稍微缩短一点,有一个简单的问题和一个大的问题,我认为解决简单的问题也会解决大的问题。但不管怎样,让我们继续吧。
好吧,看起来我们已经结束了杂乱无章的讨论。这有点棘手,让我先看看……让我快速解释一下现在正在发生什么。
所以,之前的问题是在这个移动函数(move function)里,当我移动东西的时候,如果我点击,它会把生成位置(spawning location)移动到我点击的位置,但那并不一定是敌人的位置。所以,现在当我点击时,它实际上会把敌人移动到那个位置,这对我来说更有意义。
我还做了另一件事情,其实我本来以为我不需要它,但最终还是决定加进去了。所以现在,当我搞砸了某件事情,移动了某个东西后,我会想:“哦不,它之前在哪里?”在移动模式下,我仍然可以按Y键(这里可能是指某个特定于应用的快捷键或操作,原文中的“o button”可能是个误写或者是特定上下文中的术语,但在这里我会按照常见的逻辑将其翻译为Y键,因为Y键在很多应用中常作为撤销操作的快捷键)来撤销移动。所以,我实际上必须按X键来确认,然后它才会提交移动。同样,当我右键点击时,也会取消移动,甚至还会有一个小圆点闪烁,作为之前敌人位置的提醒。
顺便说一下,我不确定之前它结束在哪里,我搞砸了一些生成位置,那些生成位置可能需要重新调整一下。但问题是,这里实际上有一个问题,而且我用了一种非常愚蠢和疯狂的方式来解决它。
现在的问题是,更新函数(update function)实际上并不知道我们正在移动的敌人的位置。我们是使用这样一个复杂的函数来计算敌人的位置的,这个函数我们之前在这里做过,它是……嗯,它是这样的:判断是不是镜像敌人,获取敌人,获取大脑,获取大脑的轨迹,计算敌人的年龄,找出我们在哪个轨迹上,然后得到位置。它是一个巨大的步骤过程,用来编码生成计划和实际创建屏幕上可见的敌人对象。
但是,这些在屏幕上可见的敌人对象,更新函数并不知道它们。所以我们不能提取出这里编码的信息,我们可以,但我不喜欢那么做。
所以,我实际上是在权衡两个邪恶的选择。所以,我所做的是,我把所有这些代码都提取出来,重新打包到一个新的函数中,这个函数叫做“Calc Offset”(计算偏移量),它基本上说:“好吧,对于这个生成计划,计算敌人在当前时间的X和Y偏移量。”
然后,在更新移动的时候,当我点击时,我获取鼠标位置,计算敌人从其原点移动的偏移量,我获取这些值,从鼠标位置中减去它们,这给了我一个新的、更新后的生成位置,但它不是我点击的位置,而是另一个位置。
然后,还有另一件事情,我在这里有三个变量,我记住敌人在进入移动状态之前的位置,然后,当我退出移动状态时,我就重新构造它。就这样,我记住了。
嗯,当我们复制东西的时候,我想确保复制的东西是正确的,因为当你复制东西的时候,你会立即进入移动状态,我想确保这一点也被妥善处理了。但除此之外,我们在这里已经完成了。
我们不需要Skidget了,这给我们留下了最后一件需要在模式编辑器(pattern editor)中添加的事情,那就是复制模式,然后我们就完成了。