版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.07.21 星期三 |
前言
SceneKit
使用高级场景描述创建3D游戏并将3D内容添加到应用程序。 轻松添加动画,物理模拟,粒子效果和逼真的基于物理性的渲染。接下来这几篇我们就详细的解析一下这个框架。感兴趣的看下面几篇文章。
1. SceneKit框架详细解析(一) —— 基本概览(一)
2. SceneKit框架详细解析(二) —— 基于SceneKit的简单游戏示例的实现(一)
3. SceneKit框架详细解析(三) —— 基于SceneKit的简单游戏示例的实现(二)
4. SceneKit框架详细解析(四) —— 基于SceneKit的简单游戏示例的实现(三)
5. SceneKit框架详细解析(五) —— 基于SceneKit的简单游戏示例的实现(四)
6. SceneKit框架详细解析(六) —— 基于SceneKit的简单游戏示例的实现(五)
7. SceneKit框架详细解析(七) —— 基于SceneKit的简单游戏示例的实现(六)
8. SceneKit框架详细解析(八) —— SceneKit 3D编程入门(一)
源码
1. Swift
首先看下工程组织结构
下面就是源码了。
1. Planet.swift
import Foundation
enum Planet: String, CaseIterable {
case mercury
case venus
case earth
case mars
case saturn
var name: String {
rawValue.prefix(1).capitalized + rawValue.dropFirst()
}
}
2. Planet+Info.swift
import Foundation
extension Planet {
var moonCount: Int {
switch self {
case .mercury:
return 0
case .venus:
return 0
case .earth:
return 1
case .mars:
return 2
case .saturn:
return 62
}
}
var yearLength: String {
switch self {
case .mercury:
return "88 Earth days"
case .venus:
return "225 Earth days"
case .earth:
return "365.25 days"
case .mars:
return "1.88 Earth years"
case .saturn:
return "29.45 earth years"
}
}
var namesake: String {
switch self {
case .mercury:
return "Roman god of speed"
case .venus:
return "Roman goddess of love"
case .earth:
return "The ground"
case .mars:
return "Roman god of war"
case .saturn:
return "Roman god of agriculture"
}
}
}
3. PlanetInfoRow.swift
import SwiftUI
struct PlanetInfoRow: View {
let title: String
let value: String
var body: some View {
HStack {
VStack(alignment: .leading) {
Text("\(title):").fontWeight(.thin).font(.title2)
Text(value).fontWeight(.semibold)
}
.foregroundColor(.white)
Spacer()
}
}
}
struct PlanetInfoRow_Previews: PreviewProvider {
static var previews: some View {
PlanetInfoRow(title: "Number of moons", value: "3")
}
}
4. ColorPalette.swift
import SwiftUI
enum ColorPalette {
static let primary = Color(
red: 29 / 255,
green: 17 / 255,
blue: 53 / 255,
opacity: 1)
static let secondary = Color(
red: 186 / 255,
green: 30 / 255,
blue: 104 / 255,
opacity: 1)
static let accent = Color(
red: 252 / 255,
green: 251 / 255,
blue: 254 / 255,
opacity: 1)
}
5. SolarButtonStyle.swift
import SwiftUI
struct SolarButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration
.label
.foregroundColor(configuration.isPressed ? .gray : .white)
.padding(8)
.background(ColorPalette.secondary)
.cornerRadius(8)
}
}
6. ProgressViewWithBackgroundStyle.swift
import SwiftUI
struct ProgressViewWithBackgroundStyle: ProgressViewStyle {
func makeBody(configuration: Configuration) -> some View {
ColorPalette.accent
.opacity(0.8)
.overlay(ProgressView(configuration))
.cornerRadius(5)
}
}
7. AppMain.swift
import SwiftUI
@main
struct AppMain: App {
var body: some Scene {
WindowGroup {
ContentView()
.buttonStyle(SolarButtonStyle())
}
}
}
8. ContentView.swift
import SwiftUI
import SceneKit
struct ContentView: View {
// 1
static func makeScene() -> SCNScene? {
let scene = SCNScene(named: "Solar Scene.scn")
applyTextures(to: scene)
return scene
}
static func applyTextures(to scene: SCNScene?) {
// 1
for planet in Planet.allCases {
// 2
let identifier = planet.rawValue
// 3
let node = scene?.rootNode.childNode(withName: identifier, recursively: false)
// Images courtesy of Solar System Scope https://bit.ly/3fAWUzi
// 4
let texture = UIImage(named: identifier)
// 5
node?.geometry?.firstMaterial?.diffuse.contents = texture
}
// 1
let skyboxImages = (1...6).map { UIImage(named: "skybox\($0)") }
// 2
scene?.background.contents = skyboxImages
}
// 2
var scene = makeScene()
@ObservedObject var viewModel = ViewModel()
var body: some View {
ZStack {
SceneView(
// 1
scene: scene,
// 2
pointOfView: setUpCamera(planet: viewModel.selectedPlanet),
// 3
options: viewModel.selectedPlanet == nil ? [.allowsCameraControl] : [])
// 4
.background(ColorPalette.secondary)
.edgesIgnoringSafeArea(.all)
VStack {
if let planet = viewModel.selectedPlanet {
VStack {
PlanetInfoRow(title: "Length of year", value: planet.yearLength)
PlanetInfoRow(title: "Number of moons", value: "\(planet.moonCount)")
PlanetInfoRow(title: "Namesake", value: planet.namesake)
}
.padding(8)
.background(ColorPalette.primary)
.cornerRadius(14)
.padding(12)
}
Spacer()
HStack {
HStack {
Button(action: viewModel.selectPreviousPlanet) {
Image(systemName: "arrow.backward.circle.fill")
}
Button(action: viewModel.selectNextPlanet) {
Image(systemName: "arrow.forward.circle.fill")
}
}
Spacer()
Text(viewModel.title).foregroundColor(.white)
Spacer()
if viewModel.selectedPlanet != nil {
Button(action: viewModel.clearSelection) {
Image(systemName: "xmark.circle.fill")
}
}
}
.padding(8)
.background(ColorPalette.primary)
.cornerRadius(14)
.padding(12)
}
}
}
func setUpCamera(planet: Planet?) -> SCNNode? {
let cameraNode = scene?.rootNode.childNode(withName: "camera", recursively: false)
// 1
if let planetNode = planet.flatMap(planetNode(planet:)) {
// 2
let constraint = SCNLookAtConstraint(target: planetNode)
cameraNode?.constraints = [constraint]
// 3
let globalPosition = planetNode.convertPosition(SCNVector3(x: 50, y: 10, z: 0), to: nil)
// 4
let move = SCNAction.move(to: globalPosition, duration: 1.0)
cameraNode?.runAction(move)
}
return cameraNode
}
func planetNode(planet: Planet) -> SCNNode? {
scene?.rootNode.childNode(withName: planet.rawValue, recursively: false)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
9. ViewModel.swift
import Foundation
class ViewModel: NSObject, ObservableObject {
@Published var selectedPlanet: Planet?
var title: String {
selectedPlanet?.name ?? ""
}
func selectNextPlanet() {
changeSelection(offset: 1)
}
func selectPreviousPlanet() {
changeSelection(offset: -1)
}
func clearSelection() {
selectedPlanet = nil
}
private func changeSelection(offset: Int) {
guard
let selectedPlanet = selectedPlanet,
let index = Planet.allCases.firstIndex(of: selectedPlanet)
else {
selectedPlanet = Planet.allCases.first
return
}
let newIndex = index + offset
if newIndex < 0 {
self.selectedPlanet = Planet.allCases.last
} else if newIndex < Planet.allCases.count {
self.selectedPlanet = Planet.allCases[newIndex]
} else {
self.selectedPlanet = Planet.allCases.first
}
}
}
后记
本篇主要讲述了
SceneKit 3D
编程入门,感兴趣的给个赞或者关注~~~
网友评论