前面两篇主要在做一些准备工作, 这一篇就有趣了, 开始搭建游戏场景.
我们在之前已经在game scene创建了一个巨大的草坪, 现在我们要补上其余的细节: 两条拥挤的高速公路, 漂亮的树, 和我们的主角猪大哥.
添加主角
首先先来添加我们的主角Mr. Pig到game scene, 这样有助于将其作为参照物来放置其他的物体.
首先选中MrPig.scnassets/ GameScene.scn, 在Media Library找到Mr. Pig reference, 将其拖动到scene, 并确保Position跟Euler都为0;然后到ViewController.swift, 添加这条属性, 用来持有Mr.Pig的实例:
var pigNode: SCNNode!
添加下面这行到setupNodes():
pigNode = gameScene.rootNode.childNode(withName: "MrPig", recursively:
true)!
这个方法会在GameScene.scn加载完成后调用, 所以是一个安全的方法来绑定game scene里实际的对象到pigNode.
(虽然Mr. Pig是一个reference node, 你仍可以在game scene中像正常node一样访问它)
设置镜头和灯光
跟前面一样, 这里仍使用"自拍杆"原则, 即通过创建empty node来作为camera 或 light的焦点 - 这样可以简化很多数学问题, 你应该不会介意的. :]
创建跟随镜头
返回MrPig.scnassets/GameScene.scn, 首先确保Scene graph上没有东西被选中, 然后点左下角 + 创建empty node. 将其命名为Follow Camera, Position值为0, Euler:(x:-45, y:20, z:0).然后把之前的camera拖动到新建的Follow Camera下面, 将camera的Position设置为(x:0, y:0, z:14), Euler为0;
点击Perspective 选中camera, 你的视角应该是上图的样子.
选中ViewController.swift, 添加这两个属性:
var cameraNode: SCNNode!
var cameraFollowNode: SCNNode!
这用来持有camera和Follow Camera的实例
添加下面代码到setupNodes():
// 1 将camera绑定到cameraNode
cameraNode = gameScene.rootNode.childNode(withName: "camera", recursively: true)!
// 2 将hud作为子node添加给camera, 这样玩家可以始终看到游戏内需要展示的信息
cameraNode.addChildNode(game.hudNode)
// 3 将Follow Camera绑定到cameraFollowNode, 这样你仅仅需要更新cameraFollowNode的position就可以使我们的镜头时刻跟随猪大哥
cameraFollowNode = gameScene.rootNode.childNode(withName: "Follow Camera", recursively: true)!
创建跟随光源
这里会用到和上面镜头差不多的步骤, 我们需要两个基础光源, 一个环境光来填充, 一个平行光来模拟阳光照射和它产生的阴影.
首先确保Scene graph没有东西被选中, 点击左下 + , 命名为Follow Light并将Position 和 Euler 设置为0.
从Object Library拖动Ambient Light和Directional Light到Follow Light下, 选中ambient, 确保Position 和 Euler 均为0, 点击Attributes Inspector, 颜色选择Aluminum, 这会让整个场景稍微亮一些.因为阴影是个相对比较消耗性能的操作, 所以你并不想在玩家看不到的地方计算阴影.
接下来是一些代码工作, 将下面属性添加到ViewController.swift
var lightFollowNode: SCNNode!
这用来持有FollowLight的实例
添加下面的代码到setupNodes():
lightFollowNode = gameScene.rootNode.childNode(withName: "Follow Light", recursively: true)!
这将Follow Light与lightFollowNode绑定, 你可以将lightFollowNode的position设置为与Follow Camera相同来使玩家可视区域投射出漂亮的阴影.
command + R来验证一下你的成果:
看起来像一个阳光明媚的好天气, 注意一下你的阴影, 简直闻到了烤肉的香气 :]
添加高速公路与车流
首先, 确保你的game scene的scene graph没东西被选中, 点击左下角 + 添加empty node, 将其命名为Highway. 这个node用来作为两条高速公路的容器node来使用.
然后在Media Library拖动两个Road到Highway下, 选中第一个, 设置Position:(x:0, y:0, z:-4.5), Euler为0.
选中第二个, 设置Position:(x:0, y:0, z:-11.5), Euler : 0.(我这里Xcode又出了问题, scene editor的马路被阳光照射成了白色, 但实际运行起来是ok的, Xcode的bug真是多啊)
添加车流
跟前面类似, 创建一个empty node并命名为Traffic作为容器, 然后从Media Library拖动Bus到Traffic下, 并设置position : (x:0, y:0, z:-4), Euler: (x:0, y:-90, z:0).
完美! 接下来添加下面属性到ViewController.swift,
var trafficNode: SCNNode!
因为路上车会很多, 不需要给每个引用添加一个属性, 而是通过这个属性来持有所有车辆.
添加下面代码到setupNodes():
trafficNode = gameScene.rootNode.childNode(withName: "Traffic", recursively: true)!
这会绑定Traffic到trafficNode, 你可以访问它所有的子node来是车辆动起来.
下面是挑战内容, 你可以尝试自己添加很多很多很多的车辆来使公路变得热闹起来. 当你完成时差不多应该是这样的:
添加时你应该注意如下事项:
- 确保道路左侧为大巴路线, 右侧为跑的更快的小车.
- 确保两条公路的车流方向是相反的
- 摆放车的位置时记着按着command键, 这样可以确保位置都为整数.
- 当你完成一条公路时, 可以选中路上的所有车, 然后按着option键的同时拖动到另外一跳公路上, 这样你可以将所有的车复制过去, 然后, 修改Euler 180°即可将其朝向另一个方向.
- 同样的, 修改旋转角度时长按command键可以使角度都为整数.
当你完成后, command + R来验证一下你的成果.
添加树
目前看起来像是有人在足球场上建了两条路, 我们要的效果是在公园里有两条路, 所以现在要加树 :]
接下来你要添加两组不同的树: 树线, 和 树组... 树线用来勾勒整个公园的边界, 树组用来装饰, 使足球场变公园.
创建树线TreeLine
比起一棵棵的栽树, 你会采用一个更聪明的方法: 创建一个由很多树构成的可复用的scene, 然后在game scene把这些作为reference node来使用.
首先在File Template Library拖动一个SceneKit Scene File到Project Navigator. 将其命名为TreeLine, 并放在MrPig.scnassets目录下. 选中, 删除里面自带的camera因为你不会用到, 在里面再创建一个empty node 并命名为TreeLine. 然后按照下面的模式来搭建TreeLine:这个表是啥意思呢? 每个白色方块代表一棵树的位置.
- 行: 代表树Position的x值
- 列: 代表树Position的z值
- S: SmallTree reference node
- M: MediumTree reference node
-
L: LargeTree reference node
举个例子, 第一行第一个, 需要从Media Library拖动一个SmallTree到scene, 并设置其position 为 (x:-5, y:0, z:-1). y = 0确保其一直在草地的表面.
你可以通过长按option + command并拖动相应的node来加速你的工作.
当你完成的时候应该看起来是这样的:
创建树组TreePatch
这根上面几乎是完全一样的, 除了模式换成了这样的:当你完成时应该看起来是这样:
添加数线
终于, 我们的reference node准备完毕, 是时候把它们扔进game scene了.
选中GameScene.scn并确保Scene graph上没有东西被选中, 创建一个empty node命名为Trees. 接下来从Media Library 拖动一个TreeLine到scene, 然后开始复制并调整位置, 直到你的game scene看起来差不多这个样子:
我个人习惯按着option + command一个一个拖动来布局, 当然, 你也可以直接复制一堆TreeLine, 然后在Node Inspector直接修改Position, 如果你选择后者, 可以这样修改:
猪先生前面
• Position:(x:0,y:0,z:7),Euler:(x:0,y:0,z:0).
• Position:(x:-7, y:0, z:3), Euler: (x:0, y:90, z:0).
• Position:(x:7,y:0,z:3),Euler:(x:0,y:90,z:0).
• Position:(x:-14,y:0,z:-1),Euler:(x:0,y:0,z:0).
• Position:(x:14,y:0,z:-1),Euler:(x:0,y:0,z:0).
道路两侧
• Position:(x:-14,y:0,z:-8),Euler:(x:0,y:0,z:0).
• Position:(x:14,y:0,z:-8),Euler:(x:0,y:0,z:0).
后面
• Position:(x:18,y:0,z:-19),Euler:(x:0,y:90,z:0).
• Position:(x:-18,y:0,z:-19),Euler:(x:0,y:90,z:0).
• Position:(x::-11,y:0,z:-23),Euler:(x:0,y:0,z:0).
• Position: (x:0, y:0, z:-23), Euler: `(x:0, y:0, z:0).
• Position:(x:11,y:0,z:-23),Euler:(x:0,y:0,z:0).
添加树组
这个就简单多了, 只有三个
• Position:(x:10,y:0,z:-17),Euler:(x:0,y:0,z:0).
• Position:(x:-10,y:0,z:-17),Euler:(x:0,y:0,z:0).
• Position:(x:0,y:0,z:-17),Euler:(x:0,y:90,z:0).
完成后大概是这样的:
最后, 再次确认你的树node都在Trees容器node下面.
添加金币
听说都爱金币 :] , 所以现在我们要添加些金币到我们的game scene.
先创建一个名为Coins的empty node, 然后从Media Library拖动一个coin到里面, 再复制3个, 设置位置如下:
• Position:(x:0,y:0.5,z:-8).
• Position:(x:0,y:0.5,z:-21).
• Position:(x:-14,y:0.5,z:-20).
• Position:(x:14,y:0.5,z:-20).
完美! 最后comman + R来验证一下本章的成果.
现在, 你有一个漂亮的启动页面Splash Scene, 然后有个漂亮的转场动画让你的Mr.Pig出现在你刚搭建的有着拥挤车流的漂亮的公园, 一切都是那么的漂亮. 应该给自己一个掌声了.
然而, 我们猪大哥跟车流都是静止的, 如何让他们动起来呢? 我们下篇见!
目录 (不定期更新中 :] )
1 准备工作、创建项目、Splash Scene
2 过场动画 Transition
3 搭建游戏场景 Game Scene
4 用场景编辑器添加动作
5 用代码添加动作