VRC Head Chopを利用して視界を遮らない帽子の持ち替えギミックを実装する

# はじめに

今回は、少し前のアップデートで密かに追加された「**VRC Head Chop Component**」を使って視界を遮らない帽子の持ち替えギミックを実装する方法をご紹介します。

ドキュメントを読んでいたら発見したのですが、日本人向けの記事が見当たらなかったため拙筆ながら初めて記事を書いてみることにしました。

本記事の目的は、改変に少し慣れてきた方向けに**VRC Head Chop Component**の紹介とその具体的な使い方を応用したギミックの作り方を詳しく解説することです。記事後半にてVRC Constraintを用いた持ち替えギミックにHead Chopを使用して、頭の上にある時だけ視界を遮らない帽子を作成していきます。

また、この記事は以下の読者層向けに書かれています。
```
VRChatでアバターをアップロードしたことがある
UnityのAnimatorやComponentについて多少の知識がある
```
それでは早速本題に入っていきます。

## 目次
最初の方に詳細説明が入るので改変手順だけ見たい方は[ここ](#ギミックの作成)まで飛ばしてください。

- [はじめに](#はじめに)  
- [VRC Head Chopの概要](#vrc-head-chopの概要)  
   - [VRChatでのHeadBoneの仕様について](#vrchatでのheadboneの仕様について)  
   - [コンポーネントの詳細](#コンポーネントの詳細)  
   - [Modular AvatarのVisible Head Accessaryとの違いについて](#modular-avatarのvisible-head-accessaryとの違いについて)  
- [ギミックの作成](#ギミックの作成)  
   - [概要](#概要)  
   - [前提](#前提)  
   - [実装手順](#実装手順)  
      - [1. Prefabの準備](#1-prefabの準備)  
      - [2. Constraintの設定](#2-constraintの設定)  
        - [2.1 Parent Constraintを設定](#21-parent-constraintを設定)  
      - [3. Contactの設定](#3-contactの設定)  
        - [3.1 Contact Sender(手側)の設定](#31-contact-sender手側の設定)  
        - [3.2 Contact Receiver(頭側)の設定](#32-contact-receiver頭側の設定)  
      - [4. HeadChopとAnimatorの設定](#4-headchopとanimatorの設定)  
        - [4.1 コンポーネントの設定](#41-コンポーネントの設定)  
        - [4.2 Animatorの編集アニメーション編](#42-animatorの編集アニメーション編)  
        - [4.3 Animatorの編集トランジション編](#43-animatorの編集トランジション編)  
      - [5. 確認](#5-確認)  
- [まとめ](#まとめ)

## VRC Head Chopの概要
まずこのコンポーネントの機能について一言で纏めるとするならば、**自分の視界(Local)から見える対象オブジェクトの大きさを制御する**というものです。

ドキュメントでは「自分の髪を自分の視界から見えたままにする」という活用が主に想定される使い方として示されています。
英語が読める方はまず本家ドキュメントに目を通しましょう。

https://creators.vrchat.com/avatars/avatar-dynamics/vrc-headchop/

ここからは前提知識も含めてなるべく伝わるように当機能を解説していきます。

#### VRChatでのHeadBoneの仕様について
そもそもVRChatでHead以下すべての子にスキニングされているオブジェクトは、(View Pointを塞いで視界の邪魔にならないために)自分の視界からのみ表示されない仕様となっています。
別にシェーダーで隠しているわけでも本当にメッシュが消えているわけでもなく、単にローカル視点のみheadのsizeが0になっているのです。

そのため、HeadBoneの子として帽子の持ち替えを実装しようとした場合...こうなります。

![IMG_1848.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/16f484fd-c853-4fa4-1207-7c603756745e.png)

視界側の手の先に帽子が表示されていませんね。これは実際にはローカル視点からのみ発生している不具合です。もしあえてこちらの方が都合いい場合、この先を読む必要はないでしょう。

#### コンポーネントの詳細

ここからが本題です。まずプロパティを見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/e6687a94-4988-c4ea-0614-b14d5482c4fb.png)

|プロパティ|説明|
|-|-|
|**Target Bones**|制御するボーンのリストで、このコンポーネントで最大32個まで設定できる。それぞれのボーンを個別に設定可能。|
|**Transform**|制御するボーン。|
|**Scale Factor**|ローカル視点でのスケール(0で完全に非表示)。|
|**Apply Condition**|Scale Factorを適用する条件を制御します。|
|- Always apply| - 常に適用（デフォルト）|
|- VR only| - VRのみ|
|- Non-VR only| - 非VRのみ|
|**Global Scale Factor**|各Scale Factorに乗算します。全てのボーンのスケールを一度に変更したい場合を除いて、通常は1のままにしておきます。|

シンプルな構成ですね。正直説明がなくても直観的に分かりそうです。

この中でAnimatorから制御できるのはコンポーネント自体の有効無効とGlobal Scale Factorのみとなります。
また、このコンポーネントやターゲットボーンは必ずしもHead内部にある必要はありません。つまりはHeadボーン外にあるボーンや小物オブジェクトのローカルスケールも制御できるという訳ですね。

#### Modular AvatarのVisible Head Accessaryとの違いについて
「あれ？MAに似たような機能があったような...」と思った方向けにVisible Head AccessaryとHead Chopの違いを説明いたします。必要ない場合読み飛ばして頂いて構いません。

Visible Head AccessoryはModular Avatarのコンポーネントで、Headボーン以下の特定のオブジェクトを一人称視点で表示させるために提供されています。 
仕組みとしては自動的にHeadボーンを複製し、Parent Constraintで本来のHeadボーンに追従させるというものです。
代替的に動作させているVisible Head Accessoryと比べて、VRC Head ChopはVRCから提供されているため本来のHead Boneの仕様を直接利用することができる他、コンストレイント一個分ではありますが軽いです。

## ギミックの作成
ここからは実際に帽子を使用して適切に取り外し出来るギミックを作っていきます。
コンストレイントで持ち替えギミックが実装できる人は[HeadChopとAnimatorの設定](#4-headchopとanimatorの設定)まで飛ばして頂いて構いません。

#### 概要

誤解を恐れずに簡単に図解するとこんな感じ。

![switchHat.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/afd69860-f81c-3e6c-e513-7143d0ff4aea.png)

Contact Senderを手に、Contact Receiverを頭に付けて帽子の移動はアニメーターで行います。`HatContact`と名付けたパラメータを介して手と頭が触れていることを認識&グリップを押した(手の形がFist OR Gun OR Point OR ThumbsUpの)ときに帽子を持つようにします。

ContactやConstraintについては都度簡易的に説明を挟みますが、如何せん詳しく書こうとするとこの記事の文量がとんでもないことになってしまうので省きます。完全に理解できなくても大丈夫です。
Unityの神になりたい方は専門的に解説している記事を見ていただければと思います。

御託を並べるより実際に作ってみたほうがよく理解できるでしょう。


#### 前提

以下の環境で進めていきます。導入までは省くので各自の判断で入れてきてください。
```
VCC
Unity 2022.3.22f1(2022版以降推奨)
VRChat SDK 3.7.3
GestureManager 3.9.2(動作確認用)
ModularAvatar 1.10.8(FXを非破壊的に統合するため)
```
2022 Avatar projectにGestureManagerとModularAvatarが導入された状態です。

画像例ではこのシルクハットを使用していますが、配布はしないのでFBXモデルもフリーのものなど各自でご用意頂けると幸いです。

https://nanarima.booth.pm/items/6338339

### 実装手順

#### 1. Prefabの準備
まずアバターを配置してください。
Assetsタブで右クリックして設定用の新規プレハブを追加します。分かりやすいようにリネームしておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/aafb511d-3f68-4541-68fa-f6866bb09e4e.png)

アバター直下にプレハブを配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/dd09a0e5-b428-b3d3-96c8-d010378009b7.png)

被せるための帽子をプレハブの下に配置して、なんとなく位置を合わせておきます(後で再調整する羽目になるのでなんとなくでOK)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/901d4d5e-973c-9d46-afba-f8b6cf7ec534.png)

また頭に被る位置や手に持つ位置の調整を楽にするため、プレハブ直下にヌルオブジェクトを二つ追加してそれぞれ`HeadTarget`,`HandTarget`とリネームしておいてください。(名前は分かりやすければ自由です)

`Add Component`から`MA Bone Proxy`を追加して、それぞれ追従させたいボーンをターゲットに指定してください。ターゲットボーンの子に配置されるだけのコンポーネントですが、一度参照が外れてもボーンの名前を自動的に探し出して設定してくれる優れものです。こちらもそれぞれ位置をなんとなく調整しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/46fafdfd-8cfa-7ff6-0cf1-e1288f707ab0.png)


#### 2. Constraintの設定

Constraintとは平たく言うと特定のオブジェクトに追従させる機能です。追従先を複数登録でき、重み付けによってどのオブジェクトにどの程度追従するのかを制御します。
実は沢山種類があるのですが、今回はシンプルに登録したオブジェクトに対して位置、回転、サイズなどが完全追従する(そのオブジェクトの子のように振る舞う)ParentConstraintのみ使用します。

##### 2.1 Parent Constraintを設定
帽子(SilkHat)のオブジェクトに`VRC Parent Constraint`を追加します。

:::note info
VRC ConstraintはUnity標準のConstraintと機能がほぼ同じですが、VRC用に最適化と軽量化が施されたヤツです。最近登場しました。
:::

SourceにはHeadTargetとHandTargetを登録しておきます。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/9a1bdbac-d6b9-b349-426c-6e3b55bffc6e.png)

TransFormの横にある数値がWeight(どれだけ追従するか)です。0で無効、1で完全に追従。デフォルトの位置は頭上なのでひとまずHeadTargetを1、HandTargetを0に設定しておきます。
設定出来たら右のZeroを押してください。Lockにチェックがついて位置がTarget達と同期します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/ba1aea62-7c49-c3aa-a4c6-0e113b7e1f32.png)

ズレて頭にめり込んでしまっていますね。今は位置がターゲットオブジェクトと同期している状態なのでHeadTargetのトランスフォームを調整します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/308c55c0-2593-1c1d-035c-14389e958d60.png)

Weightの数値を入れ替えて、HandTargetの方も同じように調整してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/8d534072-cb50-a137-53b9-9fe2123b99c3.png)

いい感じですね。

後でAnimatorからConstraintのWeightを制御することで、どちらに追従するかを切り替えます。

#### 3. Contactの設定

Contactと呼ばれていますが、ContactSenderとContactReceiverの二つのコンポーネントがあります。

`ContactSender`: 標準で頭や手についていることが多いですが、自分で作ることもできます。他人の手が頭に触れたとき表情が変わるなでなでギミックなどは標準で入っている手のContactSenderを利用しています。右手ならHandL、頭ならHeadのように識別タグのようなものを登録することができます。

`ContactReceiver`: 識別タグが一致するContactSenderに触れているとき、Parameterを変化させます。(触れているあいだ1にしたり、中心に近いほど数値が大きくなったり...)ContactSenderの識別タグは複数登録できますが、一つのContactReceiverで操れるParameterは一つのみです。自分のみしか触れないようにするなど、色々カスタマイズできます。

##### 3.1 Contact Sender(手側)の設定

Contact設定用のヌルオブジェクト(空のオブジェクト)を帽子直下に追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/d572f6bc-f14f-96b9-4a88-3fdadd5e18f6.png)

`Add Component`から`Contact Sender`を追加します。  
ソースにはHandTargetを指定して手に追従するよう設定し、`Radius`と`Position`を調整してください。

`Collision Tags`(識別タグのようなもの)はAddで追加できます。Hand Rを指定しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/4414aabf-5d38-9997-5a98-9c0c0c6fd6db.png)

##### 3.2 Contact Receiver(頭側)の設定

`Add Component`から`Contact Receiver`を追加します。
同じくソースにHeadTargetを指定して、位置と大きさを調整してください。
判定のサイズや位置は帽子がすっぽり収まるようなイメージで調整してください。頭の上にコントローラーを持っていくと飛ぶ可能性がある人は大きめにしておきましょう。

こちらにも`Collision Tags`の設定がありますが、これはどの識別タグを持つContact Senderに反応するかを設定する項目です。今回は先ほど設定した`Hand R`を指定しましょう。
また、`Allow Others`のチェックボックスを外しておくのを忘れないでください。自分専用の持ち替えギミックで、他人の手が反応しないようにするためです。

ParameterはConstact(触れている間ずっと1|触れていない時は0)を指定して、`HatContact`と書いておきます。分かりやすい任意の名前で大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/a5ff82c4-339d-40a8-0546-37234eab4e4b.png)



#### 4. HeadChopとAnimatorの設定

ここまでContactとConstraintの設定をしてきました。ある意味ではここからがこの記事の本題になります。

##### 4.1 コンポーネントの設定

まず帽子のオブジェクトに移動して、`VRC Parent Constraint`の下に`VRC Head Chop`を追加してください。
Target Bonesには自分自身(`Silkhat`)を登録して、Scale Factorを`0`にしておいてください。Unityでは確認できませんが、これはVRChat内で自分の視界から帽子が見えない状態です。

この後Animatorからこのコンポーネントのオンオフで自分の視界から帽子が見えるか見えないかを制御します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/98bf462e-e75b-011b-3fae-b158a9b4bc91.png)


次に、帽子の親のプレハブに移動して`Animator`と`MA Merge Animator`を追加してください。それぞれこんな使い道。

`Animator`-> なくても挙動には問題ないけどAnimatonのパスが通って編集しやすくなる。
`MA Merge Animator` -> アバター本体の設定を壊さずに任意のAnimatorを統合できる超便利ツール。

Assetsタブで右クリックしてCreate->Animator Controllerを追加します。`Animator`と`MA Merge Animator`の一番上の項目にそれぞれドラッグアンドドロップで設定しておきましょう。

また`MA Merge Animator`の設定を合わせておいてください。正しく動かすために重要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/66935e3e-6787-fbcb-bcc0-5c9ff68e87d5.png)

##### 4.2 Animatorの編集(アニメーション編)

Assetsタブにある追加したAnimator ControllerをダブルクリックしてAnimatorタブに移動しましょう。
ここで持ち替えを設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/be5537e3-6fd6-3a10-e521-14be3d637758.png)

まず必要なParameterを定義しましょう。左上にParameterの頁があるかと思います。

`HatContact` (Bool) -> Contact Receiverで定義したパラメータ。手と頭の接触を検知するとこのパラメータが自動的に`1`になります。接触していない時は`0`。
`GestureRight` (Int) -> 予約語と呼ばれるVRChat側で事前に定義されているパラメータの一つで、右コントローラーからの入力によって0-7の間で自動的に数値が変化します。ハンドジェスチャーに対応します。

| 数値 | ハンドジェスチャー | 説明 |
|------|----------------|------|
| 0    | Neutral       | 手を開いたニュートラルな状態 |
| 1    | Fist         | 握りこぶし（グー） |
| 2    | Open Hand    | 手を開いた状態（パー） |
| 3    | Fingerpoint  | 人差し指を立てた状態（指差し） |
| 4    | Victory      | ピースサイン（Vサイン） |
| 5    | RocknRoll    | ロックサイン（🤘） |
| 6    | Handgun      | 指で銃の形を作る |
| 7    | Thumbs Up    | 親指を立てる（👍） |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/f68a31a2-0e7e-aeb2-2eba-0c42dacb5039.png)

Layersの頁に戻ってアニメーションを設定します。
Assetsタブで右クリックしてCreate->Animationを追加してください。名前はDefaultPosとしておきましょう。

DefaultPosアニメーションファイルをanimatorにドラッグアンドドロップで追加します。

![スクリーンショット 2025-01-14 164600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/048fbfe8-b966-0158-87f6-11c695e5d250.png)

アニメーションファイルをダブルクリックしてAnimationタブに移動します。
Add Propertyの欄がグレーアウトしている場合、ヒエラルキーからSilkhat_prefab (アバター直下に置いてあるprefab)を選択するとAdd Propertyできるようになるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/8940dab6-79d1-823a-d996-53485defc669.png)

Add Propertyから以下の三つのpropertyを追加してください。
```
Silkhat -> VRC Head Chop (Script) -> VRC Head Chop.Enabled
Silkhat -> VRC Parent Constraint (Script) -> VRC Parent Constraint.Sources.source 0.Weight
Silkhat -> VRC Parent Constraint (Script) -> VRC Parent Constraint.Sources.source 1.Weight
```

今回は初期状態を設定するので、以下のようにしてください。

Head Chopの有効化 -> チェックあり
初期位置のWeight -> 1
持ち替え位置のWeight -> 0

また、動きはいらないので後ろの方のキーフレームを削除しておいてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/fa9076d5-03ca-9d65-3ac6-2a53538a2e49.png)

今作ったDefaultPosアニメーションを複製して、持ち替え状態のアニメーションを作っていきます。
Assetsタブからアニメーションを選んでctrl+D(Macなら⌘+D)で複製することが出来ます。こちらは`PickupPos`とリネームしておいてください。

こちらもドラッグアンドドロップでAnimatorタブに追加しておきます。

![スクリーンショット 2025-01-14 174625.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/e7dcce15-b16c-1fdb-2ecd-9b65a7a1b7a7.png)

アニメーションの数値を持ち替え用に変更します。
Head Chopの有効化 -> チェックなし
初期位置のWeight -> 0
持ち替え位置のWeight -> 1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/1ead2de6-575d-2209-9b65-815856ea3ae9.png)

これでアニメーションの設定はおわりです。

##### 4.3 Animatorの編集(トランジション編)

続けてAnimatorで移行条件の設定をしていきます。

DefaultPosのステートで右クリックしてMake Transitionをクリック -> PickupPosのステートで再びクリックして矢印を繋げてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/874b2ae7-71a8-0b7e-0186-202cc0b06704.png)

矢印を選択して移行する時の条件やアニメーションを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/8445a72c-4e90-fb6c-a6c3-27ba63afed5e.png)

下のような感じで変更してください。
GestureRight（右手）が0(ニュートラル), 2(パー), 4(ピースサイン)以外の時(つまり、コントローラーのグリップが引かれている状態のとき)に右手が帽子の近くにあると帽子が頭から手に移動するという条件設定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/76a43ca5-77e6-554d-0d31-d097dea5c669.png)

手から頭に戻す時のアニメーションも設定してしまいましょう。
今度はPickupPosからDefaultPosに矢印を引いてこんな感じで設定します。GestureRight（右手）が0(ニュートラル)かつ帽子の近くにあると帽子が手から頭に移動するという条件設定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/e0ad6526-8982-e355-c90d-8570eb5f5e0c.png)

GestureRightが2または4の時も同じようにします。
TransitionsでCopy Transition Parametersをクリックしてこの設定をコピーしておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/bb899052-c6b0-120f-e5cd-5f8fcf39d245.png)


下の条件設定に追加してしまうとAND(かつ)になってしまうので、OR(または)にするため矢印を増やします。もう二本PickupPosからDefaultPosへの矢印を引いてください。
Paste Bothで増やしたトランジションにも同じ設定を適用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/798f0577-51e9-096a-00b3-50198ef9edad.png)

あとは条件のGestureRightの数字だけそれぞれ0, 2, 4と変えたら完成です。

#### 5. 確認
これですべての作業が終わりました。アップロードして確認してみましょう。

頭に追従しているときは視界に映りません。
![20b5601698c2444db18e1e267cc9e0d5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/3583e75e-6de2-1f37-b376-974db56f0711.jpeg)

手に持つと視界に映るようになりますね。成功です。
![6ba96945a64f4c3897ed628d2613cf39.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862178/94a94a06-c4b4-c5f3-72e8-7cbb79aa07e7.jpeg)

## まとめ

ここまで、VRC Head Chopのご紹介と持ち替えギミックまでを作成していきました。(文量的にはギミックの実装メインになってしまいましたが)

これを応用するとヘイローや眼鏡の持ち替えだったりVRの時にだけ表示するコンソールなど色々なことが出来ます。
是非活用してみてください。