转载

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

原文: Scene Kit Tutorial with Swift Part 2: Nodes

注:这是 《3D iOS 游戏教程》 这本书中一个章节的缩写版,来让您可以来领略一下这本书的内容。希望您喜欢!

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

这一教程将向你展示如何使用苹果最新的游戏框架-SceneKit,来创建你的第一个 3D 游戏。

在这个一共 5 节的系列教程中,你将会创建你的第一个 SceneKit 游戏: 几何忍者(GeometryFighter) !这是一个类似于 水果忍者 那样的游戏,你来摧毁不断涌上屏幕的几何体来满足自己内心的破坏欲:]

下面是本系列教程文章的导航及简介:

  • 第 1 节,你将会学习到如何新建一个空白的 SceneKit 工程做为一个好的开始。

  • 第 2 节,开始编写游戏,并学习有关 SceneKit 节点的知识。

  • 第 3 节(待更新),学习如何使用 SceneKit 内置的物理引擎来移动你的几何体。

  • 第 4 节(待更新),学习有关 SceneKit 中有关渲染循环的知识,并让你的几何体不断重生。

  • 在最后的第 5 节(待更新)中,你将学习如何将一些非常酷的粒子效果添加到游戏中。

在上一节的学习中,我们了解了如何新建一个空白的 SceneKit 工程做为一个好的开始。

在本节的教程里,我们将开始编写游戏,在这个过程中学习有关 节点(Node) 的知识。

本篇教程从上一节教程结束的地方开始。如果你没有跟上,不要着急 ———— 你可以从这里下载本次教程的 开始程序包 。

开始

Scene Kit 中把所有游戏中的元素以一种 场景图(scene graph) 的方式组织起来。

每一个游戏中的元素(比如灯光、摄像头、粒子发射器等)都被称为一个个的 节点(node) 存储在这种树形结构中。

为了说明它的工作原理,我们来看看下面这张图:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

为了进行类比,我们把上面这个图中每一个骨头都想象成一个 节点(node) 。最大的骨头是脊柱,我们认为它是所有骨头的根源,所以它就是 根节点(root node) 。肩骨是连接在脊柱上的,所以我们认为肩骨是脊柱的 子(child) 节点,而脊柱则是肩骨的 父(parent) 节点。

同理可知,大臂的骨头是肩骨的子节点,小臂的骨头是大臂骨头的子节点,手指的骨头是小臂的骨头的子节点。

每一个节点的位置都是相对于它的父节点的。比如,如图中右边所示,要想挥动骷髅的左边的手臂,我们只需要像蓝色箭头这样旋转他的大臂的骨头,所有的子节点(小臂、手指)都会跟着一起旋转了。

恭喜你!你刚刚学习完了人体解剖学的入门课程!:]

代码中的节点

从技术的角度来看,一个节点的类型是 SCNNode ,它代表了在三维空间中相对于父节点的位置。节点本身并不包含任何可以显示的内容,在渲染到场景上之后也会消失不见。为了创建可以显示的内容,你需要将其他的组件,比如灯光、摄像机或者几何体(比如骨头)添加到节点上。

场景图中有一个特殊的节点形成了整个节点层级结构的基础,它就是 根节点(root node) 。为了构建游戏场景,我们把节点做为子节点要么加入到根节点中,要么就加入到根节点的其它子节点中。

Scene Kit 中的资源目录(Asset Catalog)

如果有一天你变成了成功又有钱的 3D 游戏设计者,你可以自己雇佣图形设计师以及音效工程师,这样你就可以把时间全部用来专心致志地写游戏代码了。:] Scene Kit 中的 资源目录(Asset Catalog) 就是设计来专门帮你处理这些代码之外的游戏资源的。

资源目录(Asset Catalog)让我们可以一个单独的文件夹里管理所有的游戏资源;使用它只需要我们在工程中新建一个以 .scnassets 为扩展名的文件夹,然后把所有的游戏资源文件放在那个文件夹里就可以了。Xcode 在编译的时候会自动把所有这个目录下的文件拷贝程序包里,并且保留你文件夹里的层级结构。

这样做的好处在于,你只需要把资源目录文件夹共享给设计师,如果有什么需要修改的问题(比如某只 不-是-那-么-凶-的-重-眼-怪),他们直接在上面修改之后,效果就会出现在你下一次编译的时候了,不需要来回拷来拷去的。

现在你应该明白资源目录的作用是什么了,很快你就要在几何忍者中添加一个这样的目录。

把教程资源文件包中的 GeometryFighter.scnassets 文件夹拖拽到 Xcode 的游戏工程里。在弹出的窗口中,确保 Copy items if neededCreate Groups ,以及 Add to targets 中的 GeometryFighter 是选中状态,然后点击 Finish

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

在项目的导航中选择 GeometryFighter.scnassets 这个文件夹,你会注意到在右边的面板中出现了一些独有的选项。展开GeometryFighter.scnassets 及它的子文件夹来查看更多细节:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

你会看到在资源目录下有两个文件夹: Sounds (声音)文件夹包含了游戏所需的声音文件, Textures (纹理)文件夹包含了所需的图像。你可以尽情地看看都是些什么内容。

添加启动屏幕

我们刚刚添加好了资源目录,现在需要做一些整理工作,并添加一张合适的图片做为启动屏幕。

首先,在项目的导航栏中单击 Assets.xcassets ,把 GeometryFighter.scnassets/Textures/Logo_Diffuse.png 这个文件拖进资源里,就放在 AppIcon 的下面。

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

下一步,点击导航栏中的 LaunchScreen.storyboard 。选中 View ,在右边的属性设置栏中,把 Background (背景)这个属性设置为深蓝色(或者任何你喜欢的颜色),如下图:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

下一步,将 Logo_Diffuse 这张图片从 Media Library (媒体库)拖到视图的正中。把这张图片的 Mode (拉伸模式)属性设置为 Aspect Fit (等比例缩放),如下图:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

干的漂亮,就还差一点点就完成了。现在我们需要添加一些约束来保证图片在所有 iOS 设备上的正常显示。在底部点击 Pin 这个按钮,把四个边的约束都点亮,然后点击底部的 Add 4 Constraints 的按钮,如图所示:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

这样我们就添加好了启动屏幕!编译并运行程序;你会看到一个崭新的启动屏幕:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

添加背景图片

炫酷的启动画面一消失,你就又被扔回到了命运的黑色大门前。现在是时候添加一个漂亮的背景了,这样你就不会感觉自己在盯着一个黑洞了。

为了完成这个,打开 GameViewController.swift 这个文件,将下面这行代码添加到 配置场景() 这个方法的底部:

scn场景.background.contents = "GeometryFighter.scnassets/Textures/Background_Diffuse.png"   

这行代码告诉我们的场景去加载资源目录中一个名为 Background_Diffuse.png 的图片,使用它来做我们的背景。

编译并运行;游戏运行后你将看到一个蓝色的背景图:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

你现在已经完成了基本的清理工作。你的游戏现在有了一个闪亮的图标、一个耀眼的启动图,外加一个非常漂亮的背景!接下来我们要准备向上面添加并显示 节点(Node) 了。

Scene Kit 的坐标系

在开始添加节点之前,你首先需要理解 Scene Kit 的坐标系的有关知识,这样你才能把节点放到想要的位置上。

在诸如 UIKit 和 Sprite Kit 这样的 2D 系统中,你使用一个包含 X 坐标和 Y 坐标的点来描述一个视图(view)或者一个精灵(sprite)的位置。而在三维的空间里,为了确定一个物体的位置,你还需要一个 Z 轴的坐标来描述物体的深度。

来看下面这个示意图:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

Scene Kit 使用这种三个轴的坐标系来描述一个物体在三维空间中的位置。红色的方块放置在 X 轴上,绿色的方块放置在了 Y 轴,蓝色的方块放置在了 Z 轴。正中间的灰色的方块代表了 原点(origin) ,坐标为 (x:0, y:0, z:0)

Scene Kit 使用 SCNVetor3 这种包含三个向量的数据类型来代表三维空间中的坐标。在代码中我们这样写:

let 位置 = SCNVector3(x:0, y:5, z:10)   

这里,我们使用向量 (x:0, y:5, z:10) 来声明了一个变量 位置 。可以使用下面这种方法来轻松地得到每个坐标的数值:

let x = 位置.x   let y = 位置.y   let z = 位置.z   

如果你以前使用过 CGPoint ,那么你应该可以很容易地看出 SCNVetor3 和它的类似之处。

注:节点(Node)加入到场景的时候有一个默认的位置是 (x:0, y:0, z:0) ,记住这里的位置都是指相对于 父节点 而言的。为了把一个节点放置在指定的位置上,我们需要调整其相对于它的父节点(亦称为本地坐标)的位置,而不是相对于原点(世界坐标)的位置。

摄像机的工作原理

理解了如何在 Scene Kit 中放置节点之后,你可能很好奇如何真正把内容 显示 在屏幕上。想一下第 1 章中关于拍电影的类比:拍电影的时候,场景上有各种演员和道具,然后我们会放置一个摄像机来拍摄场景,所以最终出来的影像什么样取决于摄像机的位置,我们看到的也是摄像机的视角。

Scene Kit 的工作原理也是类似的。包含 摄像机(camera) 的那个节点的位置决定了我们看场景的视角。

下面这个示意图说明了摄像机是如何工作的:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

图中有几个关键术语需要解释一下:

  • 摄像机拍摄的方向永远是其所在节点位置的 负的 Z 轴 方向。
  • 视野(Field of View) 是摄像机的可视区域的极限角度。角度越小,视野越窄,反之,角度越大,视野越宽。
  • 视锥体(viewing frustum) 决定着摄像头可视区域的深度(Z 轴表示深度)。任何不在这个区域内的物体将被剪裁掉(离摄像头太近或者太远),不会显示在最终的画面中。

Scene Kit 中 SCNCamera 这个类就代表了摄像头,它有几个属性,其中 xPovyPov 用来调整视野, zNearzFar 用来调整视锥体。

另外,有一点很关键要记住,那就是摄像头只有附属于某个节点时才发挥作用,只有其本身不会做任何事情。

添加摄像头

现在我们来试试摄像头。打开 GameViewController.swift 这个文件,在 scn场景 的下面再添加一个属性:

var 摄像头节点: SCNNode!   

接下来,在 配置场景() 方法的下面再添加一个新方法:

func 配置摄像头() {     // 1   摄像头节点 = SCNNode()   // 2   摄像头节点.camera = SCNCamera()   // 3   摄像头节点.position = SCNVector3(x: 0, y: 0, z: 10)   // 4   scn场景.rootNode.addChildNode(摄像头节点) } 

我们来看一下这些代码:

  1. 首先创建一个空的节点 SCNNode 并把它赋值给了变量 摄像头节点
  2. 然后创建一个新的 SCNCamera 对象并把它赋值给了 摄像头节点camera 这个属性。
  3. 然后设置 摄像头节点 的位置为 (x:0, y:0, z:10)
  4. 最后,把 摄像头节点 作为子节点加入到场景的根节点中。

然后我们在 viewDidLoad() 这个方法中调用刚刚添加的这个新方法,在 配置场景 这行的下面再添加一行代码:

配置摄像头() 

完成这一步之后并不需要立即运行,因为这时候你看不到什么变化。现在我们的场景中的确添加了摄像头,但是并没有什么东西可供我们拍摄。接下来,为了解决这个问题,让我们添加一些演员到场景来。

了解几何体

为了创建可见的内容,我们需要把几何体添加到节点中。一个几何体代表了一个三维立体的形状,由无数个 交点(Vertices) 构成。

另外,一个几何体可以使用不同的材料来装饰它的表面。 材料(Material) 可以让我们指定物体表面的颜色、材质、对光的敏感度等一系列的视觉效果。一系列交点和材料的组合被称为一个 模型(Model) 或是一个 网(Mesh)

Scene Kit 内置了一系列的几何体,也被称为 原型(Primitives)

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

前排从左到右是:圆锥体(cone)、圆环体(torus)、胶囊体(capsule)、水管体(tube)。后排从左到右分别是:锥体(pyramid)、立方体(box)、球体(sphere)和圆柱体(cylinder)。

你可以提供自定义的几何体,在之后的章节中将会教你如何去做。

添加形状

在把几何体添加进场景之前,我们需要创建一个新的 Swift 文件来声明一个名为 形状 的枚举用来定义游戏中用到的各种不同的几何体形状。

右键单击 GeometryFighter 这个组,然后选择 New File ……选择 iOS/Source/Swift File 这个模版,然后单击 Next

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

把这个文件命名为 形状.swift ,确保其包含在工程文件里,然后单击 Create

文件创建好了之后,打开 形状.swift ,把文件里的内容替换为下列代码:

import Foundation     // 1 public enum 形状:Int {       case 立方体 = 0   case 球体   case 锥体   case 圆环体   case 胶囊体   case 圆柱体   case 圆锥体   case 水管体     // 2   static func 随机() -> 形状 {     let 最大值 = 水管体.rawValue     let 随机 = arc4random_uniform(UInt32(最大值+1))     return 形状(rawValue: Int(随机))!   } } 

上述代码的意思相当直白:

  1. 定义一个公有的枚举 形状 代表不同的几何体类型。
  2. 定义了一个静态的方法 随机() 用来生成随机的形状。这个方法之后会很有用。

添加几何体

下面的任务就是创建一个方法来随机生成 形状 这个枚举中所定义的几何体类型。

在下面的方法添加到 GameViewController.swift 中,就放到 配置摄像头() 的下面:

func 生成几何体() {       // 1     var 几何体: SCNGeometry     // 2     switch 形状.随机() {     default:         // 3         几何体 = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)     }     // 4     let 几何体节点 = SCNNode(geometry: 几何体)     // 5     scn场景.rootNode.addChildNode(几何体节点) } 

下面按照注释的顺序一个个地来解释:

  1. 首先,声明一个变量 几何体 备用。
  2. 接下来,使用一个 switch 语句来处理 形状.随机() 这个方法的返回值。现在这里的代码还不完整,只会生成一个正方体。在文章结尾的挑战中,需要你把它来补充完整。
  3. 然后创建一个 SCNBox 立方体的对象并把它存储在 几何体 中。这里要指定它的宽、高、长,以及斜面角度(也就是所谓的圆角,只不过换了个更高大上的说法)。
  4. 这一行我们创建了一个 SCNNode 的节点的对象并把它命名为 几何体节点 ,这次我们使用的是带 geometry 参数的初始化方法,这样生成的节点会自动跟我们提供的几何体链接起来。
  5. 最后,我们把创建好的几何体节点添加到场景中。

现在,我们需要在程序的某处来调用这个方法。在 viewDidLoad() 中,紧挨着 配置摄像头() 这一行的下面,添加下面的代码:

生成几何体() 

编译并运行;你会发现屏幕中显示了一个白色的方块。如下图所示:

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

这里我们可以看到几点:

  1. 这个立方体节点是 生成几何体 生成的默认的形状,在场景中位于 (x:0, y:0, z:0) 的位置。
  2. 你正在从之前创建的 摄像头节点 所在的位置观察这个场景。由于摄像头节点的位置在 (x:0, y:0, z:10) ,所以这个立方体刚好就处在我们摄像头视野的正中间。

没错,我知道这看起来一点也不令人兴奋(而且看上去也不像是 3D 的),但是别着急——下一节会改变一切。

使用内置的 3D 图形浏览器

SCNView 内置了一些可以直接拿来使用的功能来让我们的工作轻松一些。

来到 GameViewController.swift 这个文件,在 配置视图() 这个方法的最后,增加以下的代码:

// 1 scn视图.showStatistics = true   // 2 scn视图.allowsCameraControl = true   // 3 scn视图.autoenablesDefaultLighting = true   

下面来解释一下上面的代码在干嘛:

  1. showStatistics 会在底部打开一个实时的统计数据的面板。
  2. allowsCameraControl 允许用户使用简单的手势来控制摄像机。
  3. autoenablesDefaultLighting 会自动给我们的场景添加一个全方向的灯光,这样暂时我们就不需要自己来添加灯光了。

编译并运行;这次事情开始变得有趣起来了!

「从零开始」做一个水果忍者那样的游戏(Scene Kit 基础教程)第 2 节:节点

你可以使用以下的手势来控制场景中活动的摄像机:

  • 单手指滑动 :围绕着物体旋转你的摄像机。
  • 两只手指滑动 :上下左右移动你的摄像机。
  • 两只手指缩放 :拉近或拉远你的摄像机。
  • 双击 :如果你的场景中有两个或以上的摄像机,这个动作会在不同的摄像机之间进行切换。当然,在本例中,我们只有一个摄像机,所以不会进行切换。但这个手势在切换的同时也会把摄像机的状态重设回最初的状态。

挑战

在本节的最后,我们留了一个挑战任务来供你实践学习到的知识:完善 生成几何体() 中的 switch 语句,来处理其它的几何体类型。

在苹果官方的 Scene Kit 文档 中查看更多关于不同几何体的说明。同时看看 形状 这个枚举中的各种类型的名字,应该可以给你足够的提示来开始。

不用太担心尺寸的问题;试试让它们看起来跟之前的正方体差不多大小就可以。

如果你完成了挑战,那么恭喜你!你已经牢牢掌握了 Scene Kit 中最基本的一些概念了!

接下来怎么办?

这里是 完成挑战后的代码(链接待更新) 。

就目前而言,您应该继续阅读本系列教程的第三节(待更新),在第三节中,我们将学习如何使用物理引擎让几何体动起来。

本节视频教程地址(链接待更新)

免费直播教程:

  • 2016/04/26(周二)19:30-21:30(已结束)
  • 2016/05/03(周二)19:30-21:30(已结束)
  • 2016/05/10(周二)19:30-21:30 直播地址(待更新)

译者注:如果您刚开始学习 iOS 开发,希望系统地学习并得到及时的指导,可以考虑来购买我的视频教程: 「从零开始」学习 3D iOS游戏编程

这一教程基于本文提及的 《3D iOS 游戏教程》 这本书,将会教给你制作 3D iOS 游戏中所需要知道的一切知识,在这个过程中你将从头开始制作一系列类似于水果忍者、经典的敲砖块,甚至苹果 WWDC 上和 Apple TV 一起首发的游戏 —— Crossy Road 。

于此同时,如果您有任何问题,欢迎在微博上@方一雄 给我留言或私信。

原文  http://iosinit.com/scenekit-02/
正文到此结束
Loading...