import SwiftUI import RealityKit import RealityKitContent struct ContentView3: View { @State private var errorMessage: String? // エラーメッセージを表示するための状態 @State private var modelEntity: Entity? // モデルを保持する状態 @State private var animationController: AnimationPlaybackController? // アニメーションのコントローラー @State private var isAnimationPlaying: Bool = false // アニメーション再生状態 @State private var lastDragLocation: CGPoint? // 最後のドラッグ位置 var body: some View { VStack { // エラーメッセージを表示 if let errorMessage = errorMessage { Text(errorMessage) .foregroundColor(.red) .padding() } // RealityViewでモデルとアニメーションを表示 RealityView { content in do { if modelEntity == nil { if let entity = try? await Entity(named: "step_all") { // モデルの位置やサイズを設定 entity.position.y = -0.45 // pivot考慮 entity.scale *= 0.2 // モデルを状態に保持 modelEntity = entity // コンテンツにモデルを追加 content.add(entity) errorMessage = nil } else { errorMessage = "モデルの読み込みに失敗しました。" } } } catch { errorMessage = "モデルの読み込みに失敗しました。" print("Error loading model: \(error)") } } .gesture( DragGesture() .onChanged { value in guard let modelEntity = modelEntity else { return } if let lastLocation = lastDragLocation { let deltaX = value.location.x - lastLocation.x let deltaY = value.location.y - lastLocation.y let rotationX = simd_quatf(angle: Float(-deltaY) * 0.01, axis: [1, 0, 0]) let rotationY = simd_quatf(angle: Float(-deltaX) * 0.01, axis: [0, 1, 0]) modelEntity.transform.rotation = rotationY * rotationX * modelEntity.transform.rotation } lastDragLocation = value.location } .onEnded { _ in lastDragLocation = nil } ) // アニメーション再生/停止ボタン Button(action: toggleAnimation) { Text(isAnimationPlaying ? "アニメーションを停止" : "アニメーションを再生") .padding() .background(isAnimationPlaying ? Color.blue : Color.blue) .foregroundColor(.white) .cornerRadius(8) } .padding() } } // アニメーションの再生/停止を切り替える関数 private func toggleAnimation() { guard let modelEntity = modelEntity else { errorMessage = "モデルがロードされていません。" return } if modelEntity.availableAnimations.isEmpty { errorMessage = "アニメーションはありません。" // アニメーションがない場合にエラーメッセージを設定 return } if isAnimationPlaying { // アニメーションを停止 animationController?.pause() } else { // 停止位置から再生 if let animation = modelEntity.availableAnimations.first { if let controller = animationController { controller.resume() // 停止位置から再開 } else { animationController = modelEntity.playAnimation(animation.repeat(duration: .infinity)) } } } // 状態を更新 isAnimationPlaying.toggle() errorMessage = nil // 正常に動作する場合はエラーをクリア } } #Preview(windowStyle: .automatic) { ContentView3() } import SwiftUI import RealityKit struct ContentView4: View { @State private var modelEntity: ModelEntity? // モデルエンティティ @State private var isAnimationPlaying = false // アニメーションの再生状態 @State private var animationController: AnimationPlaybackController? // アニメーションコントローラ @State private var errorMessage: String? // エラーメッセージを表示するための状態 @State private var rotationY: Float = 0.0 // Y軸の回転角度 @State private var rotationX: Float = 0.0 // X軸の回転角度 var body: some View { VStack { // エラーメッセージの表示 if let errorMessage = errorMessage { Text(errorMessage) .foregroundColor(.red) .padding() } // アニメーション再生・停止を切り替えるボタン Button(action: toggleAnimation) { Text(isAnimationPlaying ? "アニメーション停止" : "アニメーション再生") .font(.title) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(10) } .padding() RealityView { content in if let modelEntity = modelEntity { content.add(modelEntity) } else { Text("モデルを読み込んでいます...") .foregroundColor(.gray) .padding() } } .gesture( DragGesture().onChanged { value in // ドラッグによる回転角度の計算 rotationY += Float(value.translation.width) * 0.005 // 横方向 rotationX += Float(value.translation.height) * 0.005 // 縦方向 // 回転を適用 if let modelEntity = modelEntity { modelEntity.transform.rotation = simd_quatf(angle: rotationY, axis: [0, 1, 0]) * simd_quatf(angle: rotationX, axis: [1, 0, 0]) } } ) .onAppear { loadModelFromRemoteURL() } } } // アニメーション再生・停止の切り替えメソッド private func toggleAnimation() { guard let modelEntity = modelEntity else { return } if isAnimationPlaying { // アニメーションを停止 animationController?.pause() } else { // 停止位置から再生 if let animation = modelEntity.availableAnimations.first { if let controller = animationController { controller.resume() // 停止位置から再開 } else { animationController = modelEntity.playAnimation(animation.repeat(duration: .infinity)) } } } // 状態を更新 isAnimationPlaying.toggle() errorMessage = nil // 正常に動作する場合はエラーをクリア } // リモートからモデルをダウンロードして読み込むメソッド private func loadModelFromRemoteURL() { guard let url = URL(string: "http://192.168.2.101/practice/dice_anime.usdz") else { errorMessage = "無効なURLです。" return } let task = URLSession.shared.downloadTask(with: url) { localURL, response, error in if let error = error { DispatchQueue.main.async { errorMessage = "モデルのダウンロードに失敗しました: \(error.localizedDescription)" } return } guard let localURL = localURL else { DispatchQueue.main.async { errorMessage = "モデルのダウンロードURLが不正です。" } return } do { let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("dice_anime.usdz") try FileManager.default.moveItem(at: localURL, to: tempURL) let loadedEntity = try Entity.load(contentsOf: tempURL) DispatchQueue.main.async { let modelEntity = ModelEntity() for child in loadedEntity.children { modelEntity.addChild(child) } // アニメーションの確認 if let animation = loadedEntity.availableAnimations.first { animationController = modelEntity.playAnimation(animation.repeat(duration: .infinity)) isAnimationPlaying = true errorMessage = nil } else { errorMessage = "アニメーションが見つかりませんでした。" } modelEntity.position.y = -0.1 modelEntity.scale *= 1.0 self.modelEntity = modelEntity } } catch { DispatchQueue.main.async { errorMessage = "モデルの読み込みに失敗しました: \(error.localizedDescription)" } } } task.resume() } } #Preview { ContentView4() }