概述

SceneKit和SpriteKit的区别简单的来说就是二维和三维的区别

详细

代码下载:http://www.demodashi.com/demo/10664.html

上周一, 相信很多人和我一样, 全程观看了WWDC2017的开发者大会, 其中虽然亮点平平但也能些许的看出苹果未来的战略, 虽然已经从先驱者变成跟随者, 但强者恒强的道理是亘古不变的真理, 而且在生态链的建设上也是无人能出其右, 虽然在消费者眼中最为关注的是HomePod和iPad Pro10.5, 而在开发者眼中为之眼前一亮的则是ARKit和Core ML.

一、了解SceneKit

Core ML 刚发布的时候还以为是终于能用Swift进行模型的训练了, 终于不用学习缩进地狱的Python了, 然而这仅仅是一个类似适配器一样的东西, 把通过机器学习训练完成后的模型通过.py转换器转换成Core ML的格式并进行集成到Apple Devices中, 诶, 没意思...

ARKit 的出现能够看到苹果对扩展现实技术未来推动的决心, 的确如会上所说, ARKit凭借众多的移动设备一跃成为了最大的AR开发平台, 在会上的Demo也做的栩栩如生, 但对于ARKit, 并不是直接就能够学习的, 需要一些基础的知识, 比如跨平台的Unity, 可是并没有做过游戏开发的我, 新学一门语言虽然并非难事, 但要学个大概也并非易事啊!! 还好ARKit也支持SceneKit和SpriteKit, 诶... 亲儿子嘛, 所以为了学习之后的ARKit, 先学习下SceneKit打好基础吧~

SceneKit 基本概念

SCNVector3:

果然三维的向量, 苹果创建了一个有三个属性的结构体, 这也是意料之中的事情, 对于向量的理解就是方向加上速度, 还有毕达哥拉斯定理的应用也是非常重要的一部分.

public struct SCNVector3 {public var x: Floatpublic var y: Floatpublic var z: Floatpublic init()public init(x: Float, y: Float, z: Float)
}

有了之前学习SpriteKit的经验, 现在对于游戏世界的概念也变的清晰了起来, SceneKit和SpriteKit的区别简单的来说就是二维和三维的区别, 现在我们就要对Z轴的概念需要有更深的理解了.

SCNCamera

摄像头的概念和之前的SKCamera还是有歇息不同的, 好歹也是个三维的摄像头, 更加真实的体现了拍电影时机位的特点, 360°无死角的拍摄, 比较能够符合这个概念吧.

var cameraNode: SCNNode!func setupCamera() {cameraNode = SCNNode()cameraNode.camera = SCNCamera()cameraNode.position = SCNVector3(x: 0, y: 5, z: 10)scnScene.rootNode.addChildNode(cameraNode)}

Camera和SpriteKit中的概念相同但形式不同, 图中zNear和zFar就能了解到机位的概念, 代码中的position是指机位上调5个单位, 向后调10个单位.

SCNGeometry

三维几何图形, 这些几何图形都是系统框架内自带的, 当然后期可能会有其他方法进行自定义几何图形, 这些几何图形, 如果你了解CALayer的子类CAShapeLayer你就能够了解到, 只不过是二维和三维的区别了.

func spawnShape() {var geometry: SCNGeometryswitch ShapeType.random() {case .box:geometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)case .sphere:geometry = SCNSphere(radius: 0.5)case .pyramid:geometry = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)case .torus:geometry = SCNTorus(ringRadius: 0.5, pipeRadius: 0.25)case .capsule:geometry = SCNCapsule(capRadius: 0.3, height: 2.5)case .cylinder:geometry = SCNCylinder(radius: 0.3, height: 2.5)case .cone:geometry = SCNCone(topRadius: 0.25, bottomRadius: 0.5, height: 1.0)case .tube:geometry = SCNTube(innerRadius: 0.25, outerRadius: 0.5, height: 1.0)}let color = UIColor.random()geometry.materials.first?.diffuse.contents = colorlet geometryNode = SCNNode(geometry: geometry)geometryNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)let randomX = Float.random(min: -2, max: 2)let randomY = Float.random(min: 10, max: 18)let force = SCNVector3(x: randomX, y: randomY , z: 0)let position = SCNVector3(x: 0.05, y: 0.05, z: 0.05)geometryNode.physicsBody?.applyForce(force, at: position, asImpulse: true)let trailEmitter = createTrail(color: color, geometry: geometry)geometryNode.addParticleSystem(trailEmitter)if color == UIColor.black {geometryNode.name = "BAD"game.playSound(scnScene.rootNode, name: "SpawnBad")} else {geometryNode.name = "GOOD"game.playSound(scnScene.rootNode, name: "SpawnGood")}scnScene.rootNode.addChildNode(geometryNode)}

其中SCNBox, SCNSphere, SCNPyramid, SCNTorus, SCNCapsule, SCNCylinder, SCNCone, SCNTube对应这上图中的八个几何图形, 各自的初始化方法就是对于长宽高及弧度的属性设置.

  • geometry.materials.first?.diffuse.contents = color 表示这对几何图形的内容进行赋值

  • geometryNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil) 和SpriteKit一样 创建三维的物理体

  • geometryNode.physicsBody?.applyForce(force, at: position, asImpulse: true) 对物理体申请力及脉冲力

  • geometryNode.addParticleSystem(trailEmitter) 添加粒子系统

SceneKit 渲染周期

所谓的渲染周期, 就是在每一帧系统会做哪些时期, 当然不是很理解也没关系, 我们可以用生命周期距离, UIKit中当压入栈的时候, 会出发生命周期, 从开辟到销毁会经历好几个方法, 当然渲染周期也是类似.

SceneKit在每一帧渲染的时候会经历图上九个过程:

1.更新:视图在其代理方法中进行渲染(:updateAtTime :)。 一般写一些基本的更新逻辑.

2.执行操作和动画:SceneKit执行所有操作并执行所有连接的动画到场景图中的节点。

3.应用动画:视图调用其代理的渲染器(:didApplyAnimationsAtTime :)。在这一点上,场景中的所有节点都基于应用的动作和动画完成了一帧的动画。

4.模拟物理:SceneKit将物理模拟的一个步骤应用于场景中的所有物理体。

5.完成模拟物理:视图在其委托上调用渲染器(:didSimulatePhysicsAtTime :)。在这一点上,物理模拟步骤已经完成,您可以添加任何取决于上面应用的物理学的逻辑。

6.评估约束:SceneKit评估和应用约束,这是可以配置的规则,使SceneKit自动调整节点的转换。

7.将要渲染场景:视图在其委托上调用渲染器(:willRenderScene:atTime :)。在这一点上,视图即将呈现场景,所以在这里应该执行最后一分钟的更改。

8.渲染场景视图:SceneKit渲染视图中的场景。

9.渲染场景完成:最后一步是让视图调用其代理渲染器(_:didRenderScene:atTime :)。这标志着渲染循环的一个循环的结束;您可以将任何游戏逻辑放在这里,需要在进程重新启动之前执行。

SceneKit 粒子系统

如果了解过CALayer的子类的话, 就可能会知道有一个粒子的Layer, 相信做过直播项目的同学们都有所了解, 其实技术做久了就会发现好多的东西概念都是相通的, 仅仅是属性和方法名不同罢了, 所以我认为做技术的朋友, 记住API本身是没有什么意义的, 毕竟现在谁开发能离开Google和Baidu, 对于技术来讲, 设计模式, 数据结构及算法, 才是技术进阶的根基, 所以现在我不再强调API了, 而是更加强调概念.

  • 出生率:控制粒子的排放率。将其设置为25,指示粒子引擎以每秒25个粒子的速率产生新的粒子。

  • 预热持续时间:在渲染粒子之前模拟运行的秒数。这可以用于在开始时显示充满颗粒的屏幕,而不是等待颗粒填充屏幕。将其设置为0,以便从一开始就可以观察到模拟。

  • 位置:相对于形状,发射器产生其粒子的位置。将其设置为顶点,这意味着粒子将使用几何顶点作为产生位置。

  • 排放空间:发射的颗粒将驻留的空间。将其设置为世界空间,以便将发射的粒子发射到世界空间,而不是对象节点本身的本地空间。

  • 方向模式:控制如何产生的粒子行进;您可以将它们全部移动到恒定的方向,让它们从形状的表面径向向外移动,或者简单地将它们随机移动。将其设置为Constant,保持所有发射的粒子沿恒定方向移动。

  • 方向:指定方向模式不变时使用的初始方向矢量。将此矢量设置为(x:0,y:0,z:0),将方向设置为无。

  • 传播角度:随机产生的粒子的发射角度。将其设置为0°,从而在以前设置的方向上精确地发射颗粒。

  • 初始角度:发射粒子的初始角度。将其设置为0°,因为这与零向矢量无关。

  • 形状:发射粒子的形状。将形状设置为“球形”,从而使用球形作为几何体。

  • 形状半径:此属性的存在取决于您使用的形状; 对于球形发射器,这决定了球体的大小。 将其设置为0.2,它定义了足够大的球体,满足您的需要。

  • 使用寿命:指定粒子的寿命(以秒为单位)。 将其设置为1,因此单个粒子将只存在一秒钟。

  • 线速度:指定发射粒子的线速度。 将其设置为0,以便粒子不会产生方向或速度。

  • 角速度:指定发射的粒子的角速度。 将其设置为0,以便颗粒不会旋转。

  • 加速度:指定施加到发射粒子的力矢量。 将它设置为(x:0,y:-5,z:0) - 它是一个向下的向量 - 一旦产生,就模拟颗粒上的软重力效应。

  • 速度因子:设定粒子模拟速度的乘数。 将其设置为1以正常速度运行模拟。

  • 拉伸因子:在其运动方向上延伸颗粒的乘数。 将其设置为0以不展开粒子图像。

  • 图像:指定要渲染每个粒子的图像。 选择CircleParticle.png图像,给出粒子的主要形状。

  • 颜色:设置指定图像的色调。 将颜色设置为白色,给出粒子系统的基本颜色为白色。

  • 动画颜色:使粒子在其使用寿命期间变色。 取消选中此项,因为粒子颜色根本不会改变。

  • 颜色变化:为粒子颜色添加一点随机性。 您可以将其设置为(h:0,s:0,b:0,a:0),因为粒子颜色不会改变。

  • 大小:指定粒子的大小。 将其设置为0.1,使发射的颗粒尺寸小。

  • 初始帧:设置动画序列的第一个基于零的帧。 第零帧对应于网格中的左上角图像。 您正在使用单帧图像,因此将其设置为0。

  • 帧率:以秒为单位控制动画的速率。 将其设置为0,因为这仅适用于使用包含多个帧的图像时。

  • 动画:指定动画序列的行为。 重复循环动画,Clamp仅播放一次,Auto Reverse从开始到结束播放,然后再次播放。 你可以把它放在重复上,因为在使用单帧图像时并不重要。

  • 维度:指定动画网格中的行数和列数。 由于您使用单帧图像,请将其设置为(行:1,列:1)。

  • 混合:指定在将粒子绘制到场景中时渲染器的混合模式。 将其设置为Alpha,将使用图像Alpha通道信息进行透明度。

  • 方向:控制颗粒的旋转。 将其设置为Billboard屏幕对齐,这将始终保持平面微粒面向相机视图,因此您不会注意到颗粒确实是平面图像。

  • 排序:设置粒子的渲染顺序。 此属性与混合模式配合使用,并影响如何应用混合。 将其设置为无,因此粒子系统将不会使用排序。

  • 照明:控制SceneKit是否将照明应用于颗粒。 取消选中此项,以便粒子系统忽略场景中的任何指示灯。

  • 受重力影响:使场景的重力影响颗粒。 取消选中此项,因为您不希望粒子系统参与物理模拟。

  • 受物理场影响:造成场景内的物理场影响粒子。 取消选中此项,因为您不希望物理字段对粒子产生影响。

  • 死于冲突:使您的场景中的物理体碰撞并破坏粒子。 取消选中此项,因为您不想在与场景中的节点对象冲突时删除粒子。

  • 物理属性:在物理模拟过程中控制粒子物理行为的基本物理属性。 您可以将所有这些保留为默认值,因为粒子系统将不会使用它们。

  • 发射持续时间:控制发射器发射新颗粒的时间长度。 将其设置为1,这将激活粒子发射器,总长度为1秒。

  • 空闲持续时间:循环粒子系统在指定的发射持续时间内发射粒子,然后在指定的空闲持续时间内空转,之后循环重复。 将其设置为0,因此粒子系统将仅发射一次。

  • 循环:指定粒子系统是否像爆炸一样发射粒子,或像火山一样持续发射粒子。 将其设置为循环,以使发射器在再次从场景中移除之前尽可能长的发射。

二、SceneKit 实战演练

SceneKit 实战演练

我们今天所要实现的是一个类似水果忍者的游戏, 从底部发射出一些几何模块, 点击赚取分数, 当点到黑色的时候就会被扣除一条命. 根据我们刚刚所学的知识, 我们就能够实现出这样一个3D游戏, 就当入门吧!

Step1 场景设置

override func viewDidLoad() {super.viewDidLoad()setupView() //添加ViewsetupScene() //添加场景setupCamera() //添加摄像头setupHUD() //添加文字setupSplash() //添加图片setupSounds() //添加声音}func setupView() {scnView = self.view as! SCNView//scnView.showsStatistics = true//scnView.allowsCameraControl = falsescnView.autoenablesDefaultLighting = truescnView.delegate = selfscnView.isPlaying = true}func setupScene() {scnScene = SCNScene()scnView.scene = scnScenescnScene.background.contents ="GeometryFighter.scnassets/Textures/Background_Diffuse.png"}

Step2 逐帧渲染

extension GameViewController: SCNSceneRendererDelegate {func renderer(_ renderer: SCNSceneRenderer, updateAtTime time:TimeInterval) {if game.state == .Playing { //当时可玩状态时if time > spawnTime { 进行生产几何模型速度的时间调节spawnShape()spawnTime = time + TimeInterval(Float.random(min: 0.2, max: 1.5))}cleanScene() //删除场景内节点}game.updateHUD() //更新文字表述}
}func cleanScene() {for node in scnScene.rootNode.childNodes {if node.presentation.position.y < -2 { 当几何图形到某个位置的时候 删除节点node.removeFromParentNode()}}}

Step3 用户交互

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {if game.state == .GameOver {return }if game.state == .TapToPlay {game.reset()game.state = .PlayingshowSplash(splashName: "")return}let touch = touches.firstlet location = touch!.location(in: scnView)let hitResults = scnView.hitTest(location, options: nil) //获取触碰到的节点if let result = hitResults.first {if result.node.name == "HUD" || //根据节点名字判断执行业务逻辑result.node.name == "GAMEOVER" ||result.node.name == "TAPTOPLAY" {return} else if result.node.name == "GOOD" {handleGoodCollision() } else if result.node.name == "BAD" {handleBadCollision()}createExplosion(geometry: result.node.geometry!, //爆炸效果position: result.node.presentation.position,rotation: result.node.presentation.rotation)result.node.removeFromParentNode() //删除子节点}}

Step4 交互逻辑

func handleGoodCollision() {game.score += 1 //当时好的碰撞 加一分game.playSound(scnScene.rootNode, name: "ExplodeGood")}func handleBadCollision() {game.lives -= 1 当时坏的碰撞 减条命game.playSound(scnScene.rootNode, name: "ExplodeBad")game.shakeNode(cameraNode)if game.lives <= 0 { //当命数等于零时 游戏结束game.saveState()showSplash(splashName: "GameOver")game.playSound(scnScene.rootNode, name: "GameOver")game.state = .GameOverscnScene.rootNode.runAction(SCNAction.waitForDurationThenRunBlock(5) { (node:SCNNode!) -> Void inself.showSplash(splashName: "TapToPlay")self.game.state = .TapToPlay})}}

Step5 爆炸效果

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

三、运行效果与文件截图

1、运行效果:

2、文件截图:

GeometryFighter文件夹内的截图:

GeometryFighter.xcodeproj文件夹内的截图:

注:本文著作权归作者,由demo大师(http://www.demodashi.com)宣传,拒绝转载,转载需要作者授权

[SceneKit] 不会 Unity3D 的另一种选择相关推荐

  1. Unity3D:中小型团队游戏研发的突围之道

    对于业界研发网游的中小企业来说,2011年将会是刻薄的一年. 从今年已经推出或者即将推出的产品来看,它们多出自一二线厂商之手,单从美术上.世界构架上.操作感上,较往年都有很大提升,有的还展现出一两个让 ...

  2. Unity3D将来时:IL2CPP(下)

    Unity3D将来时:IL2CPP(下) 转载地址:http://www.game798.com/site/news_detail/id/1580 版本准备 前文详细的介绍了IL2CPP的来龙去脉,这 ...

  3. 使用Swift和SceneKit开发一片圣诞树林

    过去几年中,移动应用像风暴一样席卷世界,改变了我们的网上工作.娱乐方式.很多移动应用开发技术应运而生,而移动也开始得到开发过程重视.尽管移动已经看似无所不在,但未来才刚刚开始.新一代的移动设备,好比可 ...

  4. unity3d游戏开发猜想——当程序猿老去

    程序猿将代码注入生命去打造互联网的浪潮之巅.当有一天他们老了.会走向那里,会做些什么? 非常多年以后,在我60岁的那天早晨,天刚蒙蒙亮我就起床了,先去公园晨练,然后回来做早餐(50岁的时候我学会了做饭 ...

  5. Unity3D 中 2D_Toolkit插件下载 和 导入方法

    Unity3D 中 2D_Toolkit插件下载 和 导入方法 1.你把下载来的包放到 安装目录:Editor\Standard Packages里面. 2.然后按ctrl+9,进入asset sto ...

  6. Unity3D 镜面反射

    原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 QQ群:[119706192] 本文链接地址: Unity3D 镜面反射 这是官方CharacterCustomization事例中的镜面 ...

  7. [推荐]C#快速开发3d游戏工具--Unity3d

    最近有幸接触了一点Unity3d的东西,和大家分享一下. Unity3d 简介 是一款可视化的,3d游戏开发软件.可以进行手动绘制3d场景,自己添加摄像机角度,3d模型设计,事件触发,对于园子里大家很 ...

  8. unity3d 切换网络_Unity3d新网络请求方式UnityWebRequest详解

    Unity将要逐步放弃www网络请求api,新的api请求方式来临:UnityWebRequestThe,也正是本篇文章要给大家介绍的重点,那就是UnityWebRequestThe的使用详解. 旧的 ...

  9. Unity3D提示“XX,some are mac os x (unix) and some are windows”

    2019独角兽企业重金招聘Python工程师标准>>> 解决办法: 将Unity安装目录\Editor\Data\Resources\ScriptTemplates\目录下的所有文件 ...

最新文章

  1. cookie自动登录
  2. 利用 T-sql 的从句 for xml path('') 实现多行合并到一行, 并带有分隔符
  3. java架构师之路:JAVA程序员必看的15本书的电子版下载地址
  4. Android自动化打包工具,利用Jenkins实现Android自动化打包
  5. 【注意力机制】SENet(Squeeze-and-Excitation Networks)详解
  6. CSS3渐变——线性渐变
  7. jQuery----分页插件实现
  8. 用React实现基于Canvas的图片放大镜功能
  9. iOS 在CollectionView上做展开收起动画
  10. python中re.group()简介
  11. 阳春3月,这个技术博客要暂停1月!!!!
  12. 50岁的程序员该何去何从
  13. java学习--类与对象
  14. 170716 网线接口顺序
  15. Python打印详细报错日志,获取报错信息位置行数
  16. 用 Python 写了一个表白神器,照片隐藏表白话语!
  17. 计算机输入出设备课件,《电脑输入设备》PPT课件.ppt
  18. 基于 web 的单视图三维重建可视化系统
  19. 演员选择框三级联动(原生javascript和jquery实现)
  20. 为什么很少人用redmine_为什么中文不能用来编程呢?其实还有这些原因!看完长见识了...

热门文章

  1. word2vec实例详解python_在python下实现word2vec词向量训练与加载实例
  2. 数据返回nan_数据处理教程
  3. NAPI 方式的实现
  4. va_list函数族应用
  5. ARM11---中断---向量中断控制器(VIC)---结合s3c6410
  6. php文件覆盖相同文件,为什么这个PHP代码在打开时会覆盖文件内容?
  7. mac java 版本_Mac 下 Java 多版本切换
  8. 远程分支显示不全 idea_IDEA中的Git操作,看完你就会了
  9. c# msi中加入驱动_MSI微星:给你的CPU装上热交换气缸活塞,不用电也能驱动风扇降温...
  10. redis压力测试工具-----redis-benchmark