Open11

【Swift】visionOSアプリ開発スクラップ

Raisuke ShirabeRaisuke Shirabe

壁のすり抜けもisContinuousCollisionDetectionEnabledという継続的判定をすればいい

これやってみてなんとなく通り抜けなくはなったけど
それでも速すぎるとやっぱりすり抜ける
Unityみたいに4段階あってほしい

こちらの記事がわかりやすかった

Raisuke ShirabeRaisuke Shirabe

ParticleはParticleEmitterComponentを使えばできるが、RealityComposerProを使ったが柔軟性あるのかな

entity.components.set(particleComponent())

func particleComponent() -> ParticleEmitterComponent {
  var particles = ParticleEmitterComponent()
  particles.emitterShape = .sphere
  particles.emitterShapeSize = [1,1,1] * 0.05
        
  particles.mainEmitter.birthRate = 2000
  particles.mainEmitter.size = 0.05
  particles.mainEmitter.lifeSpan = 0.5
  particles.mainEmitter.color = .evolving(start: .single(.white), end: .single(.cyan))

  return particles
}
Raisuke ShirabeRaisuke Shirabe

左右のAnchorを保存して手首の距離を計算

  // HandTrackingのAnchorを更新
    func processHandUpdates() async {
        for await update in handTracking.anchorUpdates {
            switch update.event {
            case .updated:
                let anchor = update.anchor
                guard anchor.isTracked else { continue }
                
                if anchor.chirality == .left {
                     self.leftHandAnchor = anchor // 左手のアンカーを保存
                 } else if anchor.chirality == .right {
                     self.rightHandAnchor = anchor // 右手のアンカーを保存
                 }
                
                detectGunGestureTransform(handAnchor: anchor)
                updateTranslationAndShootEnergyBall(handAnchor: anchor)
            default:
                break
            }
        }
    }

    func calculateLeftHandWristToRightHandWristDistance(leftHandAnchor: HandAnchor?, rightHandAnchor: HandAnchor?) -> Float? {
        guard let leftHandAnchor = leftHandAnchor else { return 0 }
        guard let rightHandAnchor = rightHandAnchor else { return 0 }
        let leftWristPosition = leftHandAnchor.originFromAnchorTransform.columns.3.xyz
          let rightWristPosition = rightHandAnchor.originFromAnchorTransform.columns.3.xyz
          
          return distance(leftWristPosition, rightWristPosition)
    }
Raisuke ShirabeRaisuke Shirabe

Particleはこれ

func particleComponent() -> ParticleEmitterComponent {
        var particles = ParticleEmitterComponent()
        
        particles.spawnOccasion = .onDeath
        particles.emitterShape = .sphere
        particles.emitterShapeSize = [0.2, 0.2, 0.2]
        
        particles.speed = -0.1
        particles.birthLocation = .surface
        particles.mainEmitter.birthRate = 1000
        particles.mainEmitter.size = 0.2
        particles.mainEmitter.billboardMode = .billboard
        particles.mainEmitter.color = .evolving(start: .single(.white), end: .single(.cyan))
        
        return particles
    }
Raisuke ShirabeRaisuke Shirabe

カメラの方を向かせるにはDioramaのBilboardSystemがわかりやすい
cameraのpositionはAnchorEntity(.head)だと連続的に取れないっぽいので、WorldTrackingからqueryDeviceAnchorを取得する必要がある

Raisuke ShirabeRaisuke Shirabe

カメラの方に向かってくるのできた

func update(context: SceneUpdateContext) {
    let entities = context.scene.performQuery(Self.query)
    guard let deviceAnchor = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else { return }

    let cameraTransform = Transform(matrix: deviceAnchor.originFromAnchorTransform)
    let cameraPosition = SIMD3(cameraTransform.translation.x, cameraTransform.translation.y - 1.5, cameraTransform.translation.z)

    for entity in entities {
        guard let movingComponent = entity.components[MovingComponent.self] else { return }

        // カメラの方を向く
        entity.look(at: cameraPosition,
                   from: entity.position(relativeTo: nil),
                   relativeTo: nil,
                   forward: .positiveZ)

        // 移動速度を設定
        let moveSpeed: Float = 0.1

        // entityの前進方向を計算
        let forwardDirection = entity.transform.matrix.columns.2.xyz
        let newPosition = entity.position(relativeTo: nil) + (forwardDirection * moveSpeed)

        // 移動処理(毎フレームの更新でスムーズに移動)
        entity.position = newPosition

        entity.components[MovingComponent.self] = movingComponent
    }
}
Raisuke ShirabeRaisuke Shirabe

カメラの位置はSystemを別途作ったほうが各Systemからアクセスできる
↓でSceneにあるEntityにpositionを追加すればいい

func update(context: SceneUpdateContext) {
    guard let device = context.scene.findEntity(named: "Device") else { return }

    guard let deviceAnchor = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else { return }

    device.transform = Transform(matrix: deviceAnchor.originFromAnchorTransform)
}

↓で各Systemから呼び出すときはこれ

guard let device = context.scene.findEntity(named: "Device") else { return }
let deviceTransform = device.transform