iOS9 每日学习第12天:GameplayKit 之 Behaviors 和 Goals

在上一篇我们学习了利用 GameplayKit的 pathfinding API 来计算位于场景中的两点之间的路径,并避开指定的障碍物的算法。

在这一篇中,让我们来实现一种不同的在场景中移动的效果。GameplayKit 介绍了 Behaviours(行为) 和 Goals(目标) 的概念.他们提供了一种方式,让你能够依赖约束和目标把节点的放置在场景中某个特定位置。让我们先看一下视频,然后再来详细看一下。

上面的例子中(我们马上要创建它),你可以看到一个黄色的盒子代表一个用户。黄色的盒子随着用户点击场景中的任意一处来移动。特别基本的东西,对吧。有趣的是导弹部分,它能够寻找到player,并且总是试图通过player节点的中心。

这不需要任何的物理或者自定义代码来完成,这完全有一个行为可寻址目标来控制。

现在,让我们通过 Demo了解一下 behaviours 和 goals 是怎么工作的。

Creating a Behavior and Goal Example

使用默认的 SpriteKit 模版创建项目,打开 GameScene.swift

setup1.png

首先,我们定义一个实例

GKEntity 是一个通用的实体,可以给它添加组件和方法。在我们的例子中,我们有两个实例,一个代表 player,一个代表导弹。我们马上来看一下它的细节实现。

我们还需要创建一个组件系统的数组。这个组件系统是指符合同样类型的组件的一个集合。我们可以在需要时候的时候,再定义它(lazy var),因为我们仅需初始化它一次。我们有一个组件作为靶子(可以用来追踪player的位置,并添加冒烟的效果),另一个作为导弹。我们定义的顺序,会成为一会儿运动的顺序。所以我们先返回targeting 然后是 rendering. 因为我们希望根据目标的变化,来追踪显示他们的。

但什么才是一个 GameKit 组件呢?我们已经讨论了在场景中的实体的效果,但没讲具体做了什么。一个 GKComponent 在特定部分,囊括了数据和逻辑。组件和实体联系,一个实体可能对应多个组件。它们为组件提供可重用的行为。它们通过组件模型,来帮助解决大型游戏中可能出现的复杂而大型的继承树问题。

在这个场景中,两个实体都有渲染组件,导弹实体还有靶子组件。

设置实体

The Player Entity

下面代码是 player 类,它是一个简单的几成字 NodeEntity的类,拥有唯一一个组件。注意还有一个 GKAgent2D 的属性.

GKAgent2D 是 GKAgent的一个子类, 呈现为一个根据速度定位的本地坐标系统。

在本例中,代理其实是无言的。如果不是用户手动干预,它不会做任何事情,也不会对位置进行任何变化。我们需要一个代理,因为靶子组件必须有一个代理。

在初始化中,我们添加一个 RenderComponent 和一个PlayerNode. 我们不详细讲 PlayerNode 了,因为非常枯燥。这里我们仅简单画一个黄色的方盒。

我们把代理设为自己,通过把代理添加到实体上去。

我们还需要去生命 GKAgentDelegate 的代理方法。这样,当代理更新后,Node 的位置会自动更新,同时,当用户手动更新了位置后,代理也会通过计算更新位置。

The Missile Entity

missile 实体和 PlayerNode 略有不同。我们添加一个目标代理,让导弹去追踪。

你可能注意到这个类中没有 GKAgent2D,这是因为我们使用了 TargetingComponent 来控制实体在场景中的移动。稍后,我们会讨论 TargetingComponent. 现在,我们需要知道,我们已经提供了 targetAgent ,我们启动代理的方法。

我们需要生命 agentDidUpdate 和 agentWillUpdate两个代理方法。这和Player类中有什么不同呢?在这个类中,我们还需要为方法提供 Z 轴的数值。

The Targeting Component

到目前为止,所有的类相对都是轻便的。你可能都忘了还需要在靶子组件中完成逻辑代码
幸运的是, 得益于 GameplayKit,在本例中,我们仅需要写20行代码就可以。

这段代码简单的不需解释。你可以看到他继承自 GKAgent2D, 创建了一个GKGoal.然后通过这个goal 创建了CKBehavior对象。如果你有多个 goal,例如去追踪一个目标同时要避开某个目标,你就可以创建多个 GKGoal。 你甚至还可以分别GKGoal 的 weight 属性,这样可以设置避开某个 goal 比追逐某个 goal 的权重更重一些。

我们同时也设置了一些其他的属性:maxSpeed,maxAcceleration 和 mass. 这些属性需要根据你的实际场景进行设置,这里设置成这样对我来说是合适的。刚开始的时候我使用了默认值,然后以为那里出来毛病。后来发现是默认值太低了,导致移动非常慢,完全看不出效果。

The Missile Node

现在 Missile entity 创建好了,我们需要给它添加一个node,以在场景中显示。这个node 是SKNode的子类,有一个单独的方法。

你可以看到setupEmitters 方法创建了两个 SKEmitter nodes.把 target node 设置为了场景,如果不设置的话,那么就不会出现跟踪导弹并冒烟的效果。你可以打开 MissileFire.sks 和 MissileSmoke.sks 两个文件,查看具体内容,这里我们不详细解释了。

Combining the Parts

现在我们的nodes, entities 和 components都已经创建好了,我们回到 GameScene.swift文件中,把它们组合起来。 我们需要重载 didMoveToView方法。

我们已经在初始化是创建了 player,所以我们添加player.node到场景中。

对于missile, 我们也必须要在这里创建好。

然后我们为 missile 添加setupEmitters方法,让烟雾可以根据目标移动并扩散,而非只是动一下。

最后,所有的entities创建好后,我们添加它的components到我们的组件系统中。

现在在update.currentTime方法中,为组件的更新时间数组,添加增量时间。这会使的重新计算时间并进行渲染。

这就是全部我们做的了。现在运行一下游戏,你会看到一个导弹始终跟随着playe。在这里我们并没有添加碰撞和爆炸效果,如果你感兴趣可以自己做一下。为什么不呢?

延伸阅读

想要了解更多关于 GameplayKit的特性,推荐观看WWDC 2015的session 608, Introducing GameplayKit. 别忘了,可以在Git中找到本文的示例代码。

备注:本文译者对 iOS戏比较陌生,如有翻译错误,还望大家在评论中指出。

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部