Edited at

VCIで人型の関節を動かす


概要

VCIで人型オブジェクトの関節を操作できるようにします。

記事を書いてる途中に改めて思いましたが、結構面倒です。



© Unity Technologies Japan/UCL

まぁ、でも頑張ると一人でも画面映えしますよ。

結構ゴリ押しなので、バージョンアップでダメになるかもしれません。


前書き

2019/03/20現在、VCIはwikiに「※SubItemの下の階層にSubItemを配置する事はできません。」とあるとおり、Unityで言う所の子オブジェクトに対する参照ができません。これは、関節の操作をするにあたっては難しい制約です。ので、Fixed Jointを使って無理やり近い振る舞いをさせます。


Unityドキュメントより引用


Fixed Joints は、オブジェクトの動きを別のオブジェクトに依存するよう制限します。これは、親子関係 に若干似ていますが、トランスフォーム 階層ではなく、物理特性を通して実現されます。簡単に分解したいオブジェクトがある場合や、親子関係なしで 2 つのオブジェクトの動きをつなげたい場合などが、最適な使い道です。




仕組み

以下3つが要点です。

・関節ごとに分離してSubItem化

・関節の位置にFixed Jointで繋いだSubItemの作成

・関節操作用のSubItemの作成


関節ごとに分離してSubItem化



人型オブジェクトを根本から辿って、動かしたい関節の部分からVCIObjectの直下に配置してSubItem化します。RigidbodyのUse Gravityは無効にしておきましょう。え?これ動くの?と、私も思いましたが、動きます。UnityEditor上で確認できます。画像の例は「首腕肘足膝」計9関節です。


関節の位置にFixed Jointで繋いだSubItemの作成



各関節と同じ位置に別途SubItemを作成して、その親に該当するオブジェクトをFixed Jointで繋ぎます。RigidbodyのUse Gravityは無効にしておきましょう。位置はTransformの右上の歯車からCopy Componen→Paste Component Valuesで同じ位置を設定できます。画像の例は頭なので、親は人型オブジェクトの根本にしています。肘(Elb)なら腕(UpperArm)、膝(Kne)なら足(UpperLeg)にします。


関節操作用のSubItemの作成



各関節の操作用に別途SubItemを作成します。操作用なのでColliderを追加します。Colliderの形と大きさを調整して、IsTriggerを有効にします。SubItemはGrabbableを有効にして、ここでもUse Gravityは無効にしておきましょう。


全体の位置移動と同期

お疲れ様です。一応ここまでの設定で関節操作の準備は整いました。が、全体の位置移動と同期のためにもうひと踏ん張り必要です。



全体の位置移動のため、人型オブジェクトの根本(Fixed Joint視点で見ても根本)にColliderを追加して、形と大きさを調整、IsTriggerを有効にします。SubItemのGrabbableを有効にして、ここでもUse Gravityは無効です。あとは同期のため、全てのSubItemのGroupIDを1に設定します。

※ここでのColliderの大きさは肩用のColliderと干渉しやすいので、小さめがオススメです。


スクリプト

仕上げにVCIObjectへスクリプトを貼り付ければ完成です。各SubItemの名前は適宜読み替えて変更してください。

local Obj = {

"J_Bip_C_Head",
"J_Bip_L_UpperArm",
"J_Bip_R_UpperArm",
"J_Bip_L_LowerArm",
"J_Bip_R_LowerArm",
"J_Bip_L_UpperLeg",
"J_Bip_R_UpperLeg",
"J_Bip_L_LowerLeg",
"J_Bip_R_LowerLeg"
}

local Pos = {
"Pos_Head",
"Pos_ArmL",
"Pos_ArmR",
"Pos_ElbL",
"Pos_ElbR",
"Pos_LegL",
"Pos_LegR",
"Pos_KneL",
"Pos_KneR"
}

local Rot = {
"Rot_Head",
"Rot_ArmL",
"Rot_ArmR",
"Rot_ElbL",
"Rot_ElbR",
"Rot_LegL",
"Rot_LegR",
"Rot_KneL",
"Rot_KneR"
}

local Root = "Sample"

local RootGrab = false

function updateAll()
for i = 1, #Pos do
local pos = vci.assets.GetSubItem(Pos[i]).GetPosition()
vci.assets.GetSubItem(Obj[i]).SetPosition(pos)
vci.assets.GetSubItem(Rot[i]).SetPosition(pos)
end
for i = 1, #Rot do
local rot = vci.assets.GetSubItem(Rot[i]).GetRotation()
vci.assets.GetSubItem(Obj[i]).SetRotation(rot)
end
if RootGrab then
for i = 1, #Pos do
local rot = vci.assets.GetSubItem(Pos[i]).GetRotation()
vci.assets.GetSubItem(Obj[i]).SetRotation(rot)
vci.assets.GetSubItem(Rot[i]).SetRotation(rot)
end
end
end

function onGrab(target)
if target == Root then
RootGrab = true
end
end

function onUngrab(target)
for i = 1, #Obj do
Stop(Obj[i])
Stop(Pos[i])
Stop(Rot[i])
end
Stop(Root)
RootGrab = false
end

function Stop(target)
local SubItem = vci.assets.GetSubItem(target)
SubItem.SetVelocity(Vector3.__new(0, 0, 0))
SubItem.SetAngularVelocity(Vector3.__new(0, 0, 0))
end

知能が感じられない脳筋スクリプトですが、一応動きます。(ご指摘お待ちしています!


参考

VCI作成

https://virtualcast.jp/wiki/doku.php?id=%E3%83%A2%E3%83%87%E3%83%AB%E4%BD%9C%E6%88%90:vci%E4%BD%9C%E6%88%90:vci%E4%BD%9C%E6%88%90

使用可能なUnityComponent

https://virtualcast.jp/wiki/doku.php?id=%E3%83%A2%E3%83%87%E3%83%AB%E4%BD%9C%E6%88%90:vci%E4%BD%9C%E6%88%90:%E4%BD%BF%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AAunitycomponent

Fixed Joint

https://docs.unity3d.com/jp/current/Manual/class-FixedJoint.html

GroupIDについて

https://virtualcast.jp/wiki/doku.php?id=%E3%83%A2%E3%83%87%E3%83%AB%E4%BD%9C%E6%88%90:vci%E4%BD%9C%E6%88%90:groupid%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

バーチャルキャストのVCIスクリプトの例

https://qiita.com/sakano/items/adc88174a7dfd32ae248

サンプルVCI

https://seed.online/items/8d7e3473bd5d31e872a0bffb6829aea468bdc45b9a44e159cb05600226cc3d62