Sprite Kit编程指南(7)-高级场景处理

使用Sprite Kit涉及到操纵场景树的内容来让内容在屏幕上的动起来。通常情况下,动作是该系统的核心。然而,通过直接地挂接到(hooking into)场景处理,你可以创建动作不能单独完成的其他行为。要做到这一点,你需要学习:

·      场景如何处理动画

·      如何在场景处理过程中添加自己的行为


场景如何处理动画帧


在传统视图系统中,视图内容渲染一次后,然后只有当模型(model)的内容发生变化时才会再次渲染。这种模式对于视图非常适用,因为在实践中,大多数视图内容是静态的。另一方面,Sprite Kit是明确为动态内容设计的。Sprite Kit不断更新的场景内容并渲染它,以确保动画是平滑和精确的。

动画和渲染场景的过程绑定到场景对象(SKScene)上。场景和动作处理只在场景被呈现时运行。呈现的场景运行一个渲染循环,该循环在处理场景的节点树和渲染它之间交替进行。这种模式类似于在大多数游戏中使用的渲染和处理循环。

图7-1展示了场景执行渲染循环的步骤。

  图7-1场景中的帧处理

Sprite Kit编程指南(7)-高级场景处理_第1张图片

每次通过的最终目标是要渲染并更新场景的节点树的内容。你不直接挂接到渲染步骤,而是更新节点树的内容,如在“建筑场景中描述的那样。然而,Sprite Kit为你提供了工具来挂接到其他步骤。下面是那些步骤:

1.    随着时间在模拟中流逝,渲染循环从调用场景的update:方法开始。这是实现你自己游戏内模拟(in-game simulation)的主要场所,包括输入处理、人工智能、游戏脚本和其他类似的游戏逻辑。通常情况下,你使用这个方法对节点进行更改或运行节点上的动作。

2.    场景处理树中的所有节点上的动作。它找到任何正在运行的操作,并将那些更改应用到树上。在实践中,因为自定义操作,你还可以挂接到动作进程(mechanism)调用你自己的代码。

你不能直接控制动作的处理的顺序,也不能让场景跳过某个节点上的动作,除非你从这些节点中移除动作或从节点树中移除节点。

3.    在帧的所有动作都已处理后,场景的didEvaluateActions方法被调用。

4.     然后场景对场景中的物理体模拟物理。添加物理到场景中在模拟物理中描述,但模拟物理的最终结果是,物理模拟可能会调节树中节点的位置和旋转角度。你的游戏也可以在物理体之间互相接触时接收到回调。

5.    场景的didSimulatePhysics方法是场景渲染前的最后一个步骤。这是你对场景进行更改的最后机会。

6.    渲染场景。


场景中的后处理


场景可以以任意顺序处理场景树中的动作。由于这个原因,如果你有一些需要在每一帧运行的任务,且你需要在它们运行时精确地控制它们,你应该使用didEvaluateActionsdidSimulatePhysics方法来执行这些任务。通常情况下,你在这里进行的更改,需要树中的某些节点的最终计算出来的位置。你的后处理(post-processing)可以利用这些位置,并在树上执行其他有用的工作。

下面是一些你可能执行的后处理任务的例子:

·      居中场景的内容在某个节点上

·      在场景内容上添加调试信息的覆盖层(overlay)

·      复制节点树的某一部分的节点到另一部分。例如,你可能有一个效果节点,它的子节点需要跟树中其他的节点一样。


例子:居中场景在节点上

在需要内容滚动的游戏中,居中场景的内容在某个节点上,对相机(cameras)和类似的概念是有用的。在这种情况下,内容比场景的frame要大。在玩家左右移动时,角色在某个地方保持固定,而世界(world)在他们周围移动。场景保持对角色锁定,无论玩家把角色移动到哪里。

Sprite工具并不提供相机的内置支持,但实现却是非常简单。世界和相机分别由场景中的一个节点表示。世界是场景的一个直接子节点,而相机是一个世界节点的后代。节点的这种安排是非常有用的,因为它给游戏世界一个不绑定于场景坐标系的坐标系。你可以使用这个坐标系布置世界内容。

图7-2   为一个滚动的世界组织场景

Sprite Kit编程指南(7)-高级场景处理_第2张图片

相机被放置在世界中。世界可以在场景中四处滑动。因为相机是一个节点,你可以用动作甚至是物理来移动相机。然后,在后处理步骤,你重新在场景中定位世界节点,使相机节点在场景正中。图7-3展示了这个。世界放置在场景中,以便让角色居中。然后,该角色在世界里面四处移动。最后,后处理步骤重定位世界以便角色再次居中。

图7-3   世界在场景内移动

Sprite Kit编程指南(7)-高级场景处理_第3张图片

下面是实现的片段:

1.    把场景的锚点放置在场景中心。

[cpp]  view plain copy
  1. self.anchorPoint = CGPointMake(0.5,0.5);  

2.    使用世界节点来表示滚动的世界。世界的内容将作为世界的子节点,将是精灵节点和内容节点。(图中未示出)。

[cpp]  view plain copy
  1. SKNode *myWorld = [SKNode node];  
  2. [self addChild:myWorld];  

3.    使用世界内的一个节点表示相机。

[cpp]  view plain copy
  1. SKNode *camera = [SKNode node];  
  2. camera.name = @“camera”;  
  3. [myWorld addChild:camera];  

4.    使用didSimulatePhysics方法居中场景在相机上。centerOnNode方法把相机当前的位置转换成场景坐标,然后从世界的位置减去那些坐标来滑动角色到0,0位置。

[cpp]  view plain copy
  1. - (void)didSimulatePhysics {  
  2.     [self centerOnNode:[self childNodeWithName:@“//camera”]];  
  3. }  
  4.    
  5.  - (void)centerOnNode:(SKNode *)node  
  6. {  
  7.     CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];  
  8.     node.parent.position = CGPointMake(node.parent.position.x  -  cameraPositionInScene.x   
  9.     node.parent.position.y  -  cameraPositionInScene.y);  
  10. }  

例子:添加调试覆盖层

当你正在使用Sprite Kit开发一个游戏时,显示实时的可视化调试信息是有帮助的,这些信息关于场景中正在发生的事情。例如,以下信息可能在调试你的游戏时是有用的:

·      场景中角色的人工智能决策

·      在世界中触发脚本动作的位置

·      物理信息,如重力以及其他力,甚至物理体的尺寸

形状和标签节点对注释你的游戏的行为特别有用。

要添加一个调试覆盖层,最好的办法是使用一个单一的节点代表覆盖层,并把它添加到场景。所有调试信息由这个节点的后代节点表示。这个节点被放置在场景中附带一个z坐标,z坐标把它放置在所有其他场景内容的上面。调试节点可以是但不必是场景的直接子节点。例如,在一个滚动的世界,你放置在覆盖层的信息可能绑定于世界坐标,而不是场景坐标。

在场景开始处理一帧之前,你从场景中移除这个节点,然后移除它的子节点。其假设是,调试信息需要每帧更新。经过场景模拟物理后,节点被添加回到场景且它的内容会重新生成。

1.    创建场景类中的调试节点的属性,并在场景第一次呈现时初始化它。

[cpp]  view plain copy
  1. @property(SKNode *)debugOverlay;  
  2.    
  3. self.debugOverlay = [SKNode node];  
  4. [self addChild:self.debugOverlay];  

2.    在动作处理前移除节点。

[cpp]  view plain copy
  1. - (void)update:(NSTimeInterval)currentTime  
  2. {  
  3.     [self.debugOverlay removeFromParent];  
  4.     [self.debugOverlay removeAllChildren];  
  5. }  

3.    在场景处理完后添加节点。

[cpp]  view plain copy
  1. - (void)didSimulatePhysics  
  2. {  
  3.     [self ddChild:self.debugOverlay];  
  4.     / /添加代码来创建调试节点并添加调试图像到调试节点。  
  5.     / /这个例子显示了重力矢量。  
  6.     SKShapeNode *gravityLine = [[SKShapeNode alloc] init];  
  7.     gravityLine.position = CGPointMAke(200,200);  
  8.    
  9.     CGMutablePathRef path= CGPathCreateMutable();  
  10.     CGPathMoveToPoint(path, NULL, 0.0, 0.0);  
  11.     CGPathAddLineToPoint(path, self.physicsWorld.gravity.x * 10,  
  12.     self.physicsWorld.gravity.y * 10);  
  13.     CGPathCloseSubpath(path);  
  14.     gravityLine.path = path;  
  15.     CGPathRelease(path);  
  16.    
  17.     [self.debugOverlay addChild:gravityLine];  
  18. }  

例子:在场景中复制信息

实现此行为的技术类似与添加调试覆盖层。当你预处理(pre-process)场景时,从树中移除节点过时的副本。然后,在后处理过程中,你从树的一部分复制节点到另一部分。你使用节点的副本,因为每个节点只能有一个父节点。

在某些情况下,你只需要在每一帧更新少量的信息。在这种情况下,不断地添加、移除和复制节点的成本可能是昂贵的。相反,只复制一次,然后用你的后处理步骤来更新重要的属性。

[cpp]  view plain copy
  1. copyNode.position = originalNode.position;  

你可能感兴趣的:(Sprite Kit编程指南(7)-高级场景处理)