在 iOS 18 中使用 RealityView 实现立体视图 [已关闭]

问题描述 投票:0回答:2

我正在尝试在 iOS 18 中使用 RealityView 和 SwiftUI 制作 VR 立体 3d 视图

问题:两侧没有连接在一起,这意味着当左侧视图上的 3D 项目移动时,我看不到右侧的项目以相同的方式移动或根本没有移动

预期:两个 Sids 应与单个用户移动同步移动

这是到目前为止我的代码,

import SwiftUI
import RealityKit

struct ContentView: View {
  @State var box = AnchorEntity()
  @State var box2 = AnchorEntity()

  @State private var rotateTime: Timer!

  @State var rotateCounter: Float = 0.0

  var body: some View {
    
    HStack {
        RealityView{ content in
            
            let item = ModelEntity(mesh: .generateBox(size: .init(0.5, 0.5, 0.5)), materials: [SimpleMaterial(color: .white, isMetallic: false)])
            box.addChild(item)
            // rotate box on x and y 45 degrees
            box.transform.rotation = simd_quatf(angle: .pi / 4, axis:[0,1,1])

            content.add(box)
        }

        RealityView{ content in
            content.add(box.clone(recursive: true))
        }

    }
    .background(.black)
    .onAppear(){
        
        // After 5 Sec the animation starts
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5.0)
        {
            //-- Run Rotation Loop ----
            rotateTime?.invalidate() // to make sure no instance for it before
            
            rotateTime = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in
                rotateMe()
            })
        }
    }
}

func rotateMe(){
    
    rotateCounter += 1
    
    if rotateCounter > 360 {
        rotateCounter = 0.0
    }

    box.transform.rotation = simd_quatf(angle: (rotateCounter * (.pi / 180)), axis: [0,1,0])
  }
}

当代码启动时,它将等待 5 秒,然后开始尝试左视图框的动画 enter image description here

enter image description here

swift swiftui virtual-reality realitykit realityview
2个回答
1
投票

如果每个锚实体都是唯一的,一切都会正常工作。

此外,

iOS 18
/
iPadOS 18
模拟器无法正常工作。

使用设备!

import SwiftUI
import RealityKit

struct ContentView : View {
    @State var anchor1 = AnchorEntity()
    @State var anchor2 = AnchorEntity()
    @State var rotateTime: Timer!
    @State var rotateCounter: Float = 0.0

    var body: some View {
        HStack {
            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)
            }
            RealityView { rvc in
                let item = ModelEntity(mesh: .generateBox(size: 0.5), 
                                  materials: [SimpleMaterial()])
                anchor2.addChild(item)
                anchor2.orientation = .init(angle: .pi/4, axis:[0,1,1])
                rvc.add(anchor2)
            }
        }
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                rotateTime?.invalidate()
                rotateTime = Timer.scheduledTimer(
                    withTimeInterval: 0.01, repeats: true, block: { _ in
                        rotate(anchor1)
                        rotate(anchor2)
                    }
                )
            }
        }
        .background(.black)
    }
    func rotate(_ anchor: AnchorEntity) {
        rotateCounter += 1
        
        if rotateCounter > 360 { rotateCounter = 0.0 }
        
        anchor.orientation = .init(angle: (rotateCounter * (.pi/180)), axis: [0,1,0])
    }
}

0
投票

感谢 Andy Jazz,我编辑了回复以允许一个 RealityView,但我仍然无法同时控制两个视图,现在我添加了一个拖动选项,但它分别适用于每个视图,并且双击可停止动画并重新启动它,它也可以单独工作仍然需要研究如何使控制同步在一起,我将检查碰撞是否也可以同步工作 这是代码

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()
            }
        }
  }
}

由于在 VR 中我们无法触摸屏幕,所以这里是通过触摸和使用游戏控制器进行控制的代码

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
        }
    }
}

}

这是真实 iPhone11 与 iOS18 的结果照片

enter image description here 无法控制

enter image description here 使用触摸

enter image description here 使用游戏控制器

© www.soinside.com 2019 - 2024. All rights reserved.