本章内容源自于:Move it Around
移动控制
是的,如果屏幕上没有东西会动,那么没人会说这是个游戏的。我们甚至都没有办法控制我们的星际战舰。至少,我们应该能通过按键让它左右移动。我将告诉你如何通过控制鼠标左右移动来实现这个功能。听起来不错,不是吗?
代码如下。如果想弄明白监听器的详情用法,请看初级教程5-Hello Input System:
(你会发现,在update()中我们使用了ship.stream().findFirst()方法,这是因为)我们可能会有多个飞船,因此我们选择拿第一个。本案例中,我们只有一个飞船。但有可能以后我们会有三个飞船:一个是我们当前可以操控的飞船,另外两个稍微小一点放在屏幕左上角代表玩家还有几条命。下面的代码片段展示了如何通过过滤名称组件获取特定的飞船实体:
过滤器会先排除掉不含有模型组件或位置组件的实体,然后从剩下的实体中找到名为SpaceShip的那个实体。或者我们可以事先写一个没有任何属性的组件作为飞船的组件,然后通过查询拥有这个组件的实体获取飞船。这种组件被称为标记组件,这也是一种常用的方法。
在AnalogListener中我们可以拿到鼠标的x坐标,然后可以以此计算出飞船的x位置坐标。我的这种算法比较简陋,你可以写一个更符合物理学的算法。这个position还没有与飞船关联起来,因此还不会影响飞船位置。关联到飞船的代码在更新循环中:
每次更新都会找到飞船然后把最新的position设置到它的位置组件中。注意每次是创建新组件而不是修改组件中的属性值。
在主类中注册输入控制系统:
现在我们的飞船可以移动了吗?真的?真的。在显示系统的帮助下,你可以看到飞船移动。我们没有修改任何现有逻辑,只是添加了一个输入控制系统就可以让飞船动起来。当然,如果我们要给飞船添加新的组件,就可能要修改原有代码了。如果你坚持使用面向对象的设计方法来做这个游戏的话,我想你现在肯定已经是弄得一团糟。实体系统不是通过继承实现功能的,而是数据驱动的,不要搞混了。至少不要把这两个概念搞混了。
飞舞的入侵者
星际战舰现在由我们的鼠标控制着,可以移动。但是画面还是没什么动感。为了让入侵者动起来,我将写一个非常简单的AI系统出来:
不要忘了在主类中注册它:
入侵者现在会上下左右按照队列飘来飘去了。是时候给你看实体系统的精妙之处了。不过你应该已经能体会到了游戏逻辑是如何清晰地分离独立的了。
让我们给显示系统新增加些更酷的功能。我们要让入侵者沿着y轴旋转。于是我打算给位置组件新增加一个旋转属性。当然你也可以把旋转独立为一个新的实体组件,但这样做你得修改原先的代码。用下列代码替换原来的位置组件类:
现在我们不得不改动显示系统,使它支持模型旋转。我们稍微改动updateModelSpatial()方法,代码如下:
也许有更优雅的书写方式,但我就知道这一种。现在显示系统支持旋转更新了。为了让入侵者旋转起来我们需要修改入侵者AI系统。用下列代码替换掉wabbeling()方法:
实际上只有这部分变动了:
所有的入侵者将每秒沿y轴旋转90°。我添加了一些限制确保旋转角度始终不超过360°。