はじめに
アバターの耳をアナログ時計にしてみたかったのでしてみました。簡単に手順を紹介します。各種用語の説明についてはリンク先をご覧ください。
できたもの
アホ毛を引っ張ると両耳で時刻を教えてくれます。アホ毛を固定するとそのまま時刻を表示しつつけ、元の位置に戻すと解除されます。
誰向け
- このアホ毛耳時計がどうやって実現されているのかなんとなく知りたい人
- Avatars 3.0とPhysBonesがちょっとわかる人
- Animatorを組むときにWriteDefaultsの挙動がちょっとわかってる人向け
- ちょっとコード書ける人
- 本記事では簡単なOSCクライアントをgolangで実装しています
- golang以外で実装したい人はOSC Overview - VRChatを参考にしてください。
用意したもの
-
lyuma/Av3Emulator v2.9.8
- ダウンロードしてUnityPackageをProjectフォルダーにD&Dすると導入できます
- VRChat v2022.2.1
- Unity 2019.4.31f1
- VRCSDK3-AVATAR-2022.05.04.17.47
- Golang1.16
手順
大まかな手順は下記の通りです。
- アホ毛を引っ張れるようにする
- VRChatのExpressionMenuから耳を操作できるようにする
- 時刻をOSCでVRChatに送る
- タスクスケジューラーにOSCクライアントを登録する
アホ毛を引っ張れるようにする
アホ毛をお持ちの方はアホ毛に下図のようにPhysBonesを追加しMaxStretchとOptionsのParameterとIs Animatedを指定します。
Editorの再生ボタンを押して、アホ毛が固定できることを確認します。また、Optionsで指定したパラメーター(今回の場合ahoge
)からahoge_Stretch
が生成されます。
このパラメーターはPhysBone AV3 Parametersと呼ばれ、VRCExpressionParameters
のように定義しなくてもローカルとリモートで同期されます。
VRChatのExpressionMenueから耳を操作できるようにする
ExpressionParameterから操作できる必要はないのですがデバッグ用です。
FXLayerに時計用のアニメーションを追加する
Playable Layers FXを読む限り、耳は非HumanoidBoneであることとFXLayerは(おそらく)GestureLayerの非ヒューマノイドボーンのTransformを上書するという点からFXLayerで耳のTransfomのAnimationさせるのでよさそうです。
Animatorの作成
まずは下図のようにAnimatorのParametersに先ほど生成したahoge_Stretch
と時計の時と分のためのParameterを追加します。
次に左耳用と右耳用の空のアニメーションを作成し、下図のように左耳と右耳用のAnimatorを組みます。私の環境ではFXLayerはすべてWriteDefaultがOnなのでWriteDefaultをOnにしています。Offの人はNewStateでデフォルトの耳のアニメーションを作り追加しましょう。
また、Layersの上から下の順にanimationが実行され上書されることに注意が必要です。
animationの作成
下図のようにFXLayerのコントローラーをAnimatorのContollerに設定します。
HierarchyでAnimatorコンポーネントがついたGameObjectを選択し、Animationタブを開くと、FXLayerのAnimation
一覧がでてくるので先ほど作った空のアニメーションを選択します。その後、いい感じに60フレーム分(0~59)のアニメーションを作成します。また、Animationで耳の長さを変えたい場合は、回転させるボーンの子のボーンのPositionを移動させるのが良いです。
デフォルトだと回転の仕方が滑らかなので線形にします。keyframeを右クリックしてLinearを指定します。カーブの編集 - Unity マニュアルが参考になります。
右耳のAnimationも同様に作成します。
ExpressionParameterとMenuを設定
Editor上で実行してlyuma/Av3Emulatorで確認
lyuma/Av3Emulator v2.9.8を導入し、ToolsからAvatar3.0Emulatorを追加し、Editor上で再生、GameViewでアホ毛を右クリックで掴んで左クリックで固定、Hierarcyでアバターを選択し、InspectorのGesture Manager Av 3 MenuでExpressionMenuを操作します。
OSCクライアントの実装
私はgolangで書きましたがclientはなんでもいいです。VRChat的にはこれがおすすめのようです。
OpenSound Control - WikipediaはアプリケーションプロトコルでトランスポートはUDPが多いようです。(時々VRChatのデバッグ画面でOSCの受信が確認できないのはUDPだからなのか?)
VRChatでOSCを有効化する
下図のようにアクションメニューからOSCをEnabledにすると、AvatarParametersのconfigファイルが生成されます。
OSCのアドレスを確認
OSC用のアドレスはC:\Users\{User}\AppData\LocalLow\VRChat\VRChat\OSC\{userId}\Avatars\{avatarId}.json
にあります。OSC Avatar Parameters
golangでのOSCクライアントの実装
golangだとこんな感じで書けます。
package main
import (
"time"
"github.com/hypebeast/go-osc/osc"
)
func main() {
client := osc.NewClient("127.0.0.1", 9000)
for {
msgMin := osc.NewMessage("/avatar/parameters/LeftMin")
msgHour := osc.NewMessage("/avatar/parameters/RightHour")
t := time.Now()
hour := t.Hour()
minute := t.Minute()
// fmt.Print(t.Local().Hour(), t.Local().Minute(), "\n")
// 分を60分に対応する[0,1]のfloatに変換
fminute := float32(minute) / float32(60)
// 時間を12時間に対応する[0,1]のfloatに変換
fhour := float32(int(hour%12))/float32(12) + fminute/float32(12)
// fmt.Print(fhour, fminute, "\n")
msgMin.Append(float32(fminute))
msgHour.Append(float32(fhour))
client.Send(msgHour)
client.Send(msgMin)
time.Sleep(time.Second)
}
}
ビルド
go build OSCSend.go
- GUIを表示したくない場合
go build -ldflags="-H windowsgui" OSCSend.go
OSCSend.exeの名前の実行ファイルが生成されます。
タスクスケジューラーでWindowsログオン時にプログラムを実行する
VRChat起動時に毎回OSCをアプリを起動するのは手間なのでWindows標準アプリのタスクスケジューラーに任せます。
Windowsの検索欄から「タスク スケジューラ」を入力し起動します。
下図のようにタスクの作成からトリガータブを選択、新規ボタンからタスクの開始をログオン時に指定します。
これでWindows起動時にOSCクライアントが起動するようになりました。この状態でVRChatを起動すればOSCで時間情報を受信し両耳に時間が反映されるはずです。
最後に
Avatar3.0周りはそれぞれで環境が違うので同じように実装できるとは限らないのですが、部分的にでも誰かのお役に立てれば幸いです。また、ツール開発者に感謝を!