版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.10.20 星期六 |
前言
SceneKit
使用高級場景描述創建3D游戲并將3D內容添加到應用程序。 輕松添加動畫,物理模擬,粒子效果和逼真的基于物理性的渲染。接下來這幾篇我們就詳細的解析一下這個框架。感興趣的看下面幾篇文章。
1. SceneKit框架詳細解析(一) —— 基本概覽(一)
2. SceneKit框架詳細解析(二) —— 基于SceneKit的簡單游戲示例的實現(一)
開始
在這部分中,您將開始制作游戲,順路學習Scene Kit
節點。
我們再回來吧!
在上一個教程中,您了解到SceneKit
將游戲組件組織為一個稱為scene graph
的層次結構。
游戲的每個元素 - 例如燈光,照相機,幾何體和粒子發射器 - 都稱為節點nodes
,節點存儲在這種樹狀結構中。
為了說明這是如何工作的,回想一下你可能聽過的童年童謠......
?? The hip bone’s connected to the back bone ?? The back bone’s connected to the shoulder bone… ??
你是對的! 這是經典歌曲Dem Dry Bones
。 如果你知道一個特別好用這個概念的經典視頻游戲,那么獎勵積分。
考慮到這些歌詞,請看一下罕見的四指骨架的以下解剖學上正確的結構:
為了幫助說明如何從此骨架構建基于節點的層次結構,請將骨架中的每個骨骼視為節點node
。
正如歌曲指出的那樣,肩骨與背骨相連。因此,將后骨骼視為肩骨的父節點,將肩骨骨骼視為后骨骼的子節點。
要將肩骨添加到場景中,請將其添加為后骨骼的子項。您可以繼續以這種方式構建整個手臂,將子骨骼添加到父骨骼。
要定位骨骼,請相對于其父骨骼定位骨骼。例如,要揮動骨架的左臂,只需按照小藍箭頭的指示來回旋轉肩節點即可。肩節點的所有子節點將與其父節點一起旋轉。
恭喜!你剛剛通過骨骼解剖學!
從技術角度來看,單個節點由SCNNode
類表示,并表示相對于其父節點在3D空間中的位置。節點本身沒有可見內容,因此,當作為場景的一部分呈現時,它是不可見的。要創建可見內容,您必須向節點添加其他組件,如燈光,相機或幾何圖形(如骨骼)。
場景圖(scene graph)
包含一個特殊節點,它構成了基于節點的層次結構的基礎:根節點。要構建場景,可以將節點添加為根節點的子節點,也可以添加為根節點后代之一的子節點。
在本教程中,您將開始使用SceneKit
中的一些簡單節點,例如相機和幾何節點。在本教程結束時,您將在屏幕上呈現一個簡單的3D立方體!
Asset Catalogs
一旦你成為一名成功且富有經驗的3D游戲設計師,你將有足夠的資金聘請你自己的圖形藝術家和音響工程師,這將讓你自由地專注于游戲代碼。SceneKit asset catalog
專門設計用于幫助您與代碼分開管理游戲資產。
通過資產目錄,您可以在單個文件夾中管理游戲資產。 要使用它,只需將帶有.scnassets
擴展名的文件夾添加到項目中,并將所有游戲資源保存在該文件夾中。 Xcode會在構建時將目錄中的所有內容復制到您的應用包中。 Xcode保留您的資產文件夾層次結構;這使您可以完全控制文件夾結構。
通過與您的藝術家共享您的資產文件夾,他們可以快速解決任何問題,例如一個不那么可怕的跨眼怪物,并為下一次構建做好準備,而無需將更改的資產復制回項目。
1. Adding an Asset Catalog - 添加資產目錄
現在您已了解資產目錄的全部內容,您將向Geometry Fighter
添加一個。
將GeometryFighter.scnassets
文件夾從resources
文件夾拖放到Xcode
中的游戲項目中。 在出現的彈出窗口中,確保選中Copy items if needed
,Create Groups
和GeometryFighter
目標,然后單擊Finish
。
在項目導航器中選擇GeometryFighter.scnassets
文件夾。 請注意右側面板中資產目錄的其他獨有設置。 展開GeometryFighter.scnassets
文件夾和子文件夾以查看有關資產的更多詳細信息:
資產目錄中有兩個文件夾:
-
Sounds
:包含游戲所需的所有聲音資源。 -
Textures
:包含您需要的所有圖像。
隨意偷看每個文件夾里面的內容。
2. Adding a Launch Screen - 添加啟動屏幕
現在您已導入資產目錄,您將負責一些基本的內部處理步驟,包括在啟動屏幕上添加適當的圖像。
首先,單擊項目導航器中的Assets.xcassets
。 將GeometryFighter.scnassets / Textures / Logo_Diffuse.png
拖放到AppIcon
下面的資源中。
接下來,單擊項目導航器中的LaunchScreen.storyboard
。 選擇主視圖并將Background
屬性設置為深藍色:
接下來,將Logo_Diffuse
圖像從Media Library
拖動到視圖的中心。 將新圖像的Content Mode
屬性設置為Aspect Fit
:
你的啟動屏幕差不多完成了。 您需要做的就是添加一些約束,以便啟動圖像適用于所有設備。 單擊底部的Pin
按鈕,切換所有四條邊的約束,然后單擊Add 4 Constraints
,如下所示:
您已完成設置啟動屏幕! 構建并運行您的應用程序; 你會看到你閃亮的新發布屏幕出現:
3. Adding a Background Image - 添加背景圖像
一旦你的啟動畫面消失,你就會被轉回空白的屏幕。 是時候添加一個漂亮干凈的背景,這樣你就不會覺得自己正盯著一個黑洞。
為此,請將以下代碼行添加到GameViewController.swift中setupScene()
的底部:
scnScene.background.contents = "GeometryFighter.scnassets/Textures/Background_Diffuse.png"
此行代碼指示場景從資產目錄中加載Background_Diffuse.png
圖像,并將其用作場景背景的材質屬性。
構建并運行;你現在應該在游戲開始時看到藍色背景圖像:
您已完成項目的所有基本內部處理任務。 你的游戲現在有一個華麗的應用程序圖標,一個啟動畫面和漂亮的背景,它們都準備好顯示你將要添加到場景中的節點。
The SceneKit Coordinate System - SceneKit坐標系
在開始向場景添加節點之前,首先需要了解SceneKit
的坐標系如何工作,以便將節點定位在所需位置。
在諸如UIKit或SpriteKit的2D系統中,您使用一個點來描述x和y軸上的視圖或sprite的位置。 要在3D空間中放置對象,還需要描述對象在z軸上的位置深度。
請考慮以下簡單說明:
SceneKit使用這個三軸系統來表示3D空間中的位置。 紅色塊沿x軸放置,綠色塊沿y軸放置,藍色塊沿z軸放置。 軸正中心的灰色立方體表示原點,坐標為(x:0,y:0,z:0)
。
SceneKit
使用SCNVector3
數據類型將三維坐標表示為三分量向量。 以下是在代碼中創建向量的方法:
let position = SCNVector3(x: 0, y: 5, z: 10)
這將使用向量(x:0,y:5,z:10)
聲明position
位置。 您可以輕松訪問矢量的各個屬性,如下所示:
let x = position.x
let y = position.y
let z = position.z
如果您之前使用過CGPoint
,則可以輕松地在它與SCNVector3
之間進行比較。
注意:添加到場景中的節點的默認位置為(x:0,y:0,z:0),它始終相對于父節點。 要將節點放置在所需位置,您需要調整節點相對于其父節點的位置(本地坐標) - 而不是原點(世界坐標)。
Cameras - 相機
既然您已經了解了如何在SceneKit
中定位節點,那么您可能想知道如何在屏幕上實際顯示某些內容。 回想一下本教程系列第1部分中電影集的類比。 要拍攝場景,您需要放置一個觀察場景的攝像機,并且從攝像機的角度來看該場景的結果圖像。
SceneKit
以類似的方式工作;包含攝像機的節點的位置決定了您從中查看場景的視點。
下圖演示了攝像機在SceneKit
中的工作原理:
上圖中有幾個關鍵點:
- 1) 攝像機的視線方向始終沿著包含攝像機的節點的負z軸。
- 1) 視野
( field of view)
是相機可視區域的限制角度。 緊密的角度提供了狹窄的視野,而廣角提供了寬闊的視野。 - 1) 視錐體
(viewing frustum)
確定了相機的可見深度。 此區域外的任何東西 - 即距離相機太近或太遠 - 都將被剪裁,不會出現在屏幕上。
SceneKit
攝像機由SCNCamera
表示,其xPov
和yPov
屬性可讓您調整視野,而zNear
和zFar
可讓您調整視錐體。
要記住的一個關鍵點是攝像機本身不會做任何事情,除非它是節點層次結構的一部分。
1. Adding a Camera - 添加相機
是時候嘗試一下了。 打開GameViewController.swift
并在scnScene
下面添加以下屬性:
var cameraNode: SCNNode!
接下來,在setupScene()
下面添加以下方法:
func setupCamera() {
// 1
cameraNode = SCNNode()
// 2
cameraNode.camera = SCNCamera()
// 3
cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
// 4
scnScene.rootNode.addChildNode(cameraNode)
}
仔細看看代碼:
- 1) 首先,創建一個空的
SCNNode
并將其分配給cameraNode
。 - 2) 接下來,您將創建一個新的
SCNCamera
對象并將其分配給cameraNode
的camera
屬性。 - 3) 然后,將攝像機的位置設置為
(x:0,y:0,z:10)
。 - 4) 最后,將
cameraNode
添加到場景中,作為場景根節點的子節點。
通過調用viewDidLoad()
中剛剛添加的方法來完成任務,就在setupScene()
下面:
setupCamera()
沒有必要構建和運行。 即使你剛剛在場景中添加了一個攝像頭,仍然沒有什么可看的,這意味著你不會看到任何不同的東西。 但那即將改變!
Geometry - 幾何
要創建可見內容,您需要將幾何對象添加到節點。 幾何對象表示三維形狀,并且由稱為定義多邊形polygons
的vertices
的許多點創建。
此外,幾何對象可以包含修改幾何體表面外觀的材質對象。 通過材質,您可以指定幾何體表面的顏色和紋理等信息,以及幾何體應如何響應光線以及其他視覺效果。 頂點和材質的集合稱為模型或網格(model
或者 mesh
)。
SceneKit
包含以下內置幾何形狀:
在前排,從左側開始,您有一個圓錐,一個圓環,一個膠囊和一個管子。 在后排,從左側開始,你有一個金字塔,一個盒子,一個球體和一個圓柱體。
1. Adding ShapeTypes - 添加ShapeTypes
在開始向場景添加幾何形狀之前,請創建一個新的Swift文件,以定義您將在游戲中使用的不同形狀的ShapeType
枚舉。
右鍵單擊GeometryFighter
組并選擇New File ....
選擇iOS / Source / Swift File
文件模板,然后單擊Next
:
將文件命名為ShapeType.swift
,確保它包含在項目中,然后單擊Create
。
創建文件后,打開ShapeType.swift
并使用以下內容替換其內容:
import Foundation
// 1
enum ShapeType:Int {
case box = 0
case sphere
case pyramid
case torus
case capsule
case cylinder
case cone case tube
// 2
static func random() -> ShapeType {
let maxValue = tube.rawValue
let rand = arc4random_uniform(UInt32(maxValue+1))
return ShapeType(rawValue: Int(rand))!
}
}
上面的代碼相對簡單:
- 1) 您創建一個名為
ShapeType
的新枚舉,枚舉各種形狀。 - 2) 您還定義了一個名為
random()
的靜態方法,該方法生成隨機ShapeType
。 此功能將在您的游戲中稍后派上用場。
2. Adding a Geometry Node - 添加幾何節點
您的下一個任務是創建一個方法,該方法生成ShapeType
中定義的各種隨機形狀。
將以下方法添加到GameViewController.swift
,就在setupCamera()
下面:
func spawnShape() {
// 1
var geometry:SCNGeometry
// 2
switch ShapeType.random() {
default:
// 3
geometry = SCNBox(width: 1.0, height: 1.0, length: 1.0,
chamferRadius: 0.0)
}
// 4
let geometryNode = SCNNode(geometry: geometry)
// 5
scnScene.rootNode.addChildNode(geometryNode)
}
依次記錄每個編號的評論:
- 1) 首先,創建占位符
geometry
變量以供稍后使用。 - 2) 接下來,定義一個
switch
語句來處理ShapeType.random()
返回的形狀。 目前它還不完整,只創造了一個盒子形狀;在本教程結束時,您將在挑戰中添加更多內容。 - 3) 然后,創建一個
SCNBox
對象并將其存儲在geometry
中。 您可以指定寬度,高度和長度以及倒角半徑(這是一種說明圓角的奇特方式)。 - 4) 在這里,您將創建名為
geometryNode
的SCNNode
實例。 這次,您使用SCNNode
初始化程序,它使用geometry
參數來創建節點并自動附加提供的幾何體。 - 5) 最后,將節點添加為場景根節點的子節點。
現在你需要調用這個方法。 將以下行添加到setupCamera()
下面的viewDidLoad()
:
spawnShape()
構建并運行;你會看到屏幕上顯示一個白色方塊:
這里有幾點需要注意:
- 1)
box
節點是spawnShape()
的默認形狀,它位于場景中的(x:0,y:0,z:0)
。 - 2) 您正在通過
cameraNode
查看場景。 由于攝像機節點位于(x:0,y:0:z:10)
,因此該box
位于攝像機可視區域的中心。
好吧,這不是很令人興奮,而且它幾乎不是三維的 - 但不要害怕......下一部分會改變所有這些!
Built-in View Features - 內置視圖功能
SCNView
具有一些開箱即用的功能,可幫助您輕松生活。
將以下行添加到GameViewController.swift
中的setupView()
,就在當前實現的下方:
// 1
scnView.showsStatistics = true
// 2
scnView.allowsCameraControl = true
// 3
scnView.autoenablesDefaultLighting = true
以下是對上述代碼的解釋:
- 1)
showStatistics
在場景底部啟用實時統計面板。 - 2)
allowsCameraControl
允許您通過簡單的手勢手動控制活動相機。 - 3)
autoenablesDefaultLighting
在場景中創建一個通用的全向燈,因此您不必擔心添加自己的光源。
構建并運行;這次事情應該看起來更令人興奮!
您可以使用以下手勢來控制場景中的活動相機:
- Single finger swipe - 單指滑動:圍繞場景內容旋轉活動攝像機。
- Two finger swipe - 雙指滑動:在場景中向左,向右,向上或向下移動或平移相機。
- 雙指捏 - Two finger pinch:將相機放入和移出場景。
- 雙擊 - Double-tap:如果您有多個攝像頭,則會在場景中的攝像頭之間切換。 當然,由于場景中只有一臺攝像機,因此不會這樣做。 但是,它還具有將相機重置為其原始位置和設置的效果。
1. Working with Scene Statistics - 使用場景統計
找到屏幕底部的統計信息面板:
以下是每個元素含義的快速細分:
-
fps
:代表每秒幀數。這是在一秒內完成的連續幀重繪總量的測量。這個數量越低,你的游戲表現就越差。您通常希望您的游戲以60fps的速度運行,這將使您的游戲看起來更加流暢。 - ◆:代表每幀的總繪圖調用。這通常是每個幀繪制的可見對象的總量。影響對象的燈光也會增加對象的繪制調用量。這個數量越低越好。
- ▲:代表每幀的總多邊形。這是用于為所有可見幾何體繪制單個幀的多邊形總數。這個數量越低越好。
- ?:代表全部可見光源。這是當前影響可見對象的光源總量。 SceneKit指南建議一次不要使用3個以上的光源。
單擊+
按鈕展開面板并顯示更多詳細信息:
此面板為您提供以下信息:
-
Frame time - 幀時間:這是繪制單幀所花費的總時間。 需要
16.7ms
的幀時間來實現60fps
的幀速率。 - The color chart: - 顏色圖表:這為您提供了SceneKit渲染管道中每個組件的粗略幀時間百分比細分。
從這個例子中,您現在知道繪制一個幀的時間為22.3ms,其中±75%
用于渲染,±25%
用于GL Flush
。
注意:稍后將更詳細地討論
SceneKit
渲染管道。
您可以單擊-
按鈕以再次最小化面板。
所有這些功能都是內置是不是很好?
Challenges - 挑戰
對您來說,練習您自己學到的知識非常重要,本系列的許多部分都有一個或多個與之相關的挑戰。
我強烈建議嘗試所有的挑戰。雖然按照分步教程進行,但您可以通過自己解決問題來學習更多知識。
1. Your First Challenge - 你的第一個挑戰
本教程中只有一個挑戰,但它很有趣。
您的挑戰是改進spawnShape()
中的switch
語句以處理枚舉器中的其余形狀。
使用Apple的官方SceneKit文檔(http://apple.co/2aDBgtH)作為各種幾何形狀的指南。另外,看一下ShapeType
枚舉,看看要創建哪些形狀;他們的名字應該讓你知道從哪里開始。
不要過分擔心使用的尺寸;試著讓它們與你之前制作的盒子大小相同。
在這次挑戰之后,您將牢牢掌握SceneKit中的一些最基本的概念!
后記
本篇主要講述了基于SceneKit的簡單游戲示例的實現,感興趣的給個贊或者關注~~~