自動生成的立方體,也可以使用拖曳方式,拖曳到空間的任何位置。
使用Entity來建立一個容器:
private var contentEntity = Entity()
func setupContentEntity() -> Entity {
return contentEntity
}
生成一個立方體:
func addCube() -> Entity {
let entity = ModelEntity(
mesh: .generateBox(size: 0.5, cornerRadius: 0),
materials: [SimpleMaterial(color: .red, isMetallic: false)],
collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),
mass: 0.0
)
entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))
entity.position = SIMD3(x: 0, y: 1, z: -2)
contentEntity.addChild(entity)
return entity
}
程式碼逐一說明:
mesh: .generateBox(size: 0.5, cornerRadius: 0):產生一個大小為0.5的立方體。
materials: [SimpleMaterial(color: .red, isMetallic: false)]:這個立方體具有紅色的表面,材質為非金屬。
collisionShape: .generateBox(size: SIMD3(repeating: 0.5)):這個立方體具有碰撞的功能,必須要指定這個碰撞,才可以進行拖曳。
mass: 0.0:質量為0,表示不會受到重心影響,如果有設定大於0,立方體就會自動往下掉。
回到ImmersiveView,加入這個立方體:
@StateObject var model = Day28ViewModel()
@State var cube = Entity()
將立方體加入到場景當中:
RealityView { content in
content.add(model.setupContentEntity())
cube = model.addCube()
}
加入拖曳指令:
RealityView { content in
content.add(model.setupContentEntity())
cube = model.addCube()
}
.gesture(
DragGesture()
.targetedToEntity(cube)
.onChanged { value in
cube.position = value.convert(value.location3D, from: .local, to: cube.parent!)
}
)
與iOS系統的拖曳指令最大差別在於.targetedToEntity(cube):將手勢與指定的實體進行綁定。所以這個拖曳動作只會作用於這個立方體。
value.location3D:取得手勢在空間中的位置。
cube.position = value.convert(value.location3D, from: .local, to: cube.parent!):將立方體的位置設定為目前拖曳空間的新位置。
value.convert:主要是將手勢的局部座標轉換到立方體所在的父實體的座標系。
所以開啟App呈現如圖:
打開開關之後,會出現一個紅色立方體:
可以滑動到任何位置:
以下附上完整程式碼。
程式進入點:
import SwiftUI
@main
struct TestVision3App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
}
}
視窗View:
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
@State private var showImmersiveSpace = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
VStack {
Toggle("Show ImmersiveSpace", isOn: $showImmersiveSpace)
.font(.title)
.frame(width: 360)
.padding(24)
.glassBackgroundEffect()
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
await openImmersiveSpace(id: "ImmersiveSpace")
}
else {
await dismissImmersiveSpace()
}
}
}
}
}
場景View與模型:
import SwiftUI
import RealityKit
import RealityKitContent
struct ImmersiveView: View {
@StateObject var model = Day28ViewModel()
@State var cube = Entity()
var body: some View {
RealityView { content in
content.add(model.setupContentEntity())
cube = model.addCube()
}
.gesture(
DragGesture()
.targetedToEntity(cube)
.onChanged { value in
cube.position = value.convert(value.location3D, from: .local, to: cube.parent!)
}
)
}
}
@MainActor class Day28ViewModel: ObservableObject {
private var contentEntity = Entity()
func setupContentEntity() -> Entity {
return contentEntity
}
func addCube() -> Entity {
let entity = ModelEntity(
mesh: .generateBox(size: 0.5, cornerRadius: 0),
materials: [SimpleMaterial(color: .red, isMetallic: false)],
collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),
mass: 0.0
)
entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))
entity.position = SIMD3(x: 0, y: 1, z: -2)
contentEntity.addChild(entity)
return entity
}
}
從 SwiftUI 到 Apple Vision Pro - SwiftUI 從零開始 Day28 [完]