其他

SceneKit框架详细解析(六) —— 基于SceneKit的

2018-10-20  本文已影响46人  刀客传奇

版本记录

版本号 时间
V1.0 2018.10.20 星期六

前言

SceneKit使用高级场景描述创建3D游戏并将3D内容添加到应用程序。 轻松添加动画,物理模拟,粒子效果和逼真的基于物理性的渲染。接下来这几篇我们就详细的解析一下这个框架。感兴趣的看下面几篇文章。
1. SceneKit框架详细解析(一) —— 基本概览(一)
2. SceneKit框架详细解析(二) —— 基于SceneKit的简单游戏示例的实现(一)
3. SceneKit框架详细解析(三) —— 基于SceneKit的简单游戏示例的实现(二)
4. SceneKit框架详细解析(四) —— 基于SceneKit的简单游戏示例的实现(三)
5. SceneKit框架详细解析(五) —— 基于SceneKit的简单游戏示例的实现(四)

开始

在这部分中,您将为游戏添加一些很酷的粒子系统并将其包装起来!

想象自己在电影院里手持爆米花;在屏幕上,一个来自“速度与激情”的坏人将他咆哮的高性能高速赛车撞到一辆高度不稳定的油轮上,这辆油轮在一场巨大的死亡和大屠杀火球中爆炸。

现在,想想同样的场景,但没有大规模的火球爆炸。 你几乎可以感受到世界各地观众的集体失望。

就像好莱坞大片一样,你的游戏需要特效来提升兴奋程度。 这些特殊效果以所谓的粒子系统(particle systems)的形式出现。 您可以使用粒子系统来实现多种效果,从移动星空,燃烧火箭助推器,到雨雪,到金属火花 - 甚至是大型爆炸火球!

现在是时候看看如何将这些整洁的特效添加到Geometry Fighter中。


Particle Systems - 粒子系统

SceneKit中,SCNParticleSystem管理场景图中粒子的创建,动画和移除。

粒子本质上是一个小图像精灵。粒子系统不会将单个粒子添加到场景图本身中,因此您无法直接访问每个粒子;粒子系统管理粒子,包括它们的外观,大小和位置。

但是,您可以通过修改粒子系统上的各种属性来影响粒子系统,例如:

1. Creating a Trail Particle System - 创建跟踪粒子系统

在将粒子系统添加到游戏世界之前,您需要一个组来容纳此粒子系统以保持项目的有序性。 右键单击GeometryFighter组并选择New Group,如下所示:

将此新组命名为Particles。 右键单击该组,然后选择New File。 选择iOS/Resource/SceneKit Particle System,然后单击Next继续:

在下一个屏幕上,选择Particle system templateFire,然后单击Next。 将文件另存为Trail.scnp,然后单击Create。 完成后,您应该在场景中看到以下内容:

以下是上面注释的编辑器各个部分的快速概述:

2. Configuring the Trail Particle System - 配置跟踪粒子系统

在本节中,您将深入了解编辑器右侧的粒子系统属性。 在浏览每个部分时,将屏幕截图中每个设置的值复制到您自己的粒子系统中。

在更改每个属性时,请密切关注粒子系统编辑器;您将看到每个参数如何影响粒子系统的行为。 稍后,您将在游戏中使用此粒子效果来创建从生成的对象落下的粒子痕迹。

3. Emitter Attributes - 发射器属性

粒子发射器是所有粒子产生的原点。 以下是发射器属性:

注意:请注意,某些属性有两个输入区域,其中一个区域旁边有一个Δ=符号(请参阅Birth Rate and Initial angle)。第一个输入区域包含基值,Δ=输入区域包含delta值。每次生成粒子时,它都使用基值加上范围内的随机值(-delta值,+ delta值)。这允许您为这些属性获得一些随机方差。

4. Simulation Attributes - 模拟属性

模拟属性管理粒子在其生命周期内的运动。 这使您无需使用物理引擎即可管理其移动:

5. Image Attributes - 图像属性

图像属性控制粒子的视觉方面。它们还控制着这些粒子的外观在其生命周期中如何变化:

6. Image Sequence Attributes - 图像序列属性

要为粒子创建动画图像,可以将动画的每个帧排列到单个图像上的网格中(如游戏中的精灵表)。然后,您只需将该网格图像用作粒子发射器的图像。使用图像序列属性可以控制粒子的基本动画属性:

7. Rendering Attributes - 渲染属性

渲染属性定义渲染阶段如何处理粒子:

8. Physics Attributes - 物理属性

物理属性允许您指定粒子在物理模拟中的行为方式:

9. Life Cycle Attributes - 生命周期属性

生命周期属性可让您控制粒子系统的整个生命周期:

对于单个粒子系统,需要考虑很多属性,但这可以让您获得很多控制以获得您正在寻找的特别的特效。

如果您努力将屏幕截图中的值复制到粒子系统,您将拥有一个代表以下效果的系统:

如果您的不是这样,请尝试旋转相机。 它也可能有助于将背景颜色更改为深蓝色,就像您在此处看到的那样,可以更容易地看到粒子系统。

现在终于可以为游戏添加炫酷粒子效果了。 将以下内容添加到GameViewController.swift类:

// 1
func createTrail(color: UIColor, geometry: SCNGeometry) -> 
  SCNParticleSystem {
  // 2
  let trail = SCNParticleSystem(named: "Trail.scnp", inDirectory: nil)!
  // 3
  trail.particleColor = color
  // 4
  trail.emitterShape = geometry
  // 5
  return trail
}

以下是上面发生的事情:

此方法可帮助您创建SCNParticleSystem的实例,但仍需要将粒子系统添加到生成的形状对象。

请注意,createTrail(_:geometry :)接受一个颜色参数并使用它来为粒子着色。 您将粒子系统的颜色设置为与形状的颜色相同。

spawnShape()中找到在其中设置形状的材质漫反射内容的那一行,然后将其拆分,使随机颜色存储在常量中,如下所示:

let color = UIColor.random()
geometry.materials.first?.diffuse.contents = color

接下来,在将力强加到geometryNode的物理体之后,在spawnShape()中进一步向下添加以下行:

let trailEmitter = createTrail(color: color, geometry: geometry)
geometryNode.addParticleSystem(trailEmitter)

这使用createTrail(_:geometry :)创建粒子系统并将其附加到geometryNode

构建并运行,看看你的辛勤工作!

它看起来很棒 !


Heads-up Displays

在这个简短的部分中,您将使用Game Utils快速添加一个小小的抬头显示器来显示您的玩家的剩余生命,最佳分数和当前分数。 幕后代码使用SpriteKit标签,并使用标签的输出作为平面的纹理。 这是一种强大的技术!

将以下新属性添加到gamewnController.swift,就在spawnTime下面:

var game = GameHelper.sharedInstance

这使您可以快速访问GameHelper共享实例,该实例包含一组方法来为您完成繁重的工作。

将以下方法添加到GameViewController的底部,在createTrail()下面:

func setupHUD() {
  game.hudNode.position = SCNVector3(x: 0.0, y: 10.0, z: 0.0)
  scnScene.rootNode.addChildNode(game.hudNode)
}

在这里,您可以使用帮助程序库中的game.hudNode。 您设置HUD节点的位置并将其添加到场景中。

接下来,您需要从某个地方调用setupHUD()。 将以下行添加到viewDidLoad()的底部:

setupHUD()

现在你有一个抬头显示器,你需要保持它是最新的。 将以下调用game.updateHUD()添加到到renderer(_: updateAtTime:)的底部。

game.updateHUD()

构建并运行,您将在屏幕顶部看到您的显示,如下所示:

你的游戏现在有一个漂亮的小HUD,有生命计数器,高分和当前分数。

好的,抬头显示很好,但现在是时候为你的游戏添加一些互动了。


Touch Handling - 触摸处理

通常情况下,在您的应用中启用触摸并不像人们希望的那样简单。

第一步是了解触摸处理在3D中的工作原理。 下图显示了场景侧视图中的触摸点以及SceneKit如何将该触摸点转换为3D场景以确定您正在触摸的对象:

那么您采取了哪些步骤来处理用户的触摸事件?

1. Naming Nodes - 命名节点

在激活触摸死亡之前,您需要一种方法来识别每个衍生对象。 最简单的方法是给他们起名字。

将粒子系统添加到geometryNode后立即将以下内容添加到spawnShape()

if color == UIColor.black {
  geometryNode.name = "BAD"
} else {
  geometryNode.name = "GOOD"
}

秉承古老西方电影黑帽恶魔的精神,你将绰号BAD分配给黑色物体,并将GOOD分配给所有其他物体。

2. Adding Touch Handling - 添加触摸处理

接下来,您需要编写一个方法,以便在检测到用户已触发特定节点时稍后调用该方法。

将以下方法添加到GameViewController的底部,在setupHUD()下面:

func handleTouchFor(node: SCNNode) {
  if node.name == "GOOD" {
    game.score += 1
    node.removeFromParentNode()
  } else if node.name == "BAD" {
    game.lives -= 1
    node.removeFromParentNode()
  }
}

该方法检查被触摸节点的名字;良好的节点会增加分数,而糟糕的(黑色)节点会将生命数量减少一个。 在任何一种情况下,您都会从屏幕中删除节点,因为它已被销毁。

3. Using the Touch Handler - 使用触控处理程序

要捕获用户的触摸,您将使用touchesBegan(_:withEvent :),每次玩家触摸屏幕时都会调用它。

要实现您自己的版本,请在GameViewControllerhandleTouchFor(_ :)下面添加以下内容:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  // 1
  let touch = touches.first!
  // 2
  let location = touch.location(in: scnView)
  // 3
  let hitResults = scnView.hitTest(location, options: nil)
  // 4
  if let result = hitResults.first {
    // 5
    handleTouchFor(node: result.node)
  }
}

下面进行细分:

最后一步。 您不再需要摄像头控制,因此请更改setupView()中的行,如下所示:

scnView.allowsCameraControl = false

构建并运行,准备好释放你的手指!

点击产生的物体,使它们分解在稀薄的空气中。


Challenge - 挑战

是时间到很酷的因素 - 什么比爆炸更酷? 绝对没有,对吧?

这将带您接受本教程的挑战,即创建另一个粒子系统并将其命名为Explode.scnp。 看看你是否可以找出要修改的属性以使这些粒子爆炸。

效果看起来应该与此类似:

您可以使用以下图像作为粒子系统的起点:


Shaping Particle Explosions - 塑造粒子爆炸

现在您已经创建了爆炸粒子系统,您需要添加一些代码来使这些节点爆炸。 您将使用一些特殊属性来使爆炸与您触摸的任何节点具有相同的形状。

将以下内容添加到GameViewController的底部,在touchesBegan(_:withEvent)下面:

// 1
func createExplosion(geometry: SCNGeometry, position: SCNVector3,
  rotation: SCNVector4) {
  // 2
  let explosion =
    SCNParticleSystem(named: "Explode.scnp", inDirectory:
  nil)!
  explosion.emitterShape = geometry
  explosion.birthLocation = .surface
  // 3
  let rotationMatrix =
    SCNMatrix4MakeRotation(rotation.w, rotation.x,
      rotation.y, rotation.z)
  let translationMatrix =
    SCNMatrix4MakeTranslation(position.x, position.y, 
      position.z)
  let transformMatrix =
    SCNMatrix4Mult(rotationMatrix, translationMatrix)
  // 4
  scnScene.addParticleSystem(explosion, transform: transformMatrix)
}

以下是上述代码的细分:

你是如此接近复制那些伟大的好莱坞爆炸! 在handleTouchFor(_ :)中两次添加以下行 - 一次到“good”if块,一次到“bad”else块,就在从父节点移除节点之前:

createExplosion(geometry: node.geometry!,
  position: node.presentation.position,
    rotation: node.presentation.rotation)

这使用presentation属性来检索节点的positionrotation参数。 然后使用这些参数调用createExplosion(_:position:rotation :)

注意:您正在使用presentation,因为物理模拟当前正在移动节点。

构建并运行,点击,让这些节点爆炸!

注意:您可以在projects / challenge / GeometryFighter文件夹下找到此挑战的项目。


Adding Juice

你的游戏尚未完成;还有很大的改进空间,对吧?要将游戏推向更高级别,您必须添加一些称为juice的东西。juice会给你的游戏带来一些特别的东西,只是为了让它在其余部分中脱颖而出。

这里有一些想法肯定会让事情变得更好:

后记

本篇主要讲述了基于SceneKit的简单游戏示例的实现,感兴趣的给个赞或者关注~~~

上一篇 下一篇

猜你喜欢

热点阅读