iOS 18/MacOS 15下同步控制双Stereo VR RealityView的方法
问题:双视图Stereo VR场景下同步控制两侧实体的实现方法
在iOS 18/MacOS 15环境中,通过HStack两次调用MainView实现双视图Stereo VR方案,但目前拖拽手势、双击启停动画、Game Controller控制均仅能单独作用于单个视图,无法同步控制两个视图内的实体。现寻求通过触摸或Game Controller同步控制两侧实体的实现方法。
触摸控制版本代码
import SwiftUI import RealityKit struct ContentView : View { var body: some View { HStack { MainView() MainView() } .background(.black) } } struct MainView : View { @State var anchor1 = AnchorEntity() @State var rotateTime: Timer! @State var rotateCounter: Float = 0.0 @State var animateMode = false @State var position: UnitPoint = .zero let ratio: Float = 0.005 var body: some View { RealityView { rvc in let item = ModelEntity(mesh: .generateBox(size: 0.5), materials: [SimpleMaterial()]) anchor1.addChild(item) anchor1.orientation = .init(angle: .pi/4, axis:[0,1,1]) rvc.add(anchor1) } .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { rotateTime?.invalidate() rotateTime = Timer.scheduledTimer( withTimeInterval: 0.01, repeats: true, block: { _ in rotate(anchor1) } ) } } .gesture(dragThis) .gesture(doubleClickThis) } func rotate(_ anchor: AnchorEntity) { rotateCounter += 1 if rotateCounter >= 360 { rotateCounter = 0.0 } anchor.orientation = .init(angle: (rotateCounter * (.pi/180)), axis: [0,1,0]) } var dragThis: some Gesture { DragGesture(minimumDistance: 15, coordinateSpace: .global) .onChanged { value in print(value.translation) print(value.location) anchor1.position.x = Float(value.translation.width + position.x) * ratio anchor1.position.y = Float(value.translation.height + position.y) * -ratio } .onEnded{value in position.x += value.translation.width position.y += value.translation.height } } var doubleClickThis: some Gesture { TapGesture(count: 2) .onEnded { value in animateMode.toggle() if animateMode { rotateTime?.invalidate() rotateTime = Timer.scheduledTimer( withTimeInterval: 0.01, repeats: true, block: { _ in rotate(anchor1) //rotate(anchor2) } ) } else { rotateTime?.invalidate() } } } }
支持触摸和Game Controller的版本代码
import SwiftUI import RealityKit import GameController struct ContentView : View { var body: some View { HStack { MainView() MainView() } .background(.black) } } struct MainView : View { @State var anchor1 = AnchorEntity() @State var rotateTime: Timer! @State var rotateCounter: Float = 0.0 @State var animateMode = false @State var position: UnitPoint = .zero @State var gameController: GCController! @State private var stickTime: Timer! @State var SRDelayer = false let ratio: Float = 0.005 var body: some View { RealityView { rvc in let item = ModelEntity(mesh: .generateBox(size: 0.5), materials: [SimpleMaterial()]) anchor1.addChild(item) anchor1.orientation = .init(angle: .pi/4, axis:[0,1,1]) rvc.add(anchor1) } .onAppear { setupGameController() DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { rotateTime?.invalidate() rotateTime = Timer.scheduledTimer( withTimeInterval: 0.01, repeats: true, block: { _ in rotate(anchor1) } ) } } .gesture(dragThis) .gesture(doubleClickThis) } func rotate(_ anchor: AnchorEntity) { rotateCounter += 1 if rotateCounter >= 360 { rotateCounter = 0.0 } anchor.orientation = .init(angle: (rotateCounter * (.pi/180)), axis: [0,1,0]) } var dragThis: some Gesture { DragGesture(minimumDistance: 15, coordinateSpace: .global) .onChanged { value in print(value.translation) print(value.location) anchor1.position.x = Float(value.translation.width + position.x) * ratio anchor1.position.y = Float(value.translation.height + position.y) * -ratio } .onEnded{value in position.x += value.translation.width position.y += value.translation.height } } var doubleClickThis: some Gesture { TapGesture(count: 2) .onEnded { value in animateMode.toggle() playStop() } } func playStop() { if animateMode { rotateTime?.invalidate() rotateTime = Timer.scheduledTimer( withTimeInterval: 0.01, repeats: true, block: { _ in rotate(anchor1) //rotate(anchor2) } ) } else { rotateTime?.invalidate() } } //MARK: - GameController func setupGameController() { NotificationCenter.default.addObserver(forName: NSNotification.Name.GCControllerDidBecomeCurrent, object: nil, queue: nil, using: didConnectController) NotificationCenter.default.addObserver(forName: NSNotification.Name.GCControllerDidStopBeingCurrent, object: nil, queue: nil, using: didDisconnectController) GCController.startWirelessControllerDiscovery{} guard let controller = GCController.controllers().first else { return } registerGameController(controller) } func didConnectController(_ notification: Notification) { gameController = notification.object as? GCController print("Game Controller ◦ gameControllerIsConnected \(gameController.productCategory)") unregisterGameController() registerGameController(gameController) } func didDisconnectController(_ notification: Notification) { gameController = notification.object as? GCController print("◦ disgameControllerIsConnected \(gameController.productCategory)") unregisterGameController() } func registerGameController(_ gameController: GCController) { self.gameController = gameController // important for refresh screen gameController.extendedGamepad?.dpad.left.pressedChangedHandler = { (button, value, pressed) in self.button("Left", pressed) } gameController.extendedGamepad?.dpad.up.pressedChangedHandler = { (button, value, pressed) in self.button("Up", pressed) } gameController.extendedGamepad?.dpad.right.pressedChangedHandler = { (button, value, pressed) in self.button("Right", pressed) } gameController.extendedGamepad?.dpad.down.pressedChangedHandler = { (button, value, pressed) in self.button("Down", pressed) } gameController.extendedGamepad?.buttonX.pressedChangedHandler = { (button, value, pressed) in self.button("Square", pressed) } gameController.extendedGamepad?.buttonY.pressedChangedHandler = { (button, value, pressed) in self.button("Triangle", pressed) } gameController.extendedGamepad?.buttonB.pressedChangedHandler = { (button, value, pressed) in self.button("Circle", pressed) } gameController.extendedGamepad?.buttonA.pressedChangedHandler = { (button, value, pressed) in self.button("Cross", pressed) } gameController.extendedGamepad?.buttonOptions?.pressedChangedHandler = { (button, value, pressed) in self.button("Options", pressed) } gameController.extendedGamepad?.buttonMenu.pressedChangedHandler = { (button, value, pressed) in self.button("Menu", pressed) } gameController.extendedGamepad?.leftThumbstickButton?.pressedChangedHandler = { (button, value, pressed) in self.button("LeftThumbClick", pressed) } gameController.extendedGamepad?.rightThumbstickButton?.pressedChangedHandler = { (button, value, pressed) in self.button("RightThumbClick", pressed) } gameController.extendedGamepad?.leftShoulder.pressedChangedHandler = { (button, value, pressed) in self.button("LeftShoulder", pressed) } gameController.extendedGamepad?.rightShoulder.pressedChangedHandler = { (button, value, pressed) in self.button("RightShoulder", pressed) } gameController.extendedGamepad?.leftTrigger.pressedChangedHandler = { (button, value, pressed) in self.button("LeftTriggerClick", pressed)} gameController.extendedGamepad?.rightTrigger.pressedChangedHandler = { (button, value, pressed) in self.button("RightTriggerClick", pressed) } gameController.extendedGamepad?.leftTrigger.valueChangedHandler = { (button, value, pressed) in self.triggerL("LeftTriggerValue", value) } gameController.extendedGamepad?.rightTrigger.valueChangedHandler = { (button, value, pressed) in self.triggerR("RightTriggerValue", value) } gameController.extendedGamepad?.leftThumbstick.valueChangedHandler = { (button, xvalue, yvalue) in self.sticks("LeftThumbStick", xvalue, yvalue) } gameController.extendedGamepad?.rightThumbstick.valueChangedHandler = { (button, xvalue, yvalue) in self.sticks("RightThumbStick", xvalue, yvalue) } } func unregisterGameController() { } func button(_ button: String, _ pressed: Bool) { if button == "Cross" { if SRDelayer == false { SRDelayer = true animateMode.toggle() playStop() } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) { self.SRDelayer = false } } if button == "Up" { anchor1.position.y += 0.02 } if button == "Down" { anchor1.position.y -= 0.02 } if button == "Right" { anchor1.position.x += 0.02 } if button == "Left" { anchor1.position.x -= 0.02 } } func triggerR(_ button: String, _ value: Float) { } func triggerL(_ button: String, _ value: Float) { } func sticks(_ button: String, _ xvalue: Float, _ yvalue: Float) { if button == "RightThumbStick" || button == "LeftThumbStick" { if yvalue > 0.5 || yvalue < -0.5 || xvalue > 0.5 || xvalue < -0.5 { if stickTime == nil // if its not running { stickTime?.invalidate() stickTime = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in pollInputSticks() }) } } else { stickTime?.invalidate() stickTime = nil } } else { stickTime?.invalidate() stickTime = nil } } func pollInputSticks() { if let gamePadRight = gameController.extendedGamepad?.rightThumbstick { if gamePadRight.yAxis.value > 0.5 // Up { anchor1.position.y += 0.002 } else if gamePadRight.yAxis.value < -0.5 // Down { anchor1.position.y -= 0.002 } if gamePadRight.xAxis.value > 0.5 // Right { anchor1.position.x += 0.002 } else if gamePadRight.xAxis.value < -0.5 // Left { anchor1.position.x -= 0.002 } } if let gamePadLeft = gameController.extendedGamepad?.leftThumbstick { if gamePadLeft.yAxis.value > 0.5 // Up { anchor1.position.z -= 0.002 } else if gamePadLeft.yAxis.value < -0.5 // Down { anchor1.position.z += 0.002 } if gamePadLeft.xAxis.value > 0.5 // Right { anchor1.position.x += 0.002 } else if gamePadLeft.xAxis.value < -0.5 // Left { anchor1.position.x -= 0.002 } } } }
iPhone 11(iOS 18)实际运行效果

无控制状态
触摸控制状态
Game Controller控制状态
内容的提问来源于stack exchange,提问作者Ostoura
相关产品推荐
相关产品推荐

