本教程是我在接下来的下一个3D游戏的开发中同时进行的配套工作,该游戏仅使用SceneKit,完全由Swift 3编写。
本教程适用于谁?
- 游戏开发人员的T恤销售? 认真吗
- Steam正在成为沃尔玛。 没关系。
- 迷你专业化
- Tekwill Resident的个人资料:Spooky House Studios,摩尔多瓦的游戏工作室,下载了超过6000万个游戏
- 问Unity专家:什么是ECS?
它不适合绝对的初学者或新手,因为我假设使用Swift符号,三角技术等。另一方面,您无需一开始就学习所有内容。 就像地形类RBTerrain一样 。 您可以照原样使用它,并在拥有更多专门知识后再阅读代码。最重要的是您了解原理。
介绍
对我而言,重要的是要直接从一个良好而坚实的基础开始,以后可以将其用于实际的游戏。 因此,请不要使用Xcode Project向导创建项目,而应直接从GitHub上的Source开始。 我也不想建立一个分步显示每行的教程,但是总是要涵盖游戏的一章并介绍一个新的组件并说明如何以及为什么
许多人要求我显示有关我当前正在研究的基于SceneKit的游戏的更多背景信息。 我记得自己一开始很难找到足够的信息。 然后在iOS上使用OpenGl。 但是即使现在使用SceneKit。 如此多的教程总是在屏幕上显示相同的受物理影响的框。 那不是我想做的。 我决定将与我的真实Game项目平行的子集Game教程作为示例,该教程显示了如何使用Swift和SceneKit为AppStore创建准备好的Game。 与真实游戏相同的质量和概念,只是更简单。 我不仅将介绍SceneKit相关的内容,还将介绍如何实现GameKit和其他与游戏相关的框架。 除了与游戏编程相关的最佳实践,设计模式和体系结构。 我不会介绍3D编程和矢量几何的基础知识,有很多很好的资源。 但是,我将展示如何在带有Swift 3的SceneKit中使用它。因此请注意(此时)要使用Xcode 8 Beta 2来使用它。 我已经决定使用Swift 3,因为网上有如此多的资源甚至关注Swift 1,而且很多人告诉我在将其适应新的Swift版本方面遇到了问题。 所以本教程是最新的🙂
但是现在开始吧!
教程1-建立地形
就像在现实世界中一样,游戏的基础是地面,地面,在我们的情况下是地形。 在本教程中,我们将构建一个类似于Blue Max的游戏,这是一个80年代的经典射击游戏,但采用3D建模。 (注意:我的真实游戏不是那样,而是使用一些类似的概念,您将在后面看到)
游戏骨架
像Swift中的每个iOS应用程序一样,一个iOS游戏具有两个主要组件,这些组件使该应用程序正常运行并可见:
1. AppDelegate
AppDelegate是在加载App时首先实例化并调用的类。 在Objective-C中有实例化该类的main()
函数,在Swift中,只需在类前面添加@UIApplicationMain
完成此操作,因此您不会在Objective-C中找到任何main.m。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private var _window: UIWindow?
private var _gameViewController: GameViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
_gameViewController = GameViewController()
_window = UIWindow(frame: UIScreen.main().bounds)
_window?.rootViewController = _gameViewController
_window?.makeKeyAndVisible()
return true
}
}
(注意:我个人从不使用情节提要或Xib文件,尤其是在Games中不使用。这有充分的理由,但我不想在此引起争议,😉)
2.窗口
每个应用程序都有其窗口,其中大多数甚至只有1个,它会使用整个屏幕。 对于游戏, AppDelegate将创建完整尺寸的窗口。
3.(Game-)ViewController和视图
ViewController负责加载和显示视图。 在我们的例子中, SCNView是一个负责呈现3D内容的SceneKit视图。 而且ViewController负责旋转行为和状态栏外观(请参阅GitHub中的代码)。
class GameViewController: UIViewController {
var _sceneView: SCNView?
var _level: TutorialLevel1?`
override func viewDidLoad() {
super.viewDidLoad()
_level = TutorialLevel1()
_level!.create()
_sceneView = SCNView()
_sceneView!.scene = _level
_sceneView!.allowsCameraControl = false
_sceneView!.showsStatistics = true
_sceneView!.backgroundColor = UIColor.black()
_sceneView!.debugOptions = .showWireframe
self.view = _sceneView
}
因此,这主要是整个游戏框架,是每个应用程序和游戏的基础。 以此为每个游戏的基础,现在再忘记一次,因为它总是一样的。😉从这里开始,我们开始玩游戏本身…
我从来没有在ViewController中加入游戏逻辑,而只是创建一个SCNScene类并将其附加到SCNSceneView 。 分离代码始终是一个好方法,不仅在游戏编程中。
游戏
游戏本身是在SCNScene的子类中实现的 (就像之前编写的一样)。 因为我们稍后会创建游戏的不同级别,所以我将此类命名为TutorialLevel1 。 Swift和SceneKit的优点在于实现游戏所需的代码简短。 但是,现在让我们看一下我们在TutorialLevel类中所做的事情。
主要是在这里创建所有Game对象并对其进行控制。 因此,请实现我们的3D射击游戏的游戏玩法和逻辑。
最初,我们只有两个Game对象:terrain和玩家类,而教程1的重点是terrain类。 但是我们已经实现了一个简单的播放器类,以“跨越”地形并在以后进行扩展。 但首先是地形课。
地形类— RBTerrain.swift
我创建了一个名为RBTerrain的基本类, 该类可以创建任何大小和样式的3D景观。 因此,您可以在游戏中使用它,也可以根据需要自定义它。 用法很简单:
_terrain = RBTerrain(width: 320, length: 320, scale: 128)
这将创建terrain类并定义其大小和比例。 该比例用于定义景观丘陵的高度。 稍微玩一下此参数可以看到效果。 接下来的几行更加有趣:
let generator = RBPerlinNoiseGenerator(seed: nil)
_terrain?.formula = {(x: Int32, y: Int32) in
return generator.valueFor(x: x, y: y)
}
因此,让我们看一下它们。 主要的本质是指定一个生成地形“山丘”的公式,因为terrain类本身仅创建了网格网格,网格可以具有任何形式。 这是通过使用闭包来完成的。 如果您不熟悉它,请先阅读本文,因为它在所有与Swift相关的编程中都很重要。 如果您以前使用过Objective-C,则它们称为块 。 地形封闭的方法很简单。 地形类调用此函数,并为网格(网格)的每个x,y坐标期望一个高度坐标。 因此,您可以在此处提供任何值。 这也是开始玩地形课程的好地方。 但是,要使用山丘构建真实的地形,我们使用一种称为Perlin噪声的方法。 这当然不是我的算法,它在Wikipedia中有详细描述。 我刚刚用现有的C代码制作了一个Swift 3版本。 但是您也可以暂时忘记该类,而只是使用它或编写您自己的公式。 例如,一种有趣的方法可能是根据您拥有的图像来构建地形…无论如何,terrain类始终可以保持不变,只需创建另一个公式并对其进行分配即可。 在深入探讨地形类之前,我想谈谈其他游戏组件,即玩家类。
(游戏玩家)
播放器类首先看起来有些复杂。 但是最后很容易。 它(类似于terrain class)是从SCNNode派生的,但是包含一些支持摄像机,灯光和播放器节点本身的子节点。 我很快会谈谈相机和灯光,但请紧记:
始终将SCNNode中的子节点用于游戏对象
为什么? 稍后您将看到,但用简单的话来说,它们是彼此独立地操纵的。 例如,当您旋转播放器时,您不需要同时旋转相机。 使用这种方法,您可以分离所有动作。
相机
相机是3D编程中的重要对象。 它是必不可少的虚拟对象,它描述了当前在3D世界中可见的内容。 因此,就像在现实世界中一样。 根据相机放置的位置和方式,您会看到3D世界的各个部分。 但是,为什么我将相机添加到播放器中? 在网络中的其他大多数示例中,您会看到相机已连接到场景。 但是在我们制作的游戏中,我希望摄影机始终跟随玩家对象,这是一种简便的方法。 因此,当我们移动播放器时,摄像机随之移动。 然后,在下一个教程中,我们将介绍如何更多地操纵相机,但是现在我们有了想要的东西。
灯光
要详细了解灯光,需要更深入的专业知识,但对我们而言,这就像在现实世界中一样。 我们在场景中添加了一些灯泡,因此我们可以模拟太阳,或添加聚光灯以显示更多细节并制作阴影。 还有我连接到播放器的灯,因为我希望灯始终位于播放器所在的位置。 在本系列的下一章之后,我们将进一步讨论灯光。
Terrain类的详细信息
如前所述,我现在将在Terrain类中更详细地介绍。 SceneKit提供了很多原语,例如盒子,圆柱体等,但是在实际的3D游戏中,我们必须主要是自行创建对象或使用Blender的3D程序创建对象,我将其用于Game对象。 也可以使用Blender之类的程序创建地形,但是我们可以通过使用Perlin Noise公式以编程方式实现尺寸和外观上的灵活性。
当您查看RBTerrain类时,有一个函数可以完成所有工作:
private func createGeometry() ->SCNGeometry
SceneKit使我们可以创建通过矢量创建3D对象并稍后为其分配材质的功能(类似于OpenGL中的) 。 那就是我们在这里所做的。 我们创建给定大小的网格。 顶点列表定义了该网格:
for y in 0...Int(h-2) {
for x in 0...Int(w-1) {
vertices[vertexCount] = bottomLeft
vertices[vertexCount+1] = topLeft
vertices[vertexCount+2] = topRight
vertices[vertexCount+3] = bottomRight
vertexCount += 4
}
然后将其用于创建SceneKit几何源:
let source = SCNGeometrySource(vertices: vertices, count: vertexCount)
不幸的是,这还不足以创建自定义3D对象。 本质上,它还需要三个数据列表:
几何列表:本质上是索引列表,描述了必须如何使用顶点
let geometryData = NSMutableData()
var geometry: CInt = 0
while (geometry < CInt(vertexCount)) {
let bytes: [CInt] = [geometry, geometry+2, geometry+3, geometry, geometry+1, geometry+2]
geometryData.append(bytes, length: sizeof(CInt.self)*6)
geometry += 4
}
let element = SCNGeometryElement(data: geometryData as Data, primitiveType: .triangles, primitiveCount: vertexCount/2, bytesPerIndex: sizeof(CInt.self))
纹理列表:定义以后如何将纹理(图像)应用于几何体
let uvData = NSData(bytes: uvList, length: uvList.count * sizeof(vector_float2.self))
let uvSource = SCNGeometrySource(data: uvData as Data,
semantic: SCNGeometrySourceSemanticTexcoord,
vectorCount: uvList.count,
floatComponents: true,
componentsPerVector: 2,
bytesPerComponent: sizeof(Float.self),
dataOffset: 0,
dataStride: sizeof(vector_float2.self))
法线列表:定义如何将光应用于对象
for normalIndex in 0...vertexCount-1 {
normals[normalIndex] = SCNVector3Make(0, 0, -1)
}
sources.append(SCNGeometrySource(normals: normals, count: vertexCount))
ff! 这就是地形的创建,现在仅是一个声明:
_terrainGeometry = SCNGeometry(sources: sources, elements: elements)
let terrainNode = SCNNode(geometry: createGeometry())
现在我们有了一个SCNGeometry并从中创建一个SCNNode ,我们可以像SceneKit中的标准原语一样使用它。

就像在模拟器中运行它时一样。
现在玩得开心!
这是最重要的😉指向GitHub上的教程源的链接:
https://github.com/rogerboesch/SceneKitTutorial
教程章节:
–第1部分–建立地形
–第2部分–创建一个真实的玩家游戏对象
–第3部分–为您的地形增添生命
–第4部分–实施游戏循环
–第5部分–使用CoreMotion平稳地飞行
–第六部分–完成游戏(缺少的部分)
–第7部分– ARKit:在您的环境中玩
–第8部分–多平台:我们在macOS上的游戏
–第9部分– tvOS:出色的游戏平台
–第10部分-高级SceneKit(有待改进)
最初发布于 https://rogerboesch.github.io/scenekit/tutorial/games/2017/07/15/scenekit-zerotohero-I.html
其他具有基本信息的良好资源是:
SceneKit:
https://developer.apple.com/videos/play/wwdc2014/610/
https://developer.apple.com/videos/play/wwdc2016/609/
https://itunes.apple.com/us/book/3d-graphics-with-scene-kit/id936235049?ls=1&mt=11
向量几何:
http://physics.about.com/od/mathematics/a/VectorMath.htm