visionOSでモデルの骨を動かす
概要
Xcode上で骨が入ったモデルを読み込んだ時に、読み込んだモデルの骨を操作する方法をまとめました。
モデルの読み込み
骨を操作するためのモデルを読み込みます。
今回はプロジェクト内に usdz モデルを配置し、読み込む方法で読み込んでいます。
まずはModelsフォルダを追加しそこに動かす usdz を配置します。
プロジェクトの Add File to ...
から 追加したフォルダを選択します。
この時 Added folders
が Create groups
に Add to targets
が 対象のプロジェクト
になっていることを確認してください。
コピーされる対象がフォルダなのか内部のコンテンツなのかが変わり読み込む時の named
のパスが変わります。
Create groups
にするとフォルダはプロジェクトの管理のための Group
として使用されるだけで、フォルダ構造は維持せずコピーされるので、named
に指定するパスは ファイル名のみになります。
Create folder references
にすると フォルダがコピーされて使用されるため、フォルダ構成を維持したパスを指定する必要があります。
Create groupsで追加した状態
guard var robot = try? await ModelEntity(named: "robot") else { return }
Create folder referencesで追加した状態
guard var robot = try? await ModelEntity(named: "Models/robot") else { return }
骨の回転
読み込んだモデルの骨はjointNames
とjointTransforms
に配列で入っています。
for i in 0..<robot.jointNames.count {
print("Joint(\(i)):\(robot.jointNames[i])")
}
こちらのjointNames
を出力して何番目に何が入っているか確認します。
Joint(0):root
Joint(1):root/hips_joint
Joint(2):root/hips_joint/left_upLeg_joint
Joint(3):root/hips_joint/left_upLeg_joint/left_leg_joint
Joint(4):root/hips_joint/left_upLeg_joint/left_leg_joint/left_foot_joint
Joint(5):root/hips_joint/left_upLeg_joint/left_leg_joint/left_foot_joint/left_toes_joint
Joint(6):root/hips_joint/left_upLeg_joint/left_leg_joint/left_foot_joint/left_toes_joint/left_toesEnd_joint
Joint(7):root/hips_joint/right_upLeg_joint
Joint(8):root/hips_joint/right_upLeg_joint/right_leg_joint
...
今回は 腰を回転させてみようかと思うので、Joint(14):root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint Join
を使用したいと思います。
let spineJointIndex = 14
let rotateRadian = Float(Angle(degrees: 1).radians)
Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { _ in
robot.jointTransforms[spineJointIndex].rotation *= simd_quaternion(rotateRadian, [1,0,0])
}
これで腰の骨を回転させることができました!
アニメーションを使用しての回転
先ほどは 直接Transformを操作して回転をさせましたが、アニメーションを使用して回転させることも可能です。
こちらの方法では開始地点と終了地点を指定してアニメーションをさせることが可能です。
今回は 右肩を動かして手を振りたいと思います。
Joint(35):root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint/spine_4_joint/spine_5_joint/spine_6_joint/spine_7_joint/right_shoulder_1_joint
が右肩だったため右肩を指定してアニメーションを作成します。
let rightShoulderJointName = "root/hips_joint/spine_1_joint/spine_2_joint/spine_3_joint/spine_4_joint/spine_5_joint/spine_6_joint/spine_7_joint/right_shoulder_1_joint"
let rightShoulderJointIndex = 35
// 右肩の骨を取得します
let rightSholderJoint = robot.jointTransforms[rightSholderJointIndex]
// アニメーションの開始ポーズの作成
let fromTransforms: [Transform] = [rightShoulderJoint]
let fromPose = JointTransforms(fromTransforms)
// アニメーションの終了ポーズの作成
let toTransforms: [Transform] = [Transform(scale: [1, 1, 1], rotation:simd_quatf(angle: -.pi, axis: [0,1,0]), translation:rightShoulderJoint.translation)]
let toPose = JointTransforms(toTransforms)
最初と最後のポーズが作成できたら FromToByAnimation<JointTransforms>
から AnimationResource
を作成していきます。
let jointNames = [rightShoulderJointName]
var fromToBy = FromToByAnimation<JointTransforms>()
// アニメーション名を指定(なんでも良い)
fromToBy.name = "anim"
// 1秒をかけてアニメーションする
fromToBy.duration = 1
// .autoReverse で終わったら戻るようにする
fromToBy.repeatMode = .autoReverse
// 対象の骨の名前を指定
fromToBy.jointNames = jointNames
// 最初のポーズを指定
fromToBy.fromValue = fromPose
// 最後のポーズを指定
fromToBy.toValue = toPose
// JointTransformsを動かすので .joinTransformsを指定
fromToBy.bindTarget = .jointTransforms
// アニメーションリソースを生成
let animationResource = try? AnimationResource.generate(with: fromToBy)
AnimationResource
が作成できたら、ModelEntity
からそのアニメーションを再生します。
robot.playAnimation(animationResource!)
これでアニメーションを使用した回転もできました!
まとめ
今回は モデルの骨の動かし方についてまとめてみました。
簡単な動きであれば作れますが複雑な動きは難しので、複雑な動きを行いたい場合は 外部ツールでモデルにアニメーションを作るか、Unityを使用するのが良いかと思います!
書いた人
佐藤 寿樹
株式会社コナミデジタルエンタテインメントに入社し5年間ウイニングイレブンのオンライン実装に携わる。
その後、株式会社コロプラで9年間エンジニアとしてアプリ開発・運用を行い、位置情報やARを使用したARゲーム開発、OculusRiftやPSVRなどのVRゲーム開発を経験しMESONへ入社。
MESON Works
MESONの制作実績一覧もあります。ご興味ある方はぜひ見てみてください。
Discussion