使用Sprite Kit涉及到操纵场景树的内容来让内容在屏幕上的动起来。通常情况下,动作是该系统的核心。然而,通过直接地挂接到(hooking into)场景处理,你可以创建动作不能单独完成的其他行为。要做到这一点,你需要学习:
· 场景如何处理动画
· 如何在场景处理过程中添加自己的行为
在传统视图系统中,视图内容渲染一次后,然后只有当模型(model)的内容发生变化时才会再次渲染。这种模式对于视图非常适用,因为在实践中,大多数视图内容是静态的。另一方面,Sprite Kit是明确为动态内容设计的。Sprite Kit不断更新的场景内容并渲染它,以确保动画是平滑和精确的。
动画和渲染场景的过程绑定到场景对象(SKScene
)上。场景和动作处理只在场景被呈现时运行。呈现的场景运行一个渲染循环,该循环在处理场景的节点树和渲染它之间交替进行。这种模式类似于在大多数游戏中使用的渲染和处理循环。
图7-1展示了场景执行渲染循环的步骤。
图7-1场景中的帧处理
每次通过的最终目标是要渲染并更新场景的节点树的内容。你不直接挂接到渲染步骤,而是更新节点树的内容,如在“建筑场景”中描述的那样。然而,Sprite Kit为你提供了工具来挂接到其他步骤。下面是那些步骤:
1. 随着时间在模拟中流逝,渲染循环从调用场景的update:
方法开始。这是实现你自己游戏内模拟(in-game simulation)的主要场所,包括输入处理、人工智能、游戏脚本和其他类似的游戏逻辑。通常情况下,你使用这个方法对节点进行更改或运行节点上的动作。
2. 场景处理树中的所有节点上的动作。它找到任何正在运行的操作,并将那些更改应用到树上。在实践中,因为自定义操作,你还可以挂接到动作进程(mechanism)调用你自己的代码。
你不能直接控制动作的处理的顺序,也不能让场景跳过某个节点上的动作,除非你从这些节点中移除动作或从节点树中移除节点。
3. 在帧的所有动作都已处理后,场景的didEvaluateActions
方法被调用。
4. 然后场景对场景中的物理体模拟物理。添加物理到场景中在“模拟物理”中描述,但模拟物理的最终结果是,物理模拟可能会调节树中节点的位置和旋转角度。你的游戏也可以在物理体之间互相接触时接收到回调。
5. 场景的didSimulatePhysics
方法是场景渲染前的最后一个步骤。这是你对场景进行更改的最后机会。
6. 渲染场景。
场景可以以任意顺序处理场景树中的动作。由于这个原因,如果你有一些需要在每一帧运行的任务,且你需要在它们运行时精确地控制它们,你应该使用didEvaluateActions
和didSimulatePhysics
方法来执行这些任务。通常情况下,你在这里进行的更改,需要树中的某些节点的最终计算出来的位置。你的后处理(post-processing)可以利用这些位置,并在树上执行其他有用的工作。
下面是一些你可能执行的后处理任务的例子:
· 居中场景的内容在某个节点上
· 在场景内容上添加调试信息的覆盖层(overlay)
· 复制节点树的某一部分的节点到另一部分。例如,你可能有一个效果节点,它的子节点需要跟树中其他的节点一样。
在需要内容滚动的游戏中,居中场景的内容在某个节点上,对相机(cameras)和类似的概念是有用的。在这种情况下,内容比场景的frame要大。在玩家左右移动时,角色在某个地方保持固定,而世界(world)在他们周围移动。场景保持对角色锁定,无论玩家把角色移动到哪里。
Sprite工具并不提供相机的内置支持,但实现却是非常简单。世界和相机分别由场景中的一个节点表示。世界是场景的一个直接子节点,而相机是一个世界节点的后代。节点的这种安排是非常有用的,因为它给游戏世界一个不绑定于场景坐标系的坐标系。你可以使用这个坐标系布置世界内容。
图7-2 为一个滚动的世界组织场景
相机被放置在世界中。世界可以在场景中四处滑动。因为相机是一个节点,你可以用动作甚至是物理来移动相机。然后,在后处理步骤,你重新在场景中定位世界节点,使相机节点在场景正中。图7-3展示了这个。世界放置在场景中,以便让角色居中。然后,该角色在世界里面四处移动。最后,后处理步骤重定位世界以便角色再次居中。
图7-3 世界在场景内移动
下面是实现的片段:
1. 把场景的锚点放置在场景中心。
2. 使用世界节点来表示滚动的世界。世界的内容将作为世界的子节点,将是精灵节点和内容节点。(图中未示出)。
3. 使用世界内的一个节点表示相机。
4. 使用didSimulatePhysics
方法居中场景在相机上。centerOnNode
方法把相机当前的位置转换成场景坐标,然后从世界的位置减去那些坐标来滑动角色到(0,0
)
位置。
当你正在使用Sprite Kit开发一个游戏时,显示实时的可视化调试信息是有帮助的,这些信息关于场景中正在发生的事情。例如,以下信息可能在调试你的游戏时是有用的:
· 场景中角色的人工智能决策
· 在世界中触发脚本动作的位置
· 物理信息,如重力以及其他力,甚至物理体的尺寸
形状和标签节点对注释你的游戏的行为特别有用。
要添加一个调试覆盖层,最好的办法是使用一个单一的节点代表覆盖层,并把它添加到场景。所有调试信息由这个节点的后代节点表示。这个节点被放置在场景中附带一个z坐标,z坐标把它放置在所有其他场景内容的上面。调试节点可以是但不必是场景的直接子节点。例如,在一个滚动的世界,你放置在覆盖层的信息可能绑定于世界坐标,而不是场景坐标。
在场景开始处理一帧之前,你从场景中移除这个节点,然后移除它的子节点。其假设是,调试信息需要每帧更新。经过场景模拟物理后,节点被添加回到场景且它的内容会重新生成。
1. 创建场景类中的调试节点的属性,并在场景第一次呈现时初始化它。
2. 在动作处理前移除节点。
3. 在场景处理完后添加节点。
实现此行为的技术类似与添加调试覆盖层。当你预处理(pre-process)场景时,从树中移除节点过时的副本。然后,在后处理过程中,你从树的一部分复制节点到另一部分。你使用节点的副本,因为每个节点只能有一个父节点。
在某些情况下,你只需要在每一帧更新少量的信息。在这种情况下,不断地添加、移除和复制节点的成本可能是昂贵的。相反,只复制一次,然后用你的后处理步骤来更新重要的属性。