你将要创建 的东西
介绍
SpriteKit 和 SceneKit 是 ios框架,旨在使开发人员可以轻松地在休闲游戏中创建 2D 和 3D 资源。在本教程中,我将向您展示如何将在两个框架中创建的内容组合到一个视图中,以利用应用程序提供的api。
您将通过使用基于 2D SpriteKit 的界面向 3D SceneKit 场景添加简单的播放/暂停按钮和记分功能来实现此目的。
本教程要求您至少运行 Xcode 6+ 并且具有基本 SpriteKit 和 SceneKit 内容的经验。如果没有,那么我建议您先阅读我们其他一些关于SpriteKit和SceneKit的 Tuts+ 教程 。
还建议您使用物理 iOS 设备进行测试,这需要一个有效的付费 iOS 开发者帐户。您还需要从GitHub下载启动项目。
1. 项目设置
当你打开启动项目时,你会看到,除了默认 AppDelegate和 ViewController类之外,你还有另外两个类, MainScene和 OverlayScene.
该类 MainScene是应用程序的子类 SCNScene并提供应用程序的 3D 内容。同样, OverlayScene该类是 SKScene您的应用程序中的 2D SpriteKit 内容的子类并包含该内容。
随意查看这些类的实现。如果您对 SpriteKit 和 SceneKit 有一定的经验,它应该看起来很熟悉。在您的 ViewController类的 viewDidLoad方法中,您还将找到用于设置基本 SCNView 实例的代码。
在 iOS 模拟器或物理设备上构建并运行您的应用程序。在这个阶段,您的应用程序包含一个蓝色旋转立方体。
是时候在 3D 内容之上添加 SpriteKit 场景了。这是通过 overlaySKScene在任何符合 SCNSceneRenderer协议的对象上设置属性来完成的,在我们的示例中就是SCNView 实例。在ViewController.swift 中将以下几行添加到 viewDidLoad方法中:
override func viewDidLoad() { ... self.spriteScene = OverlayScene(size: self.view.bounds.size) self.sceneView.overlaySKScene = self.spriteScene }
当您再次构建并运行您的应用程序时,您将看到您现在在左下角有一个暂停按钮位置,在应用程序视图的底部中心有一个分数标签。
你可能想知道为什么使用这个属性比简单地添加一个 对象SKView的子视图 更好SCNView 。使用该 overlaySKScene属性时,应用程序的 2D 和 3D 组件都使用相同的 OpenGL 上下文将内容呈现到屏幕上。这比创建两个单独的视图要好得多,每个视图都有自己的 OpenGL 上下文和渲染管道。尽管这种简单设置的差异可以忽略不计,但在更大、更复杂的场景中获得的性能却是无价的。
2. SceneKit 和 SpriteKit 交互
您可以通过多种不同的方式在您的 实例MainScene和 OverlayScene实例之间传输信息。在本教程中,您将使用键值观察,简称 KVO。
在实现暂停立方体动画的功能之前,您首先需要将此功能添加到 SpriteKit 场景中。在 OverlayScene.swift中,将以下方法添加到 OverlayScene类中:
override func touchesEnded(touches: Set<NSObject>, withevent event: UIEvent) { let touch = touches.first as? UITouch let location = touch?.locationInnode(self) if self.pauseNode.containsPoint(location!) { if !self.paused { self.pauseNode.texture = SKTexture(imageNamed: "Play Button") } else { self.pauseNode.texture = SKTexture(imageNamed: "Pause Button") } self.paused = !self.paused } }
touchesEnded(_:withEvent:)当用户从设备的屏幕上抬起手指时调用该 方法。在此方法中,您检查用户是否触摸了暂停按钮并相应地更新场景。再次构建并运行您的应用程序,以检查这部分拼图是否正常工作。
我们现在需要在用户点击暂停按钮时停止 3D 立方体旋转。返回 ViewController.swift 并在方法中添加以下行 viewDidLoad:
override func viewDidLoad() { ... self.spriteScene.addObserver(self.sceneView.scene!, forKeyPath: "paused", options: .New, context: nil) }
最后,将以下方法添加到 MainScene.swift 以启用键值观察:
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) { if keyPath == "paused" { self.paused = change[NSKeyValueChangeNewKey] as! Bool } }
如果您再次构建并运行您的应用程序,当点击暂停按钮时立方体应该停止旋转。
正如信息可以从 SpriteKit 场景传输到 SceneKit 场景一样,您也可以将数据从 SceneKit 实例发送到 SpriteKit 实例。在这个简单的应用程序中,每次用户点击立方体时,您将在分数上加一分。在 ViewController.swift中,添加以下方法:
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) { let touch = touches.first as? UITouch let location = touch?.locationInView(self.sceneView) let hitResults = self.sceneView.hitTest(location!, options: nil) for result in (hitResults as! [SCNHitTestResult]) { if result.node == (self.sceneView.scene as! MainScene).cubeNode { self.spriteScene.score += 1 } } }
请注意,我们使用的是 touchesEnded(_:withEvent:)方法而不是 a UITapGestureRecognizer,因为 UIGestureRecognizer对象会导致 touchesEnded(_:withEvent:)方法的执行非常不一致。由于您将此方法用于暂停按钮,因此您需要确保每次用户点击屏幕时都会调用它。
在该touchesEnded(_:withEvent:)方法中,我们对您的 sceneView. 如果触摸的位置与立方体的位置相对应,则在您的得分上加一分 spriteScene。score由于类中属性 的属性观察者,场景中的文本将自动更新 OverlayScene。
再次运行您的应用程序并点击多维数据集,以验证无论何时点击多维数据集都会更新分数标签。
暂停按钮和乐谱标签都举例说明了如何在 SpriteKit 和 SceneKit 场景之间传输信息。但是,此信息不限于数值和布尔值,它可以是您的项目需要的任何数据类型。
3.使用 SpriteKit 场景作为 SceneKit 材质
除了能够在 SceneKit 场景之上覆盖 SpriteKit 场景,您还可以使用SKScene 实例作为 SceneKit 几何体的材质。这是通过将对象分配给SKScene对象的 contents属性来完成的 SCNMaterialProperty。这使您可以轻松地将动画材质添加到任何 3D 对象上。
让我们看一个例子。在您的应用程序中,您将向立方体添加一个简单的颜色过渡动画,而不是它当前具有的静态蓝色。在类的init方法中 MainScene, 替换以下代码块:
override init() { super.init() let cube = SCNBox(width: 3, height: 3, length: 3, chamferRadius: 0) let cubeMaterial = SCNMaterial() cubeMaterial.diffuse.contents = UIColor.blueColor() cube.materials = [cubeMaterial] self.cubeNode = SCNNode(geometry: cube) self.cubeNode.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 0.01, z: 0, duration: 1.0/60.0))) ... }
使用此代码块:
override init() { super.init() let cube = SCNBox(width: 3, height: 3, length: 3, chamferRadius: 0) let materialScene = SKScene(size: CGSize(width: 100, height: 100)) let backgroundNode = SKSpriteNode(color: UIColor.blueColor(), size: materialScene.size) backgroundNode.position = CGPoint(x: materialScene.size.width/2.0, y: materialScene.size.height/2.0) materialScene.addChild(backgroundNode) let blueAction = SKAction.colorizeWithColor(UIColor.blueColor(), colorBlendFactor: 1, duration: 1) let redAction = SKAction.colorizeWithColor(UIColor.redColor(), colorBlendFactor: 1, duration: 1) let greenAction = SKAction.colorizeWithColor(UIColor.greenColor(), colorBlendFactor: 1, duration: 1) backgroundNode.runAction(SKAction.repeatActionForever(SKAction.sequence([blueAction, redAction, greenAction]))) let cubeMaterial = SCNMaterial() cubeMaterial.diffuse.contents = materialScene cube.materials = [cubeMaterial] self.cubeNode = SCNNode(geometry: cube) self.cubeNode.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 0.01, z: 0, duration: 1.0/60.0))) ... }
此代码片段创建一个 SKScene 具有一个背景节点的简单实例,该背景节点最初为蓝色。然后,我们为从蓝色到红色和绿色的过渡创建三个动作。我们在后台节点上以重复顺序运行这些操作。
虽然我们在示例中使用了基本颜色,但可以在 SpriteKit 场景中完成的任何操作都可以转换为 SceneKit 材质。
最后一次构建并运行您的应用程序。您将看到立方体的颜色从蓝色变为红色和绿色。
请注意,暂停 SceneKit 场景不会暂停我们用作材质的 SpriteKit 场景。要暂停材质的动画,您需要保留对 SpriteKit 场景的引用并显式暂停它。
结论
结合 SpriteKit 和 SceneKit 内容在很多方面都非常有益。将 2D 场景叠加在 3D 场景之上允许您创建动态界面,并且由于两个场景使用相同的 OpenGL 上下文和渲染管道,这将带来性能提升。您还学习了如何利用 SpriteKit 为 3D 对象创建动画材质。如果您有任何意见或问题,请将其留在下面的评论中。