VCIで人型の関節を動かす２

#概要
以前、以下の記事を書きました。
https://qiita.com/120byte/items/485e38c935ddc547bb63
当時は子オブジェクトを参照することは出来ないと思い込んでいましたが、どうやら出来るようです。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="cy" dir="ltr">© Unity Technologies Japan/UCL <a href="https://t.co/71PFWC8KAV">pic.twitter.com/71PFWC8KAV</a></p>&mdash; 120/偽葵/青ユニ (@120byte) <a href="https://twitter.com/120byte/status/1114960425936801792?ref_src=twsrc%5Etfw">2019年4月7日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
© Unity Technologies Japan/UCL

#バージョン
バーチャルキャスト：1.5.3
VRM：0.51.0
VCI：0.17

#前書き
VCIで子オブジェクト操作を行う方法ですが、要点は以下のみです。
「SubItem化されていなくても、オブジェクト名を指定すればGetSubItemが可能」
加えて、onTriggerやonCollisionはオブジェクトがSubItem化されていなくても、コライダーが入っていればオブジェクトの名前を取得できます。が、今回は前者の仕組みのみを利用し、後者の仕組みは利用しません。

#仕組み
掴んだオブジェクトの名前だけは状態変数で同期
掴んだオブジェクトのみ回転をキャラクターモデルに適用
触れたタイミングでオブジェクトの回転をリセット（掴んだ後では遅い）

以下は作例です。

#関節操作用オブジェクトの作成
![00.png](https://qiita-image-store.s3.amazonaws.com/0/286581/220e79ce-2a97-19ea-32c4-0912a67d8973.png)
操作したい関節の数だけ図右側のようにSubItemを作成します。（図右側の例は頭用）名前は操作したい関節の名前にアンダーバー"_"を付けてください。

#マーカーオブジェクトの作成
![01.png](https://qiita-image-store.s3.amazonaws.com/0/286581/68ffa841-734c-4ae7-686b-c2a6fae7b0c6.png)
形は自由ですが、操作できる関節に触れていることがわかるように、マーカーを表示します。

#移動と拡縮用の本体コライダーを追加
![02.png](https://qiita-image-store.s3.amazonaws.com/0/286581/e06770fd-fb97-daa1-7729-0ed5063b089f.png)
関節操作用のオブジェクトと干渉しやすいので、モデルに合わせてコライダーの位置を調整します。Z軸方向に余裕を持たせると掴みやすいです。
![03.png](https://qiita-image-store.s3.amazonaws.com/0/286581/4bd37053-6da1-1fae-7010-c349d5d24607.png)

#スクリプト
名前定義は適宜変更してください。

```lua
-- 操作対象のキャラクターモデル
local Root = "AoUni"

-- 操作対象の関節
local Target = {
    "Character1_Head",
    "Character1_LeftArm",
    "Character1_LeftForeArm",
    "Character1_LeftHand",
    "Character1_RightArm",
    "Character1_RightForeArm",
    "Character1_RightHand",
    "Character1_Spine1",
    "Character1_LeftUpLeg",
    "Character1_LeftLeg",
    "Character1_RightUpLeg",
    "Character1_RightLeg"
}

-- マーカーのサイズ
local size = 0.01
local Marker = "Marker"

-- 掴んでる対象名を状態変数で同期
if vci.assets.isMine then
    vci.state.Set("Grab", "")
end

function updateAll()
    local grab = vci.state.Get("Grab")
    local scale = vci.assets.GetSubItem(Root).GetLocalScale()
    for i = 1, #Target do
        GetSetTransform(Target[i], scale, grab)
    end
end

-- 対象に回転操作を適用
function GetSetTransform(target, scale, grab)
    local ctrl = target.."_"
    local ctrl_item = vci.assets.GetSubItem(ctrl)
    local target_item = vci.assets.GetSubItem(target)
    local pos = target_item.GetPosition()

    -- 操作用オブジェクトの位置とサイズ合わせ
    ctrl_item.SetPosition(pos)
    ctrl_item.SetLocalScale(scale * size)

    -- 掴んでる場合は回転操を適用
    if ctrl == grab then
        local rot = ctrl_item.GetRotation()
        target_item.SetRotation(rot)
    end
end

function onTriggerEnter(item, hit)
    if item == Root or item == Marker or hit ~= "HandPointMarker" then
        -- 操作用オブジェクトでなければ中断
        return
    end
    local len = string.len(item) - 1
    local target = vci.assets.GetSubItem(string.sub(item, 0, len))
    local pos = target.GetPosition()
    local rot = target.GetRotation()
    local scale = vci.assets.GetSubItem(Root).GetLocalScale()
    local marker = vci.assets.GetSubItem(Marker)
    
    -- マーカーの位置と大きさ合わせ
    marker.SetPosition(pos)
    marker.SetLocalScale(scale)

    -- 関節に操作用オブジェクトの回転合わせ
    vci.assets.GetSubItem(item).SetRotation(rot)
end

function onTriggerExit(item, hit)
    if item == Root or item == Marker or hit ~= "HandPointMarker" then
        -- 操作用オブジェクトでなければ中断
        return
    end
    -- 見えなければいいんや・・・
    vci.assets.GetSubItem(Marker).SetPosition(Vector3.up * 1000)
end

function onGrab(target)
    vci.state.Set("Grab", target)
    -- 見えなければいいんや・・・
    vci.assets.GetSubItem(Marker).SetPosition(Vector3.up * 1000)
end

function onUngrab(target)
    -- 見えなければいいんや・・・
    vci.assets.GetSubItem(Marker).SetPosition(Vector3.up * 1000)
end
```

#備考
関節操作はたまに動きが怪しくなります。ビクビクというかバタバタというか震える感じに動く時があります。非アクティブ状態の時になりやすい気がします。あと、onTriggerやonCollisionで拾える名前はプレイヤー側の関節？の名前も拾えるみたいなので、色々使えそうですね。飲んだり食べたり系はコレで頭を拾っているのだと思います。

#参考
アイテムの状態を同期する（状態変数を介して全ユーザーで命令を実行する）
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:%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%81%AE%E7%8A%B6%E6%85%8B%E3%82%92%E5%90%8C%E6%9C%9F%E3%81%99%E3%82%8B

青ユニちゃん人形
https://seed.online/user/items/e18c79f32053cfa885d7b82d72e66a0299de2f3a6d531394f375538f4209e054/edit