Apple Engine

Apple, iPhone, iOS, その周辺のことについて

iOS で SceneKit を試す(Swift 3) その64 - SCNSkinner と Bone について

人型のキャラクタなどに稼動できる骨を入れて、骨を動かすと該当箇所のジオメトリが変化する 所謂、Skinning と Bone を使用してアニメーションを SCNSkinner が行う。

f:id:x67x6fx74x6f:20170814142434p:plain

公式ドキュメントの画像を参照

 

正直、コード上で書くのは手間がかかり、Scene Editor で編集ができないため、 他のゲームエンジンと同様に 3DCG のオーサリングツールで Bone を入れ、 アニメーションを設定したオブジェクトファイルを作成した方が速い。

 

使い方

コードで設定する場合は、 キャラクタなどのスキンにボーンと各種設定を SCNNode から適応させる。

init(baseGeometry: SCNGeometry?, 
            bones: [SCNNode], 
boneInverseBindTransforms: [NSValue]?, 
     boneWeights: SCNGeometrySource, 
     boneIndices: SCNGeometrySource)
パラメーター名 説明
baseGeometry キャラクタなど変形させたいジオメトリ。スキン
bones 階層化されたボーン情報。SCNNode がボーンとなるためその配列を渡す
boneInverseBindTransforms ボーンの初期位置。SCNMatrix4 が格納された NSValue の配列
boneWeights ボーンが boneIndices で設定したスキンの頂点に対してどれだけの影響があるかを設定したもの。所謂、ウエイト。ここで設定する Geometry Source の頂点用のベクトルが4つの要素以上になると GPU から CPU に移りパフォーマンスが悪くなるので注意
boneIndices ボーンが動いた際、スキン上のどの頂点を動かすか設定する

 

Scene Editor での見えかた

Scene Graph View でのスキン。
Attributes に Skinner が設定されている。

f:id:x67x6fx74x6f:20170814142525p:plain

 

Scene Graph View でのボーン。
Xcode のメニューバー Editor > Display > Show Joints で簡易表示される。

f:id:x67x6fx74x6f:20170814142546p:plain

 

わかりづらいので、同じく Show Bounding Box でボーンの範囲を表示している。

f:id:x67x6fx74x6f:20170814142605p:plain

 

Xcode 9 からは 3DCG のオーサリングツール同様に正八面体を伸ばした形でボーンが表示される。

 

アニメーション

f:id:x67x6fx74x6f:20170814142626g:plain

 

設定しているボーンをスキンに渡す

設定されているボーンの集まりであるスケルトンは、以下の方法で他のジオメトリに渡すことができる。

SCNNodeA.skinner.skeleton = SCNNodeB.skinner.skeleton

 

他のノードのボーンアニメーションを使用する

ボーンの構造が同じであれば、他の scn やオブジェクトファイルから呼び出してアニメーションをすることができる。

let url = Bundle.main.url(forResource: "art.scnassets/boss_attack", withExtension: "dae")
let sceneSource = SCNSceneSource(url: url!, options: [
    SCNSceneSource.LoadingOption.animationImportPolicy : SCNSceneSource.AnimationImportPolicy.doNotPlay
    ])

let attackAnimation:CAAnimation = sceneSource!.entryWithIdentifier("attackID", withClass: CAAnimation.self)!
attackAnimation.fadeInDuration = 0.3
attackAnimation.fadeOutDuration = 0.3

baseNode.addAnimation(attackAnimation, forKey: "attackID")

 

IK (Inverse Kinematics) を使用してみる

コードでのみで設定可能である点と、3DCG のオーサリングツールでキャラクタアニメーションを作る関係上、あまり使用されることはないと思われるが機能的には存在している。

shoulderNode を肩のボーンとし、handNode を手のボーン。 そこまでを SCNIKConstraint の inverseKinematicsConstraint(chainRootNode:) で設定し

SCNIKConstraint の targetPosition で移動させる。

let ik:SCNIKConstraint = SCNIKConstraint.inverseKinematicsConstraint(chainRootNode: shoulderNode)
handNode.constraints = [ik]

ik.targetPosition = SCNVector3(2, 0, 2)

 

今回はここまで