特に役に立つことは書いていないワールド制作にまつわる日記②
おはこんちにばんは。
すっかり存在を忘れていたシリーズ(?)記事を思い出したので書きます。
先日第2回1weekWCがありました。イベントの詳細は各自で調べてください。
私は作りたい時にしかワールド作らないので、開始から3~4日くらいは「何も思いつかないしスルーでいいかあ」と思っていました。
でも週末くらいに突然「影使って遊びたい!」と思ったので突貫工事を始めました。
できたものがこちら。
https://twitter.com/it__was__rainy/status/1465698680233623572?s=21
駅である必要、ある?ないですね。これ幸いとかこつけただけです。
ワールドの裏設定とかは特になんもないので以下、技術的なお話が続きます。
今回のワールドの最大かつ唯一のポイントは「Shadow only」です。
Unityの(Skinned)MeshRendererコンポーネントには、「オブジェクトが影を落とすかどうか」を決定する「Cast Shadow」という項目があります。これをShadow onlyにして影絵遊びをしたかったっていうのがワールドのコンセプトですかね。詳しい仕様が知りたい人は公式ドキュメントを読んでください。
で、影であれこれするにあたって1番の敵はQuest対応です。今Questにはリアルタイムシャドウを持ってこれません。私はQuest勢なのでQuest対応していないワールドを作ってもどうしようもない。
Questで影を見るためにはベイクするしかないわけですが、まあこれが大変というかなんというか、試行錯誤してました。作業時間の8割くらいはライティング。
実は最初は影だけのワールドにするつもりでした。左右の壁にそれぞれ影が落ちてる感じの。(写真は撮ってないので頑張って想像してください)
そうするには右を照らすライトとオブジェクト、左を照らすライトとオブジェクトを分けなきゃいけないんですが、ライトのcullingmaskはリアルタイムでしか働かないのでベイク前提だとうまくいきませんでした。没。
それで片側だけにすることになったんですけど、それだけだと味気ないので表の空間(透明な駅)がやっつけ作業で生み出されました。こっちはリアルタイムライトですが影は消しています。
あとは本題の影の方ですが、私のマシンスペックではなかなか綺麗に焼けない。1回のベイクも時間かかるし。VRCの為と言うよりはワールド制作のためにつよつよPCが欲しいですね。最終的にresolution40のsize2048で妥協しました。この時点で〆切の前日だったのでベイク時間的にもこれ以上の試行錯誤は現実的ではなかった。あ、ちなみにライトベイクはUnity標準のCPUなんちゃらでやってます。
ライトの角度は真横ではなくちょっと斜め上から照らしています。その方が全体的な見栄えが良かったのと、線路の影は床に落とした方がわかりやすいので(壁に落とすとただの一本線になるから)。まあこのせいでPC用の調整にまた苦労するんですが……。
アバターの影はどうしようもないので、プレイヤーに当たるライトを切ってアバター自体を真っ黒にすることで影っぽい写真を撮れるようにしてみましたが、standardliteとかだと設定によっては黒くならないのが惜しいところ。リアルタイムシャドウ返して。
ここまででQuest対応を完了したので、PC用にsceneを分けてリアルタイムライトにして……。
あ、ライト斜め上だとプレイヤーの影はコレジャナイ感する……プレイヤー用のライトだけ真横から照らすか……でもそうすると壁に2つライト当たって影が薄くなる……でも今更ライトの角度変えて焼き直ししたくない……まあいいか!!
こんな感じで完成しました。この後一旦提出して〆切過ぎてから微調整したりフレンドにデバッグしてもらったりしてました。
本当はcullingmaskとShadowonlyを使って、懐中電灯を壁に当ててオブジェクトの影を探して遊ぶみたいなことしたかったんだけどQuest対応できないので没にしました。
【VRChat】ワールドのライトが焦げるときの対策【Unity2019】
この記事ではUnity2019におけるVRChatワールドのライトベイクについて、うまくいかないときの考えられる原因と対策をまとめたものです。私が経験したことしか書いてません。使っているBlenderは2.91です。
ライトベイクの基本
ライトベイクするならまずこの記事を読んでその通りにやってください。だいたいうまくいきます(書かれたのはUnity2018時代ですが2019でも一緒です)。
Unity2018でLightmapを綺麗にBakeする! (k-youhinten.com)
それでもうまくいかないときは以下の点を確認してみて下さい。
ライトベイク確認事項
特殊なシェーダーやmobileシェーダーを使っている
一部のシェーダーはライトマップに対応していない?ようです。Standardに変えてライトマップが正常に表示される場合はシェーダーの問題だと思います。シェーダー書ける人は中身をいじってどうにかできるかもしれませんが、できない場合そのシェーダーをライトベイクするオブジェクトに使うことをあきらめるしかありません。私はあきらめました。
オブジェクトの面が裏になっている
面が裏(Blenderでいうと赤くなってる方)だとライトを焼けません。面を裏返すか裏面を作りましょう。
SkinnedMeshRendererコンポーネントを使用している
SkinnedMeshRendererはライトベイクがうまくいかないことがあるようです(私はSkinnedMeshRendererをMeshRendererにしたら直ったことがある)。シェイプキーとかボーンの入っているオブジェクトには注意。
Materialでテクスチャのタイリングを設定している
Materialのパラメータでテクスチャのタイリングの数値をいじるとライトマップが反映されなくなることがあったような気がします。気のせいかもしれない。タイリングの数値を戻してライトマップが正常に反映されればそれが原因です。どうしてもタイリングが必要な場合はUV展開をやり直した方がいいでしょう。
UV展開に問題がある
何をどうやってもライトが焦げるときはだいたいこれのせい。UVに重なりがあると焦げます。たとえば、Blenderでミラーモデファイアや配列モデファイアをデフォルトの設定のまま使うと、複製された部分のUVがぴったり同じ位置に重なります。
こうなるといくらライトベイクの設定をいじっても焦げます。なので、ライトを焼く予定のオブジェクトのUVは重ならないようにしましょう。
では具体的にどうすれば回避できるかというと、ミラーモデファイアと配列モデファイアの場合は以下の設定にします。
ミラーモデファイア
「データ」→「ミラーU」または「ミラーV」にチェック、値を1(または-1)に設定→モデファイアを適用する
Uだと横に、Vだと縦にUVが並びます。両方1にすれば斜めに並びますが、どっちかでいいと思います。
配列モデファイア
「UV」→「オフセットU」または「オフセットV」にチェック、以下同文。
モデファイアによって微妙にパラメータの名前が違いますがやってることは一緒です。これらのモデファイアを併用する場合は、どっちかをUにしてどっちかをVでやると綺麗に並べられます。
とはいえ、市販の3Dモデルの中にはテクスチャの領域を節約するためにUVを重ねているものも多いかと思います。これらのUVやテクスチャを編集しなおすのは骨が折れると思うので、重なっている部分はオブジェクトかマテリアルを分割すれば焦げなくなる、ような気がします。試したことないのでわかりません。誰か検証して。
あと単色とかシームレステクスチャのモデルはUV展開をやり直しても見栄えはほとんど変わらないのでそういう奴は修正しちゃいましょう。手っ取り早いのはキューブ投影とかスマートUV投影です。UV展開について詳しいことは私もよく分からないので調べてください。
Lightオブジェクトを作り直してみる
たまに、「さっきまでうまくいってたのに突然焦げた」ってことがあります。そういうときはLightコンポーネントをコピーしてLightオブジェクトを削除し、新しくLightを作ってパラメータをペーストしてみると直ることがあります。
特に役に立つことは書いてないワールド制作にまつわる日記①
自分のワールドとその制作背景について語りたくなったので自分語りのための記事第一弾を書きました。8割くらいUdonと戦っています。画像はありません。用意するのが面倒でした。のでただひたすら活字が書き散らされたチラ裏的な日記です。
今あるワールド分だけ記事が増えるか追記されるかしていきます。
1 SunShowerGarden
記念すべき処女作。
そもそもなぜワールドを作ろうと思ったのかというと、BOOTHを漁っていて素敵なアセットを見つけたから。
『これ使ったワールド見てえ~~~~』
『自分で作ればええねや!』
こう思うちょっと前にtwitterで、「ホームワールドは自作ですか?」みたいな投票が流れてきたんだが、自作してる人がそこそこいて「ほーーん意外とみんなやってるってことは実は簡単なんじゃね??」と思ったのもある。
結論から言うと、”ワールド”を作ってアップロードする→簡単
凝った景色・ギミックを作る→難しい
以上です。ホームに必要な機能を備えるだけならだれでもできるのは事実です。簡単導入ギミックセットも配布されてるしね。
閑話休題。
さてワールドを作ろうと思い立ってまずはいくつか記事を漁ってやり方をおおむね把握したのち、さあ実際に作ってみようとSDKをインポートし、オブジェクトを並べていい感じの景色を作っていく。
景色がある程度できたところで、「ミラーとBGMのスイッチがほしいなあ」と思う私に襲い掛かった最初の難関。そうSDK3.0すなわちUdonである。
『アバターは3.0だし、ワールドも3.0のほうが便利で幅が広いのかな~』
などとろくに調べずにSDK3.0を使った私はその後Udonに弄ばれ四苦八苦することになります(ちなみに3.0のほうができることの幅が広いのは事実です)。なんせ、ほとんどののワールド制作記事はSDK2.0用。「VRC_Trigger」というコンポーネントが見つからず私の頭は?で埋め尽くされることとなる。
この後どうにかして「SDK2.0とは違って3.0では”Udon”を使ってあらゆるギミックを作らなければならない」という事実にたどり着きました。どうやってたどりついたのかは覚えていません。そもそも2.0とUdonの違いも知らなかった人間がよく自力でここまでたどり着いたね、えらい。
Udonを使わないといけない、ということさえ分かればあとはいくらでも調べようがある。私がUdon初心者の頃にお世話になった神々のUdonGraph解説書はひとつ前の記事に載せてるよ。
そうして晴れて私の初制作ワールドはラボにあげられ、たくさんの人に足を運んでいただき、めでたくパブリック化することになりましたとさ(ありがとうございます)。
さて、初めてのワールドをアップロードし、こうしてミラーやBGMのオンオフにも成功した私はちょっと調子に乗る。
『ワールドカメラ・・・作れるのでは?』
このとき、3.0用のワールドカメラがまだ配布されていなかった(らしい)。少なくとも私は見つけられなかった、確かそうだった。実は調べてなかっただけかもしれん。覚えとらん。
そういうわけで私はblenderを立ち上げ4時間くらいで雑にカメラの本体のモデルを作る。何気にこれが初めて1から作った3D モデルになりました。
さあモデルができたらあとはギミックの中身だ。ワールドカメラの仕組み自体は解説記事を見つけたものの、「どうやってシャッターを切るんだ??」という疑問が。BOOTHにあったワールドカメラ制作ガイドみたいなやつではなんかアニメーションをこねこねしていたので、アニメーションに登録できる=オブジェクトのオンオフがトリガーになっているのでは、という仮説を立てた。
実験してみた。できた。やったね。
ついでにSEを鳴らす仕組みと遠隔シャッターもつくった(SEのほうはノードの組み方にちょっと苦戦した覚えがある)。
これで私の処女作ワールドは真の完成を見ました。めでたしめでたし。
今思うとこうやってカメラつくったときからすでにUdon沼にはまってしまっていたのかもしれない。
ああ疲れた。ここまででおしまい。続きはまた今度書きます。
【Unity.UdonGraph】VRChatのワールドギミック作例集
世のUdon解説が軒並みU#でGraphの作例が少なすぎるので、私が自分のワールドで使っている(ものの改良・調整版)UdonGraphの作例をぶん投げる記事です。なお作者はプログラミング素人です。
- 1.はじめに
- 2.作例集と解説少々
- 3.UdonBehaviorとuGUIを連動させる
- 4.応用・番外編
- 5.おわりに
1.はじめに
1.この記事を読むのに前提となる知識
まずはこのへんを読んでくれ。
sdk3のudonでミラーをオンオフする(ついでに同期させる)|かくちゅ|note
特に上二つは絶対読んでできれば実践しておいたほうがいい。最初はコピペでいいのでとにかくやってみる。するとあとからだんだん理屈がわかってくるので。
以下は、「UdonGraphの基礎は分かるけど具体的に”どうすれば何ができるのか”が分からない」という人に向けて、「少なくともこうすればこう動く」という作例を紹介していく。「Udon触るの初めてです」という人に分かるようには書いていないし分かる人から見ても省略しすぎてわかりにくいかもしれない。
2.この記事での凡例について
思いつくままに書いているので書き方はあまり統一されていないのだが、ノードの名前の表記はすべて「○○.△△」といった形になっている(はず)。これはノードを探すときに「まず○○で検索して、次に△△で検索して出てくるもの」という意味。U#(C#)のスクリプトの形式とは一切関係ない。
2.作例集と解説少々
1.オン/オフ切り替え系
上の参考記事ではオブジェクト(ミラーとか)だったものの対象が、AudioSourceだったりColliderだったりMeshRendererだったりUdonBehaviorだったり、まあとにかく「オフ→オン(オン→オフ)する」タイプのGraph。
1-1.AudioSource
BGMや環境音をつけたりけしたりする。ミラーと同じくらいよく使うやつ。
基本の構造は、
①「”PlayOnAwake”にチェックしてオブジェクト自体をオンオフする」または、
②「AudioSourceにAudioclipの再生/停止を命令する」のどちらか。
①の場合はミラーと同じなのでGraphは省略。gameobject変数に、目的のAudioSourceがついたオブジェクトを指定すればよい。
②の場合。基本構造は①と同じで、「GameObject.SetActive」のノードを「AudioSource.Play(Stop)」に差し替える。使う変数は「AudioSource」。
1-2.Collider/MeshRenderer/UdonBehavior ect...
上記のような「あるオブジェクトの特定のコンポーネント」をオンオフする。
基本の構造は、
①「目的のコンポーネントに対応した変数を使う」
②「GameObject変数から目的のtypeのコンポーネントを取得する」
のどちらか。
①の場合。変数は目的のコンポーネントと同じ名前のもの。GameObjectでいうSetActiveと同じような働きをするのがSet enable。
②の場合。変数はGameObject。typeは目的のコンポーネント。GameObject.GetConponentは指定したオブジェクトのさらに特定のコンポーネントを取得する。
これの利点は、「自分を対象とするオンオフ機能のあるオブジェクトを量産したい」ときに、変数にいちいち対象を指定しなくていいこと。(GameObjectや後述のTransformの変数は特に指定がない場合UdonBehaviorのついたオブジェクト自身が対象になる。ここであげたコンポーネントや上のAudioSourceは指定しないとNoneになる。)
「きらきら星の階段」に踏むと出現する階段があるが、あれは②の方法で作った階段を大量にコピペして作っている。
2.オブジェクト移動・回転系(Transformをいじる)
オブジェクトを移動させたり回転させたりする。プレイヤーのワープもここで扱う。
2-1.オブジェクトの移動・追従
使う変数・ノードは「Transform」系統。「Transrate」と「Set position」の何が違うのかは自信がないんですが、Transrateは移動距離の指定(相対的)でSetpositionが座標の指定(絶対的)?公式スクリプトリファレンス英語なのでちゃんと読めてるかどうかわからない。
→「Translate」は「現在地から、X.Y.Z軸方向に指定した値だけ平行移動する」、「Set position」は「現在地にかかわらず、X.Y.Zに指定した座標に飛ばす」といった処理になるらしい。
追従の場合はupdateで始めて移動先に追従先オブジェクトの座標を取得して渡す。画像は追従先がプレイヤーなのでやや複雑だが、普通のオブジェクトとかなら「Transform.Get position」で座標を取得すればいいと思います多分。
2-2.オブジェクトの回転
変数・ノードは「Transform」系統。これもたぶん上と同じ感じで「Rotate」と「Set rotation」がある。
2-3.プレイヤーをワープさせる
使うノードは「Networking.Get locaoplayer」「PlayerAPi.TeleportTo」。ワープ先の位置・向きは手打ちで指定してもいいし、適当なオブジェクトを置いてそいつのTransform値を取得して代入してもいい。後者が楽。
3.Animatorを操作する
オブジェクトにAnimatorコンポーネントをつけてAnimatorControllerを放り込み、そいつの中身をいじる。使う変数・ノードは「Animator」系統。
3-1.Animationを再生する
「Animator.Play」で特定のステートを再生できる。レイヤーが複数あるとレイヤー名も指定しないといけないらしいが、どう記述するのか忘れました。まあアニメーション再生するならステート名で指定するより後述の変数切り替えでステート遷移させるほうが確実なのでそっち使いましょう。
3-2.変数を切り替える
AnimatorControllerの中身をアバター3.0でやってるみたいに設定したうえで、ExpressionMenuで操作するみたいに変数を切り替えるGraph。使うノードは「Animator.Set○○」(指定したい変数と同じ型)。
3-3.speedをいじる(疑似的な一時停止)
Animatorの各ステートにはアニメーションの再生スピードを設定する項目があるが、その値をいじることができる。これを0にするとアニメーションの一時停止を再現できる。使うノードは「Animator.Set speed」。
元々はAnimatorのspeedの値を取得して、0かそうでないかを判定して一時停止と再生を再現したかったのだが、なぜかうまくいかなかったのでスイッチを二つ用意してスイッチごと切り替える方法をとっている。脳筋か?
4.Colliderの衝突を検知する
ColliderとRigidbodyのあるオブジェクトはCollider同士の衝突を判定できる。
基本的な処理の流れとしては、「OnTriggerEnter(OnCollisionEnter)で何らかのオブジェクトの衝突を検知する」→「衝突してきたオブジェクトが何であるかを判定する」→「目的のオブジェクトだった場合次の処理に進む」といった感じだと思われる。
Enterが当たったとき、Exitは当たってから離れたとき、Stayは使ったことないけど多分接触してる間ずっと処理が続く。
4-1.OnTrigger~
衝突する/されるオブジェクトの少なくとも1つにおいて、Colliderコンポーネントの「IsTrigger」が有効であり、かつRigidbodyコンポーネントを持つ場合に用いる。(IsTriggerとはざっくりいうと当たり判定をなくす。)使用する変数はGameObject、ノードは「Collider.Get GameObject」「GameObject.Equal」
4-2.OnCollision~
衝突する/されるオブジェクトの両方が当たり判定とRigidbodyコンポーネントを持つ場合に用いる。基本の構造は上と同じだが、「Collider.」系統だったところが「Collision.」系統になっていることに注意。
4-3.OnPlayerTrigger~
プレイヤーがオブジェクトに接触したことを検知する。特に条件を付けない場合、自分であれ他人であれとにかく誰かが接触すると処理が始まる。つまりグローバル。ローカルにしたい場合は「接触したプレイヤーが自分であるかどうか」の判定を挟む(後述)。
Trigger系統なので、対象のオブジェクトはIsTriggerが有効である必要がある(Rigidbodyは必要ない)。当たり判定がなくなるので、地面とかに設定する場合は別にコライダーを用意しておかないとすり抜ける。
「当たり判定がなくなって困るならCollision系統使えばいいじゃないか」と思うかもしれない。私も思った。だが動作しないんだよ。なんで?どうもOnPlayerCollision~系統は使えないらしいが…
5.skyboxを切り替える
使用する変数はMaterial、ノードは「RenderSettings.Set(Get) skybox」。
「今のskyboxがnewvaliableじゃないとき、newvaliableのskyboxに切り替える」。
6.ランダムな値を生成する
「Random.Range」というノードを使うと最大値と最小値を指定してその中からランダムな値を1つ取り出せる。[]のついた変数は「配列」というものらしく、複数の対象を並べることができる。この配列の中から1つ選んで表示する、というのが下のGraph。
floatは「~以上~以下」だがintだと「~以上~未満」なので注意。
7.値を保存する
変数をD&DするときにCtrlを押しながらやると「Set valiable」というノードになる。これを使うと変数に値を代入できるので、例えば「動かしたオブジェクトを初期位置に戻したい」というときに使える。
8.”AまたはBまたはCまたは…”を再現する
「Array.IndexOf」というノードを使うと、「AのときもBのときも同じ処理を起こしたい」という仕組みを一つのUdonbehaviorで完結させられる。下の例は、Colliderの衝突相手の判定に複数のオブジェクトを指定できるGraph。なぜそうなるのかという理屈は下のツイート参照。
あ、OnTriggerEnterの時点では「このオブジェクトになんか当たったぞ」をまず認識して、その後で「“なんか”とはなんぞや」を判定してbranchに繋げてるのかな?
— 雨降り(あめふり) (@It__was__rainy) 2021年5月10日
普通は「gameobject equal」で「“なんか”がaの時だけ次に行くぞ」って処理してたのを、「この“なんか”はこの配列のここにあるぞ」を取れるようにして、「この配列のどれとってもOKね」っていう条件にすることで結果的に「aまたはbまたは…」になるのか
— 雨降り(あめふり) (@It__was__rainy) 2021年5月10日
9.プレイヤーが自分であるかどうかを判定する
前でちょっと触れた、オブジェクトに接触したり、椅子に座ったりしているプレイヤーが「自分かどうか」を判定するやつ。使うノードは「Networking.Get LocalPlayer」「PlayerAPi.Equal」。
3.UdonBehaviorとuGUIを連動させる
CanvasUIパネルからUdonの処理を呼び出す。自分がやったのはSliderだけなので紹介はそれだけだが、Toggleなら同じようにしてinteractを呼び出すようにすればいいと思う。
なお、私が試した限りではどうあがいても同期しなかった。同期するSliderについてはAnimatorを使うシステムが公開されてるのでそちらをどうぞ↓。
Synced slidersに自作の機能を加える - Qiita
1.Sliderでオブジェクトのシェイプキーを変化させる
1-1.UdonGraphの組み方
使う変数は「UISlider」「SkinedMeshRenderer」、ノードは「SkinedMeshRenderer.Setblendshapeweight」「UISlider.Get Value」。どのシェイプキーを動かしたいかは「SkinedMeshRenderer.Setblendshapeweight」の「index」で指定する(上から順に・0から数えること)。
画像は同期させたくて頑張ってた跡なので余計なものが多い。グループ化してる部分はいらないしどうあがいても同期しないのでcustomevent化する必要もない。あとAdditionも「0を足す」設定なのでこれもいらない。
1-2.Slider側の設定
「On Value Change」に対象のUdonBehaviorがあるオブジェクトを指定し、「UdonBehavior.OnValidate()」を選ぶ。今回動かすのはシェイプキーなのでMinValue・MaxValueをシェイプキーの最大・最小値に合わせる。
4.応用・番外編
1.Eventノードを組み合わせて処理を行う条件をつくる
Eventノードには「EnterとExit」「UpとDown」のように、条件的に対になっているものがある。これをうまく組み合わせると、例えば「オブジェクトを持っている時だけ」「椅子に座っている時だけ」といった条件を再現できる。できるが、多分”オブジェクトがPickupされてるかどうか””プレイヤーが椅子に座ってるかどうか”をBranchで判定する仕組みは作れるのでこんなことしなくてもいいと思います。作ったときは思いつかなかったんです…。
1-1.「持っている時だけあるUdonBehaviorが有効になる」
カメラの遠隔シャッターを持っている時だけ、Rotationの共有を有効にするために使用。
1-2.「自分が座っている時だけあるオブジェクトが表示される」
自分が椅子に座っている時だけ、椅子の高さを変えるスイッチを表示するために使用。
2.椅子の高さを変える
「今の椅子のy座標」に「0.1/-0.1を足す」ことで、interactするたびに上がったり下がったりする。
3.Questにおけるワールド入室時のBGM鳴らない問題の回避
Questにて、ワールドに入ったときにBGMが流れないことがある現象について
— 雨降り(あめふり) (@It__was__rainy) 2021年4月22日
play on awakeだけだとうまく動かないことがあるようで(1回オンオフすると直るのでたぶんそう)、ワールドに入ったとき(オブジェクトが生成されたとき)に指定したaudioclipを再生させるような処理を作ると起こらなくなる pic.twitter.com/CLvtBZVHfK
4.視界ジャック
canvasのrender modeを「Screen SpaceーCamera」にすることで視界ジャックをすることができる。オンオフはGameObjectで組める。↓はワールド設置カメラで撮った画像を視界ジャックでスクショしやすくするシステム
5.デバッグログ
「Debug.Log」ノードを使うと、変数の値や「Get~」系のノードで取得した情報をConsoleに表示できる。思った値が取れているか、動かない場合どこに原因があるのかの判断に役立つ。
5.おわりに
とりあえず思いつく限りで汎用性のありそうなものは全部書きました。思い出したら追記するかもしれません。また、「これ解説してほしい」とか「この項目もっと詳しく」という要望があれば、私に答えられれば追記したり別記事立てたりします。しますが、作者はプログラミング素人故分からんことのほうが多いと思いますので期待はしないでください(そもそも解説したやつでも人に教えてもらったものが多い)。逆に言うと素人でもここまでできるようになる。UdonGraphすごい。SDK2でええやんとか言わない。
解説したGraphの一部を使用したギミックをBoothで配布していますので、中身が気になる方はどうぞ↓
【unity】VRCアバターのジャンプモーションの作成・変更
https://twitter.com/It__was__rainy/status/1370037437024456706?s=20
上記ツイートのようなジャンプモーションの作成・変更についてのまとめです。記事の内容はある程度unityの操作や仕組みを理解している人向けですが、VRC_AvaterDiscriptorに突っ込むだけでとりあえず動かせるサンプル(※低クオリティ)も用意していますので、上の動画まんまでもいいという方は使ってみてください。自分で設定したいという人でも一応参考になるかと思います。
1.LocomotionLayerの複製
ジャンプモーションを制御しているレイヤーを複製しておきましょう。ロコモーションレイヤーはステートマシンが入れ子状になっていて、ジャンプ時に関係するモーションは【JumpAndFall】内の【SmallHop】と【ShortFall】のステートに入っています(前者が上昇、後者が下降)。【LongFall】は高い場所から長く落ちるときのモーションです、たぶん。
2.ジャンプモーションの作成・変更
ではモーションを変更する手順ですが、『単純にジャンプ時のモーションを変えたい』だけであれば、【SmallHop】(こだわりがあればShortFallにも)に好きなモーションを入れればOKです。あ、アニメーションのループは解除しておきましょう。ループ状態だと上昇と下降の繋ぎ目がバタバタします。
・・・といって終わりたいところですが、ジャンプの挙動に少々癖があるので、ただ飛び上がるモーションをいれればよいというわけでもなさそうです(少なくとも私は結構苦戦しました)。ジャンプの時間はかなり短いので、飛び上がるまでの「溜め」が長いモーションだと見栄えがよろしくありません(私の設定が下手なだけという可能性もあります。要検証)。
※【SmallHop】のアニメーションは段差に引っかかるなどした時にも再生されるので、その場で飛び上がることだけを想定したアニメーションだとそういう時に不自然になります。後述のようにデフォルトのステートは残したまま切り替えられるようにすることをおすすめします。(3/13追記)
以下、私がいろいろと試してみて「これくらいならちょうどよさそう」と感じたアニメーションの作り方を紹介します。
まずアニメーションの長さですが、後で紹介するサンプルは上昇・下降それぞれ0.2秒(20フレーム)ずつになっています(アニメーションの長さがどれほど滞空時間に影響するのかは未検証ですので情報をお待ちしております・・・)。デフォルトのジャンプに近づけています。
また、キーフレームの打ち方ですが、ジャンプモーションは時間が短すぎるので正直最初と最後だけしっかり作ってあれば問題ないと思います(サンプルは2~3フレームしかキーを打っていません)。
・【SmallHop】のアニメーション
0フレーム目に最初の立ち姿勢を打ちます。こだわりがなければSDKにデフォルトで入っているproxy_stand_stillのコピペでも特に違和感のないモーションができます。
20フレーム目に最高点のポーズを打ちます。理想のポージングを追い求めてください。アバターの高さはRootTのy座標で調整します。
・【ShortFall】のアニメーション
SmallHopができていればこちらは最初と最後のフレームを入れ替えればとりあえず形になります。もちろん着地にこだわってアニメーションを作るもよし。
ひと通りキーを打てたらシークバー的なあれを動かしてアニメーションを確認、必要に応じてキーを増やすなど調整しましょう。
さて、アニメーションができたらアニメーションファイルの設定をします。私も詳しくはわかりませんがRootTを使うアニメーションはちゃんと設定しないとRootTの値が反映されません。
ひとまず下の画像のようにしておけば動きます。もしアニメーションファイルのインスペクターにこのような項目がないという場合はサンプルのアニメーションを複製して使ってください。
ここまで設定できたらあとはステートに放り込んでおけばジャンプモーションの変更は完了です。
手の動きをつけたければトラッキングコントールをanimationに設定してきましょう。
※【SmallHop】【ShortFall】のトラッキングコントールをanimationにした場合、【Standing】のステートにもトラッキングコントールを追加し、animationにした部分をtrackingに設定してください。特定の挙動時にトラッキングが切れたままになります。(3/12追記)
3.ジャンプモーションの切り替え
ここまでジャンプモーションの変更について紹介してきましたが、新しく変数を設定すればオブジェクトのオンオフと同様にジャンプモーションをexpressionmenuで切り替えることができます。
まずは新しくInt型変数を設定しましょう(2つだけの切り替えならboolでOK)。
下の画像のようにステートを追加して、遷移条件は既存のものと同様に設定しておきます(赤が上昇”SmallHop”、青が下降”ShortFall”のステートと同様です。矢印がごちゃごちゃしていますがひとつずつ丁寧に見比べていきましょう)。
あとはstandingからの遷移条件に先ほど設定した変数を追加すればOKです。
expressionparametersに変数を追加してexpressionmenuの設定もしましょう。このあたりの詳細な説明は割愛します。parametersとmenuは設定済みのものをサンプルに入れているのでわからなければ参考にしてください。
長くなりましたが以上でジャンプモーションの設定はすべて終了です。お疲れ様でした。
サンプルアニメーションの入ったunitypackageはこちらからどうぞ↓
VRC用ジャンプアニメーションサンプル - 雨宿り - BOOTH
【blender】VRCアバターの裏面透明問題を解決したい
上の画像を見てください。
大きい葉っぱは裏側が見えているのに、小さい葉っぱは命を刈り取る形をして見えますね。
VRCで見かけるアバターやアクセサリーには、このように裏側が見えないことが良くあります。「スカートやロングヘアの内側が透明に見える」という経験がある人も多いかと思います。
この記事は、その現象をなんとか解決しようとして発見した、私なりの解決策を紹介するものです。また、ある程度unityとblenderの知識・操作感を把握している人向けの記事になります(裏側透明問題を解決したいという段階に来てる人は基本的なことはできると思いますので)。
まずは、問題の3Dモデルをblenderに放り込んで状態を見てみましょう。
今回は上記の葉っぱの傘をモデルケースとしてみていきます。
見えるかな?これ、メッシュが一層しかないんですよね。
より分かりやすいように、面の裏表を表示させてみます。(オーバーレイ→面の向き にチェック)
青が表、赤が裏です。
裏側から見ると真っ赤になっています。
通常3Dモデルは青いほうしか描画されないので、赤くなっている方向から見ると透明に見えます。
「シェーダーによっては見えるんだけど・・・」というケースは、シェーダーの「カリングoff」という機能を使って、テクスチャを裏側に透けるようにしているんだと思います。詳しくはカリングoffについて調べてみてね。
じゃあこれをどうするのかというと、裏側からも青い面が見えるようにしてあげればいいわけです。私のやり方は
1 裏側を描画したい面を選択
2 メッシュを複製
3 位置を調整
3 面の裏表を反転
4 (場合によって)隙間を埋めるなど微調整
やってることは非常に単純というか原始的というかアナログです。なにか素晴らしい技術を期待していた人、ごめんなさい。
では実際の手順を見ていきましょう。
1 面を選択します。(shift押しながら選択すると複数選択)
2 メッシュを複製します(複製したらマウスを動かさずに左クリックorEnterキー)。
3 位置を調整します(G+XとかYとかZ)。葉っぱの裏を描画したいので、少しだけ下にずらします。スカートの裏側に移動させたい場合などはスケールも少しだけ調整する必要があるかもしれません(Sキー)。
4 面を反転させます。
5 裏側も青くなりました。(気になる人は隙間を埋めるなどしましょう)
UVマップは複製元と同一(たぶん)なので適用されるテクスチャは元の部分と同じになります。スカートの裏側など見た目も変えたい場合は、まあ、別途調べてください…。
以上、裏側透明問題を力技で解決する解説でした。正直PCアバターはシェーダーで解決できるので、Quest対応しない場合はあまり関係ないかもしれません。ただ、PCアバターでもセーフティの関係で描画されないことがあるようなので、知ってて損はないかも?
UnityでVRCアバター改変する時に見たことあるエラー集(blenderもちょこっと)
2023/05/25
全体を改訂しました。画像は古いままです。
この記事は私が遭遇したことのあるエラー(と思い通りにいかなかったところ)を備忘録的にまとめたものです。詳しいことはそれぞれ検索したほうが分かるかと思います。ちょっとした索引程度に役立てていただければ幸いです。
- ・SDKエラー
- This avater is short. This is probably shorter than you want.
- This avatar does not contain an animator, and will not animate in VRChat.A VRC_SceneDescriptor or VRC_AvatarDescriptor is required to build VRChat SDK Content.
- This avatar uses Visemes but face Mesh is not specified.
- Humanoid avatar must have head, hands and feet bones mapped.Your avatar is humanoid, bou its feet/upper arms aren't specified!This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set.
- Error saving blueprint.
- ・Unityのエラーとか
- ・ エラーではないけどなんかうまくいかないやつ
- おまけ~blender編~
・SDKエラー
This avater is short. This is probably shorter than you want.
→「このアバターは思っているよりも身長が低いかも」って聞かれてるだけなのでエラーとかじゃない。気にしなくてOK。
This avatar does not contain an animator, and will not animate in VRChat.
A VRC_SceneDescriptor or VRC_AvatarDescriptor is required to build VRChat SDK Content.
アバターのInspectorにAnimatorまたはVRC_AvatarDescriptorが入っていないよーというメッセージ。
①市販アバターの設定済Prefabをアップロードしたい場合
→FBXの方をhierarchyに置いていないか?
置くのはPrefabかunitypackageもしくはSceneファイルをダブルクリックで開く(青いほう)。
FBX、Prefab、Scene等について詳しいことは調べてください。
②blenderとかで改変したFBXを使いたい場合
→Add ComponentからAnimator/VRC_AvatardDiscriptorを追加して設定する。
基本的に元アバターからコピペするのが楽。そういうツールもある。
Animatorがないって言われるときはFBXがHumanoidじゃないかも。Humanoidにするか、GenericのままアップロードしたいならAnimatorの追加が必要。
This avatar uses Visemes but face Mesh is not specified.
→リップシンクなどの設定をしているけど、該当メッシュがないよみたいなやつ。
VRC_AvatardDiscriptorの中にあるFace MeshがNoneになってるかも。アバターの顔のメッシュを指定する。
Humanoid avatar must have head, hands and feet bones mapped.
Your avatar is humanoid, bou its feet/upper arms aren't specified!
This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set.
→ボーンマッピングに不備がある。市販アバターのFBXは基本的に正しく設定してあるので問題はない、はず。
改変FBXや自作FBXの場合前述のHumanoidの設定とかが必要になってくるので、詳しくは「Unity Humanoid ボーンマッピング」とかで検索してください。
Error saving blueprint.
→アバターに割り当てられているBlueprintIDの不備。私は削除したアバターのIDをそのまま使おうとしたときに遭遇。「Pipeline Manager(script)」から「Detach(optional)」(IDを空にして新しいアバターIDを割り当てる)で解決。
・Unityのエラーとか
ArgumentException:EditorSceneManager.Setactivefailed;you can not save a preview scene.
→AssetからHierarchyにファイルをD&Dしているか?ダブルクリックだとアップロード時にエラーになるらしい。
これ最近見ないのでどういうエラーなのかいまだにわかりません。
FileNotFoundException:
親の顔より見たFileNotFoundException原因となりうるものが幅広いのでこれだけではエラーを特定できないが、多いのはMissingScript。Avatar3.0とPhysBoneが普及したので最近は見なくなってきた。
MissingScriptを取り除くツールとかもあるので自力で探すのが面倒な時は活用してみると良い。
Cannot restructure Prefab instance
Prefabの階層構造などをHierarchy上でいじくろうとすると出てくるやつだった気がする。Prefabの編集はちょっと特殊なので調べてください。
最近はModular Avatar等の非破壊改変ツールが出てきているのでそれらも活用してみると良い。どうしてもその場でいじる必要があるなら右クリック→UnpackPrefab。
Prefabに他のオブジェクトなどを追加する(アクセサリをHeadボーン直下に置くとか)場合、boneとboneの隙間じゃなくて、親boneの名前に重ねるように置く必要がある。
Invalid AABB ~
→詳細不明だがtransformの数値に不備があるときのエラーっぽい?Unityの再起動などで直った報告もある。ほとんど見ない。
Asset bundle upload failed
→アップロード時に見た。よくわからん。Unity再起動しよう。
・ エラーではないけどなんかうまくいかないやつ
Q.Quest対応の為にシェーダー変えたら裏側透明になった
片面メッシュだとそうなる。裏に面を貼ると解決できる。ソリッドモデファイアとか便利。
【blender】VRCアバターの裏面透明問題を解決したい - It__was__rainy’s blog (hatenablog.com)
Q.パーティクルをWorld設定にしたい
なんか最近はパーティクルのWorld設定時の挙動治ったらしいです。
Q.パーティクル出ない
→「Play On Awake」にチェック入れてるか?入れてて出ないならわからない。
Q.シェーダーを変更できない
→FBXをワンクリックしてMaterialのタブのLocationをUse External Materials(Legacy)にするといじれるようになる。
もしくはProjectで右クリック→Create→Materialで新しいMaterial作って割り当てるとか。
Q.オブジェクトがピンクor黒
ピンクのやつはマテリアルエラーと言われるやつ。検索したらたぶんたくさん解説出てくる。
黒くなったり変な色になったりするのは頂点カラーが原因のことがある。頂点カラー取り除いてくれるツールもある。
Q.リアルで気を付けの姿勢なのにアバターの手が変な位置にある
→view pointのZ軸を下げるとマシになる。詳しいことは他に解説してくれてる人がいるので探してくれ。
最近はVRC内での身長設定やIK設定をいろいろいじれるようになっているのであんまり必要ないかも。
Q.エラー出てないのにbuildできない or upload失敗する
→とりあえずよくあるらしいのがSDKコントロールパネルの「setting」の「publish」のとこにチェック入ってるパターン。他はよくわからない。
Q.アバターいじってたら表情がなくなった
→同一Scene上で複製したアバターを扱ってるときはメッシュの参照とかちゃんとしてるか確認する(参照元が、非表示にした複製元のメッシュやボーンになっていることがままある。特にリップシンクとアイトラッキング)。
また、オブジェクトの階層や名前を変えるとアニメ―ションのパスが変わってメッシュを参照できなくなるので注意。
↓body→bodeyminiに名前を変更したため参照元のメッシュが全部missingになっている
おまけ~blender編~
頂点いじったらリップシンクがいらんとこにくっついてきた
→動かしたくない頂点を選択、「頂点」から「シェイプに反映」
編集モードとオブジェクトモードで見えてる世界が違う
→「シェイプキー」で「Basis」を選んでるかどうか。各シェイプキーを選択しているとそのシェイプキーを編集する状態になる。
またポーズモードの変更は編集モードには反映されない。Catプラグインとか使おう。