見出し画像

VRChat備忘録⑦ アバターに入れたギミックをコントローラーのボタンで操作しよう

***********************
注意!とお詫び
2/11にこちらの記事を公開しましたが、当初記載していた手順でAnimatorを組むと、自分視点では正常動作しますが操作するタイミングによって他人の視点で同期ズレが起きることが判明しました…
DiscordのVRCUnity勉強会というサーバーの強い方々に教えてもらって、問題解決したので2/19に手順を修正いたしました。
公開当初に見てやってみた方が居たらすいませんでした。
VRCUnity勉強会で問題解決に協力してくださった方々、ありがとうございました。
***********************

こんにちは、こむぎ* と申します。
VRChatの片隅を漂う無言勢の様な何かです。

今回の記事の内容としては、物の出し入れ等のギミックをExpressionsMenuを開かず、コントローラーのボタンを押すだけで切り替える手順をまとめてみました。
表情と連動してそのボタンを押している間だけ、ではなく、例えば左手の同じトリガーを引くたびに状態1→2→3→1…と切り替わるような操作を想定しています。

前回記事を書いた後でまた何か書こうかなーと思いつつも、需要が有りそうな事や面白い事が思いつかなかったので半年以上さぼっておりました。
おにぎりに残像を付けようとか誰も要らんのよ。
たまたま昔の自分と同じ仕組みを欲しがってる人をTwitterで見かけたので、自分の復習も兼ねてまとめてみました。


そもそも何でそんなのが必要?って話ですが、移動しながら素早くギミックを出したい!とか、自然な動作で操作したい!とかそういった場合でしょか。メニューだとどうしても見ながら操作しないと難しいですし…

この記事の対象としては、以下の様な方を想定しています。

・物の出し入れなどを覚えて、Animationの作成、AnimatorのStateや遷移の矢印の設定、ExpressionMenuの設定等が出来る。その辺りの理屈が大体わかっている。
・Quest2やそれに近いタイプのコントローラーを利用の方。ハンドジェスチャの操作形態が違う機器の場合は上手く出来るか不明です。


はじめに(今回利用する情報について)

初めに、今回の仕組みで利用する ”元々用意されているParameter” を説明します。

普段、アバターに小物の出し入れ等をする場合はExpression ParametersとAnimatorの中のParametersの両方に "Onigiri" とか、"Megane" とか、自分でパラメーターを用意しているかと思います。(Animatorの矢印で1ならこっち、0ならこっち、とかやる為のアレです)

実はVRChatには、各人が設定するパラメーター以外に、デフォルトで用意されているパラメーターが色々あったりします。
その中に、左右のコントローラーのどのボタンが押されているか(正確にはどのハンドジェスチャの押し方か)、を判別できる "GestureLeft" "GestureRight" というパラメーターが存在しています。

スクリーンショット (723)

ハンドジェスチャで表情を変えられるタイプのアバターを使っている場合、大抵Animatorの中に "Left Hand" "Right Hand" のLayerが有ると思いますので、開いてStateを繋いでいる矢印をクリックして見てみましょう。
上記画像であれば、"GestureRight" が1の時に "Fist" に遷移するようになっています。

これらの元々用意されているパラメーターや、どのボタンが押されている時(どのハンドジェスチャの時)に数値がいくつになるか、などは下記の公式の情報をご覧ください。
Gesture以外にも、アバターがどっちに移動中なのか等を確認するパラメーターも有るので夢が広がります。


手順0.ギミックの用意&操作形態を決める

当然ですが、どのボタンで何をするか決めないといけません。
後、今回はボタン操作の作り方なのでギミック自体は軽く流していきます。

今回の例としては、アバターに斧を持たせ、左コントローラーのグリップボタン(中指で押すボタン)を押すたびに 腰→右手→左手→腰… と装着位置を変える形で作ってみたいと思います。

スクリーンショット (725)

まずConstraintを使って斧を3か所に移動させられるように設定して

スクリーンショット (726)

それぞれの場所に切り替えるためのAnimationを用意します。
やる事が物の出し入れとかであれば、物の出し入れのAnimationなどを用意してください。要はParameterやMenu等のどうやって切り替えるかの設定前までやってください。

スクリーンショット (729)

最後に、"Empty" というAnimationを用意します。
名前の通り、中身は空っぽのAnimationです。
右クリックからAnimationファイルを作って、何も登録せずに名前だけ変えておいてください。むしろ名前も変えずに "New Animation" のままでも大丈夫です。


手順1.使いたいパラメーターを登録する

まず、使いたいパラメーター(今回であれば "GestureLeft")をAnimatorのParametersに登録します。
このパラメーターについては、ExParametersへの登録は不要です。
先程「デフォルトで用意されている!」と言いましたが、内部的には最初から存在するものの、Animatorの矢印のとこで選択する為にはParametersの欄に登録が必要です。

画像2

といっても、先程も言った通り既存のセットアップ済みアバターであれば、大抵は"GestureLeft" "GestureRight" は登録済みです。
Parametersを開いて存在するようであれば問題ありません。

スクリーンショット (724)

万が一メニューから表情を変えるタイプのアバター等の場合はここに登録が無い可能性が有りますので、その際は+から手動で"GestureLeft" "GestureRight" をint型で登録してください。
(操作に使いたい手の側だけでOK)

スクリーンショット (762)

後は、メニューでギミックを操作する時と同様、切り替え用のパラメーターを用意しましょう。
名前や型はやりたい事に合わせて好きに用意すればよいですが、今回は斧を3か所に移動させるという事で、AxeというInt型のパラメーターを用意しました。
こちらはいつも通り、ExParametersと、AnimatorのParameters両方に同じものを登録しましょう。


手順2.パラメーター操作用のAnimatorを組む

スクリーンショット (765)

まずは組んだ形から。最終的に2つレイヤーを作ります。
①ジェスチャーを読み取ってギミック操作用のパラメーター(今回ならAxe)の数値を変更するLayer。(今回は上記画像のAxe HandGesture)
②操作用パラメーターを元にギミックを動かすLayer(次の項目で作成する、今回であればAxe PositionというLayer)

まずは上記画像はボタン操作(ジェスチャー)を読み取って、ギミック操作用のパラメーターを変更するLayerから。


スクリーンショット (793)

まず、新しいLayerを作成します。"GestureLeft" などのパラメーターは使いますが、元々あったハンドジェスチャ用のLayerとはやる事が違うので新規作成です。

全体の形はこの手順2冒頭の画像を参考にしてください。
各State、矢印の中身はこの後で説明します。
※"Set 0" ~ "Set 2" の中身はほぼ同じなので、どれか一つのStateを作成してから、コピーして違う部分(一部の数字)だけ変更すると楽に作れます。

各項目の設定内容の意味・詳細については後程別途説明します。


スクリーンショット (7aaaaa65)

"Wait"には、手順0で用意した、空っぽの "Empty" というAnimationが登録されています。
こちらのStateは、グリップボタンを押すまで待機するだけのStateなので、空のAnimationが入っているだけで何もしていません。

そもそもこの空のAnimationが何なのかと言うと、やっておいてなんですがよくわかりません。本来何もしないのでAnimationの登録すらせずに空っぽでいい気がするんですが、何もAnimationを登録していないStateが有ると、それ以外のAnimationが誤動作する事が有るようです。
私がテストで組んだ際も、ここを空にしておくと表情やペンなど他の部分が動作しなくなりました。
調べたんですが、「そういう事がある」以上の情報は出てきませんでした…


スクリーンショット (7aaaaaaaaa65)

"Set 0"、"Set 1"、"Set 2" にも、手順0で用意した、空っぽの "Empty" というAnimationが登録されています。
"Wait" と違うのは、こちらのStateには VRC Avatar Parameter Driver という物が追加されています。これを使うと、該当のStateに行った際にパラメーターの数値を変更する事が可能です。


スクリーンショット (768)

追加手順は、該当のStateで Add Behavior をクリックし、VRCAvatarParameterDriver を選択してください。

スクリーンショット (768)

追加後は、Add Parameter を押し、設定欄が出てきたら
・Local Only にチェック
・Name の欄で操作したいパラメーター(今回はAxe)を選択
・Chage Type で Set を選択。(場合によってはAdd。後で説明します)
・Value には、Stateごとに "Set 0"なら0、"Set 1" なら1、"Set 2" なら 2
上記の様に設定してください。

Change Type が Set の場合、その数値に変更する形なので、各Stateに行くと Axe というパラメーターが、それぞれ 0、1、2 に変更されます。


スクリーンショット (773)

"Wait" から "Set 0" への矢印は上記の様な内容です。
Has Exit Timeはチェックを外し、Transition Durationは0、遷移の条件(Conditions)は、 画像赤枠の+を押して条件を5枠に拡張し、
・"GestureLeft" が 0 では無い時(NotEqual)
・"GestureLeft" が 2 では無い時(NotEqual)
・"GestureLeft" が 4 では無い時(NotEqual)
・"GestureLeft" が 5 では無い時(NotEqual)
・"Axe" が2の時(Equal)
を設定しています。


スクリーンショット (776)

"Wait" から "Set 1" への矢印は先程とほぼ同じですが、"Axe" のところだけ
・"Axe" が0の時(Equal)
に変更します。


スクリーンショット (777)

"Wait" から "Set 2" への矢印は先程とほぼ同じですが、"Axe" のところだけ
・"Axe" が1の時(Equal)
に変更します。


スクリーンショット (778)

"Set 0"、"Set 1"、"Set 2" から "Wait" への矢印は全て同じ内容で、
Has Exit Timeはチェックを外し、Transition Durationは0、遷移の条件(Conditions)は、
・"GestureLeft" が 1 では無い時(NotEqual)
・"GestureLeft" が 3 では無い時(NotEqual)
・"GestureLeft" が 6 では無い時(NotEqual)
・"GestureLeft" が 7 では無い時(NotEqual)
を設定しています。

これで、一つ目のLayerの設定は完了です。


手順3.ギミック操作用のAnimatorを組む

スクリーンショット (784)

次に、ギミック操作用のLayerを組んでいきます。
こちらは特別な事はしておらず、先程のLayerで操作した "Axe" というパラメーターを元に、手順0で用意した各Animationを再生しているだけです。
普通の物の出し入れや、Constraintの位置変更などと同じ組み方なので、分かる方は読まなくて大丈夫です。

一応今回のパターンを書いておくと


スクリーンショット (779)

"Chest" には、手順0で用意した Axe_Chest というAnimationを登録しています。
同様に、"Right"、"Left" にも、最初に用意したAnimationが登録されています。


スクリーンショット (785)

"Any State" → "Chest" の矢印は、
Has Exit Timeはチェックを外し、Transition Durationは0.25(好みで)、Can Transition To Selfのチェックを外し、遷移の条件(Conditions)は
・"Axe" が0の時(Equal)
を設定しています。

"Any State" → "Right" は 遷移の条件(Conditions)が
・"Axe" が1の時(Equal)

"Any State" → "Left" は 遷移の条件(Conditions)が
・"Axe" が2の時(Equal)

です。

これで、Animatorの作成は完了です。後はアップロードしてうまく動けば、グリップボタンを押すたびに(他のボタンの状況は関係なく)ギミックが切り替わる動きが出来ているはず…です。


解説1.設定内容などについて

スクリーンショット (786)

まずは、パラメーター設定用のところで登録した、VRC Avatar Parameter Driver から。

先程簡単に説明した通り、これを使うとAnimator側からパラメーターを設定・変更する事が可能になります。


スクリーンショット (786)

・Local Onlyのチェックボックス
これにチェックが入っていると、該当のパラメーターの設定・加算などの処理は自分のクライアント上だけで行われる事になります。
逆に言うと、チェックが入っていないと、他人のクライアント上でも同じ処理が行われます。
それだけ聞くと、自分のクライアント上だけでパラメーターが変化しても他人のクライアント上で変わらなければ意味がないのでは?となりそうですが、実際には
①自分のクライアントのAnimatorでパラメーターが変化(今回ならAxeが0に)
②ExParametersの方にその変化が反映される
③ExParameters側でパラメーターに変更が有ると、他人に同期される
④他の人のクライアント上でもパラメーターが同じ数値に
という流れで他人にも同期される模様です。

逆にチェックが外れていると他人のクライアント上でも同じ処理が行われます。処理の内容が Set で特定の数値にする場合は、うまく同期している間は正常に動きます。
が、何かのはずみで同期がズレてくると、自分と他人のクライアントで別の数字がSetされて見えてるものが変わってきたり、後で説明するAdd(加算)という処理を使うと、二重に加算されてこれまたズレが発生します。

良くわからない場合はとりあえずチェックしておきましょう。自分の所で処理をして、結果だけ同期するイメージになるので安定します。


スクリーンショット (786)

・Nameの欄
これは操作したいパラメーターを選ぶだけです


スクリーンショット (786)

・Change Type と Value
パラメーターに対する処理の種類とその数値です。
処理の種類は "Set"、"Add"、"Random"の3種類が有ります。
"Set" はそのまま、Valueに入れた数値を設定します。パラメーターの型がBoolの場合はON/OFFの設定が出来ます。
"Add" は加算です。処理前の数値に対して、Valueで指定した数値を足していきます。Valueの数値をマイナスにすれば逆に引いていくことも出来ます。たぶん。
"Random" は読んで字のごとくランダムです。Valueに最小値と最大値を設定でき、その範囲内でランダムの数値を設定します。

今回は設定する数値が0,1,2の3種類だけだったので、分かりやすさ重視でSetを使いました。設定する数値の範囲が広がってくると面倒なので、その際はAddを使うなど工夫しましょう。後で矢印の説明後に例も出します。


次に矢印部分について

スクリーンショット (787)

簡単に言うと、上記の様な設定になっています。
当初に決めたボタンを押したらStateに進みパラメーターが変更され、一度離さなければ次に進まないようになっています。
ボタンを離すとExit→Entryと進み、またWaitでボタンを押すのを待つことになります。


スクリーンショット (773)

先程、"Wait" から "Set 0" への矢印を上記内容で設定しました。
・"GestureLeft" が 0 では無い時(NotEqual)
・"GestureLeft" が 2 では無い時(NotEqual)
・"GestureLeft" が 4 では無い時(NotEqual)
・"GestureLeft" が 5 では無い時(NotEqual)
・"Axe" が2の時(Equal)
という設定になっています。
Axeが~はおいておくとして、Gestureの部分について説明します。

"GestureLeft" で取得できる0~7の8種類のジェスチャはそれぞれ、
0 idle、1 Fist、2 Open、3 Point
4 Peace、5 RockNRoll、6 Gun、7 Thumbs up
の数値を返します。

そのうち、グリップボタンを押すハンドジェスチャは
1 Fist、3 Point、6 Gun、7 Thumbs up
押さないハンドジェスチャは
0 idle、2 Open、4 Peace、5 RockNRoll
です。

なので、上記の設定としては、
・"GestureLeft" が 0 でも 2 でも 4 でも 5 無い時(NotEqual)= それ以外の4つのGesture(グリップボタンを押すジェスチャのどれか)
という内容になり、スティックやトリガーボタンの状況に係わらず、グリップボタンを押せば先に進めるようになっています。

後は"Axe"が0なら1にするStateへ、1なら2のState、2なら0のStateとの条件を付けくわえて、ボタンを押すたびに数値が0→1→2となるようにしています。

ちなみに、設定しやすい形を考えて上記内容にしていますが、イメージ的に分かりやすい作り方をしたい場合は、

スクリーンショット (775)

上記画像のように"Wait" から各 "Set *" の間に4本矢印を引いて、それぞれの内容を矢印1つめは"GestureLeft" = 1 、矢印2つ目は"GestureLeft" =3 、4種類設定してください。同一区間に複数の矢印を設定した場合、そのうちどれかの条件が満たされた時点で先に進みます。


スクリーンショット (778)

そして右側のExitに抜ける矢印は、逆にボタンを離した時に進むようになります。
こちらはAxeの数値に係わらず、ボタンを離したらExitへ向かうだけなので条件は"GestureLeft" だけです。
・"GestureLeft" が 1 では無い時(NotEqual)
・"GestureLeft" が 3 では無い時(NotEqual)
・"GestureLeft" が 6 では無い時(NotEqual)
・"GestureLeft" が 7 では無い時(NotEqual)
なので、グリップボタンを押しているジェスチャどれでも無い場合=ボタンを離したら、先に進みます。


解説2.改良版

先程 VRC Avatar Parameter Driver の説明の時に少し話したように、今回は数値が0,1,2の3つだけなので3つのStateを作るだけで済みましたが、0,1,2,3,4,5.... と増えていくといちいちSetの為のStateを増やすのは非効率的です。

その場合は

スクリーンショット (788)

こんな感じにして、数値が最大値未満の場合は "Add" に進み、Addで+1ずつ数値を増やし、数値が最大値の時のみ "Reset" に進み、数値に0をSetするようにしましょう。

スクリーンショット (789)

Stateはこんな感じで、


スクリーンショット (791)

矢印はこんな感じ。最大値9の場合です。


説明終わり。


今回は、Animatorの基本的な事とかは省きましたし、組み方も一手ずつ丁寧にではなく、ザクっとした説明にしました。
正直分かりにくいところも沢山あると思うので、不明点が有ったらnoteのコメントなりTwitter辺りで聞いてもらえれば分かる範囲で補足します。

読んでいただきありがとうございました。
また、VRCUnity勉強会の方々もありがとうございました。
皆さんも分からない事有ったらそっちで聞けば詳しい人が沢山いるので是非。


おまけ.失敗例

今回、ジェスチャーを利用してパラメーターを変更するLayerと、変更したパラメーターでギミックを操作するLayerを分けて作りました。
読んでくださった方の中には、そもそもLayer一つで、直接 "GestureLeft" とかを条件にギミックを動かせるのでは?と考えた方も居るかもしれません。
実は私も当初そう考え、下記の様な構成で組んでいました。


画像32

矢印の条件にグリップボタンを押す、離す、が交互に入っており、押したり離したりする度に一つ先のStateに進む形になっていました。
実際これでテストすると動くのですが…

記事冒頭に注意とお詫びとして書いた通り、この組み方をして他人に見てもらうと、高確率で途中でズレが生じます。
自分視点では斧が腰についてるのに、他人からは左手になってたり…

理由としては2つあった模様です。簡単に説明すると…


①ジェスチャーを変更して "GestureLeft" などが変更された時、タイミングによってそれが同期されない事が有る。

どうも、ExParametersに登録されているパラメーターを変更した際は、細かいタイミングまでは分かりませんがほぼ確実に他人に同期がされるようです。変更したタイミングに同期されてるのかも知れません。

ただし、今回の "GestureLeft" の様に ExParameters に登録されていないパラメーターの場合は、タイミング次第で結構同期されない事が有るようです。その場合、自分のクライアント上ではボタンを押したので先に進みますが、他人のクライアント上では押してない事になり先に進んでいない、などの現象がおこりズレていくようです。

最後にこのおまけを書いていて気づいたのですが、もしかしたら上記画像の組み方でも、"GestureLeft" とかを ExParametersに登録すれば動くかもしれません。
でも、パラメーター操作とギミック実行のLayerを分けた方が分かりやすかったりメンテナンス性が高かったりしますし、ExMenuにも操作ボタンを入れて好きな方で操作出来たりするので、今回の組み方がオススメです。


②途中でワールドにインした人の場合、最初のStateからスタートする

後からワールドに入って来た人の場合、その時点のパラメーターの状況は同期されますが、Animator内でどこのStateにいるかまでは同期されません。
上記画像の場合、ボタンを押す、離すの度に先に進んでいく形で組んでいるので、パラメーターが同期されてもどこのStateにいるかは分からない組み方で、後からインした人は "Axe_Chest" か、その次の "Wait 1" のどちらかのStateに行くことになり、ズレが生じる可能性が有ります。

これが記事公開時の失敗でした。参考までに…

この記事が気に入ったらサポートをしてみませんか?