LoginSignup
1
2

More than 1 year has passed since last update.

Blender で作成したアニメーション付きモデルを ARKit で配置して遊ぼう - ARKit編

Last updated at Posted at 2021-12-01

Blender で作成したアニメーション付きモデルを ARKit で配置する方法を解説していきます。
今回は ARKit で実際にアプリで表示できるようにしてみましょう。

前回の記事は以下です。

アニメーションの名前をチェックする

まずは作成したアニメーションの名前を知る必要があるので、作成したdaeファイルをテキストエディタで開いてください。下図の矢印が指しているところがアニメーションの名前となります。
おそらく unnamed_animation__1 と命名されていると思います。名前をコピーしたらテキストエディタは閉じても構いません。
(なぜか Blender 側でアニメーションの名前を設定していても unnamed_animation__1 になってしまいます...)
11

Xcodeにデータを取り込む

File > New > File... から、SceneKit Catalog を作成します。これが 3Dデータのアセットを保存する場所になります。
そして、作成した art.scnassets の中に daeファイルをドラッグ&ドロップしてください。
12

ARKit で実装

UIKit の資産を使うので、UIViewControllerRepresentable でラップしてあげます。
あとは ARSCNView を使って実装していきましょう。
アニメーションは別に呼び出して addAnimation で追加してあげないと動かないので注意です。


import SwiftUI
import RealityKit
import ARKit

// SwiftUI 用の UIViewController ラッパー
struct ARViewControllerContainer: UIViewControllerRepresentable {
  typealias UIViewControllerType = ARViewController
  let arViewController: ARViewController

  init() {
    arViewController = ARViewController()
  }

  func makeUIViewController(context: Context) -> ARViewController {
    return arViewController
  }

  func updateUIViewController(_ arViewController: ARViewController, context: Context) {}
}

class ARViewController: UIViewController, ARSCNViewDelegate {
  let modelName= "Yaguchi_Panic" // モデルの名前
  let animationName = "unnamed_animation__1" // アニメーションの名前
  var sceneView = ARSCNView()
  let configuration = ARWorldTrackingConfiguration()
  var animations = [String: CAAnimation]()
  var idle: Bool = true
  var n = 0
  var modelNode = [SCNNode()]
  var isDetectPlane = false
  var frameNode: SCNNode!
  var planeNode: SCNNode? = nil
  var isShowingAnnotation = false
  var size: Float = 0.05

  // 画面ロード時に sceneView の設定をする
  override func viewDidLoad() {
    super.viewDidLoad()
    self.configuration.planeDetection = .horizontal
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped))
    self.sceneView.addGestureRecognizer(tapGestureRecognizer)
    sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
    self.view = sceneView
  }

  // 画面表示時にセッションを開始する
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.sceneView.session.run(configuration)
    self.sceneView.autoenablesDefaultLighting = true
  }

  // 画面非表示時にセッションを停止する
  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    sceneView.session.pause()
  }

  // 画面タップ時の動作
  @objc func tapped(sender: UITapGestureRecognizer) {
    // タップされた位置を取得する
    let tapLocation = sender.location(in: sceneView)

    // タップされた位置のARアンカーを探す
    guard let raycast = sceneView.raycastQuery(from: tapLocation, allowing: .estimatedPlane, alignment: .any) else { return }
    guard let result = sceneView.session.raycast(raycast).first else { return }
    self.addItem(hitTestResult: result)
  }

  /// アイテム配置メソッド
  private func addItem(hitTestResult: ARRaycastResult) {
    sceneView.debugOptions = []

    // 現実世界の座標を取得
    let transform = hitTestResult.worldTransform
    let thirdColumn = transform.columns.3

    // モデルをロードしてシーンにノードを追加
    let heroScene = SCNScene(named: "art.scnassets/\(modelName).dae")
    for childNode in heroScene!.rootNode.childNodes {
      modelNode[n].addChildNode(childNode)
    }

    // モデルからシーンのソースを作成
    let sceneURL = Bundle.main.url(forResource: "art.scnassets/\(modelName)", withExtension: "dae")
    let sceneSource = SCNSceneSource(url: sceneURL!, options: nil)

    // モデルのシーンからアニメーションを呼び出す
    if let animationObject = sceneSource?.entryWithIdentifier("\(animationName)", withClass: CAAnimation.self) {
      animationObject.repeatCount = 0
      // モデルにアニメーションを追加
      modelNode[n].addAnimation(animationObject, forKey: nil)
    }

    // モデルのサイズを設定
    modelNode[n].scale = SCNVector3Make(size, size, size)

    // モデルの向きをカメラ方向に設定
    if let camera = sceneView.pointOfView {
      modelNode[n].eulerAngles.y = camera.eulerAngles.y
    }

    // アイテムの配置
    modelNode[n].position = SCNVector3(thirdColumn.x, thirdColumn.y, thirdColumn.z)

    // シーンビューに追加する
    sceneView.scene.rootNode.addChildNode(modelNode[n])
    n += 1

    // ノードを追加する
    modelNode.append(SCNNode())
  }

  func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    print("renderer: didAdd")
  }
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2