特に役に立つことは書いてないワールド制作にまつわる日記①

自分のワールドとその制作背景について語りたくなったので自分語りのための記事第一弾を書きました。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.はじめに

1.この記事を読むのに前提となる知識

まずはこのへんを読んでくれ。

Udon のはじめかた|toh|note

sdk3のudonでミラーをオンオフする(ついでに同期させる)|かくちゅ|note

Udon初めの初歩の初歩|tenehimu|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」。

f:id:It__was__rainy:20210512002129p:plain

1-2.Collider/MeshRenderer/UdonBehavior ect...

上記のような「あるオブジェクトの特定のコンポーネント」をオンオフする。

基本の構造は、

①「目的のコンポーネントに対応した変数を使う」

②「GameObject変数から目的のtypeのコンポーネントを取得する」

のどちらか。

①の場合。変数は目的のコンポーネントと同じ名前のもの。GameObjectでいうSetActiveと同じような働きをするのがSet enable。

f:id:It__was__rainy:20210512002043p:plain

 

②の場合。変数はGameObject。typeは目的のコンポーネント。GameObject.GetConponentは指定したオブジェクトのさらに特定のコンポーネントを取得する。

f:id:It__was__rainy:20210512002110p:plain

 

これの利点は、「自分を対象とするオンオフ機能のあるオブジェクトを量産したい」ときに、変数にいちいち対象を指定しなくていいこと。(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」で座標を取得すればいいと思います多分。

f:id:It__was__rainy:20210512004149p:plain

「指で触って弾けるピアノのギミック」に使用した、プレイヤーの指のボーンにオブジェクトを追従させる仕組み。

2-2.オブジェクトの回転

変数・ノードは「Transform」系統。これもたぶん上と同じ感じで「Rotate」と「Set rotation」がある。

f:id:It__was__rainy:20210512002102p:plain

「別のオブジェクトのRotationを反映させる」。

カメラの遠隔シャッターでカメラ本体の向きを調整するために使用。

2-3.プレイヤーをワープさせる

使うノードは「Networking.Get locaoplayer」「PlayerAPi.TeleportTo」。ワープ先の位置・向きは手打ちで指定してもいいし、適当なオブジェクトを置いてそいつのTransform値を取得して代入してもいい。後者が楽。

f:id:It__was__rainy:20210512002022p:plain

 

3.Animatorを操作する

オブジェクトにAnimatorコンポーネントをつけてAnimatorControllerを放り込み、そいつの中身をいじる。使う変数・ノードは「Animator」系統。

3-1.Animationを再生する

「Animator.Play」で特定のステートを再生できる。レイヤーが複数あるとレイヤー名も指定しないといけないらしいが、どう記述するのか忘れました。まあアニメーション再生するならステート名で指定するより後述の変数切り替えでステート遷移させるほうが確実なのでそっち使いましょう。

f:id:It__was__rainy:20210512034836p:plain

3-2.変数を切り替える

AnimatorControllerの中身をアバター3.0でやってるみたいに設定したうえで、ExpressionMenuで操作するみたいに変数を切り替えるGraph。使うノードは「Animator.Set○○」(指定したい変数と同じ型)。

f:id:It__was__rainy:20210512035415p:plain

3-3.speedをいじる(疑似的な一時停止)

 Animatorの各ステートにはアニメーションの再生スピードを設定する項目があるが、その値をいじることができる。これを0にするとアニメーションの一時停止を再現できる。使うノードは「Animator.Set speed」。

f:id:It__was__rainy:20210512035458p:plain

f:id:It__was__rainy:20210512035520p:plain

元々は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」

f:id:It__was__rainy:20210512035026p:plain

4-2.OnCollision~

衝突する/されるオブジェクトの両方が当たり判定とRigidbodyコンポーネントを持つ場合に用いる。基本の構造は上と同じだが、「Collider.」系統だったところが「Collision.」系統になっていることに注意。

f:id:It__was__rainy:20210512035143p:plain

4-3.OnPlayerTrigger~

 プレイヤーがオブジェクトに接触したことを検知する。特に条件を付けない場合、自分であれ他人であれとにかく誰かが接触すると処理が始まる。つまりグローバル。ローカルにしたい場合は「接触したプレイヤーが自分であるかどうか」の判定を挟む(後述)。

Trigger系統なので、対象のオブジェクトはIsTriggerが有効である必要がある(Rigidbodyは必要ない)。当たり判定がなくなるので、地面とかに設定する場合は別にコライダーを用意しておかないとすり抜ける。

「当たり判定がなくなって困るならCollision系統使えばいいじゃないか」と思うかもしれない。私も思った。だが動作しないんだよ。なんで?どうもOnPlayerCollision~系統は使えないらしいが…

f:id:It__was__rainy:20210512002119p:plain

「プレイヤーの接触で音を鳴らす」

5.skyboxを切り替える

使用する変数はMaterial、ノードは「RenderSettings.Set(Get) skybox」。

「今のskyboxがnewvaliableじゃないとき、newvaliableのskyboxに切り替える」。

f:id:It__was__rainy:20210512001953p:plain

6.ランダムな値を生成する

「Random.Range」というノードを使うと最大値と最小値を指定してその中からランダムな値を1つ取り出せる。[]のついた変数は「配列」というものらしく、複数の対象を並べることができる。この配列の中から1つ選んで表示する、というのが下のGraph。

f:id:It__was__rainy:20210512002034p:plain

floatは「~以上~以下」だがintだと「~以上~未満」なので注意。

7.値を保存する

変数をD&DするときにCtrlを押しながらやると「Set valiable」というノードになる。これを使うと変数に値を代入できるので、例えば「動かしたオブジェクトを初期位置に戻したい」というときに使える。 

f:id:It__was__rainy:20210512024140p:plain

「Cube」の初期位置をVector3という変数に保存し、「Sphere」の移動先の座標として使っている。

8.”AまたはBまたはCまたは…”を再現する

「Array.IndexOf」というノードを使うと、「AのときもBのときも同じ処理を起こしたい」という仕組みを一つのUdonbehaviorで完結させられる。下の例は、Colliderの衝突相手の判定に複数のオブジェクトを指定できるGraph。なぜそうなるのかという理屈は下のツイート参照。

f:id:It__was__rainy:20210512002139p:plain

「fingers」で指定したオブジェクトすべてをColliderの接触相手として判定する。

 

9.プレイヤーが自分であるかどうかを判定する

前でちょっと触れた、オブジェクトに接触したり、椅子に座ったりしているプレイヤーが「自分かどうか」を判定するやつ。使うノードは「Networking.Get LocalPlayer」「PlayerAPi.Equal」。

f:id:It__was__rainy:20210512025546p:plain

「座ったプレイヤー」が「自分」と「等しいかどうか」

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を足す」設定なのでこれもいらない。

f:id:It__was__rainy:20210512030604p:plain

「一番上のシェイプキー」に「Sliderの値」を代入する。
1-2.Slider側の設定

「On Value Change」に対象のUdonBehaviorがあるオブジェクトを指定し、「UdonBehavior.OnValidate()」を選ぶ。今回動かすのはシェイプキーなのでMinValue・MaxValueをシェイプキーの最大・最小値に合わせる。

f:id:It__was__rainy:20210512031448p:plain

4.応用・番外編

1.Eventノードを組み合わせて処理を行う条件をつくる

Eventノードには「EnterとExit」「UpとDown」のように、条件的に対になっているものがある。これをうまく組み合わせると、例えば「オブジェクトを持っている時だけ」「椅子に座っている時だけ」といった条件を再現できる。できるが、多分”オブジェクトがPickupされてるかどうか””プレイヤーが椅子に座ってるかどうか”をBranchで判定する仕組みは作れるのでこんなことしなくてもいいと思います。作ったときは思いつかなかったんです…。

1-1.「持っている時だけあるUdonBehaviorが有効になる」

カメラの遠隔シャッターを持っている時だけ、Rotationの共有を有効にするために使用。

f:id:It__was__rainy:20210512033703p:plain

 

1-2.「自分が座っている時だけあるオブジェクトが表示される」

自分が椅子に座っている時だけ、椅子の高さを変えるスイッチを表示するために使用。

f:id:It__was__rainy:20210512033853p:plain

2.椅子の高さを変える

「今の椅子のy座標」に「0.1/-0.1を足す」ことで、interactするたびに上がったり下がったりする。

f:id:It__was__rainy:20210512034325p:plain

3.Questにおけるワールド入室時のBGM鳴らない問題の回避

f:id:It__was__rainy:20210512035839p:plain

4.視界ジャック

canvasのrender modeを「Screen SpaceーCamera」にすることで視界ジャックをすることができる。オンオフはGameObjectで組める。↓はワールド設置カメラで撮った画像を視界ジャックでスクショしやすくするシステム

f:id:It__was__rainy:20210512133859p:plain

f:id:It__was__rainy:20210512133933p:plain

5.デバッグログ

「Debug.Log」ノードを使うと、変数の値や「Get~」系のノードで取得した情報をConsoleに表示できる。思った値が取れているか、動かない場合どこに原因があるのかの判断に役立つ。f:id:It__was__rainy:20210512134716p:plain

f:id:It__was__rainy:20210512134742p:plain

Vector3変数「CubePosFirst」にちゃんと初期位置が代入できていることがわかる。

5.おわりに

とりあえず思いつく限りで汎用性のありそうなものは全部書きました。思い出したら追記するかもしれません。また、「これ解説してほしい」とか「この項目もっと詳しく」という要望があれば、私に答えられれば追記したり別記事立てたりします。しますが、作者はプログラミング素人故分からんことのほうが多いと思いますので期待はしないでください(そもそも解説したやつでも人に教えてもらったものが多い)。逆に言うと素人でもここまでできるようになる。UdonGraphすごい。SDK2でええやんとか言わない。

 

解説したGraphの一部を使用したギミックをBoothで配布していますので、中身が気になる方はどうぞ↓

雨宿り - BOOTH

【unity】VRCアバターのジャンプモーションの作成・変更

https://twitter.com/It__was__rainy/status/1370037437024456706?s=20

上記ツイートのようなジャンプモーションの作成・変更についてのまとめです。記事の内容はある程度unityの操作や仕組みを理解している人向けですが、VRC_AvaterDiscriptorに突っ込むだけでとりあえず動かせるサンプル(※低クオリティ)も用意していますので、上の動画まんまでもいいという方は使ってみてください。自分で設定したいという人でも一応参考になるかと思います。

 

 

 1.LocomotionLayerの複製

ジャンプモーションを制御しているレイヤーを複製しておきましょう。ロコモーションレイヤーはステートマシンが入れ子状になっていて、ジャンプ時に関係するモーションは【JumpAndFall】内の【SmallHop】と【ShortFall】のステートに入っています(前者が上昇、後者が下降)。【LongFall】は高い場所から長く落ちるときのモーションです、たぶん。

 

f:id:It__was__rainy:20210312021644p:plainf:id:It__was__rainy:20210312021711p:plain

 

 2.ジャンプモーションの作成・変更

ではモーションを変更する手順ですが、『単純にジャンプ時のモーションを変えたい』だけであれば、【SmallHop】(こだわりがあればShortFallにも)に好きなモーションを入れればOKです。あ、アニメーションのループは解除しておきましょう。ループ状態だと上昇と下降の繋ぎ目がバタバタします。

・・・といって終わりたいところですが、ジャンプの挙動に少々癖があるので、ただ飛び上がるモーションをいれればよいというわけでもなさそうです(少なくとも私は結構苦戦しました)。ジャンプの時間はかなり短いので、飛び上がるまでの「溜め」が長いモーションだと見栄えがよろしくありません(私の設定が下手なだけという可能性もあります。要検証)。

※【SmallHop】のアニメーションは段差に引っかかるなどした時にも再生されるので、その場で飛び上がることだけを想定したアニメーションだとそういう時に不自然になります。後述のようにデフォルトのステートは残したまま切り替えられるようにすることをおすすめします。(3/13追記)

 

以下、私がいろいろと試してみて「これくらいならちょうどよさそう」と感じたアニメーションの作り方を紹介します。

まずアニメーションの長さですが、後で紹介するサンプルは上昇・下降それぞれ0.2秒(20フレーム)ずつになっています(アニメーションの長さがどれほど滞空時間に影響するのかは未検証ですので情報をお待ちしております・・・)。デフォルトのジャンプに近づけています。

また、キーフレームの打ち方ですが、ジャンプモーションは時間が短すぎるので正直最初と最後だけしっかり作ってあれば問題ないと思います(サンプルは2~3フレームしかキーを打っていません)。

 

・【SmallHop】のアニメーション

0フレーム目に最初の立ち姿勢を打ちます。こだわりがなければSDKにデフォルトで入っているproxy_stand_stillのコピペでも特に違和感のないモーションができます。

20フレーム目に最高点のポーズを打ちます。理想のポージングを追い求めてください。アバターの高さはRootTのy座標で調整します。

f:id:It__was__rainy:20210312021913p:plainf:id:It__was__rainy:20210312021937p:plain

 

・【ShortFall】のアニメーション

SmallHopができていればこちらは最初と最後のフレームを入れ替えればとりあえず形になります。もちろん着地にこだわってアニメーションを作るもよし。

 

ひと通りキーを打てたらシークバー的なあれを動かしてアニメーションを確認、必要に応じてキーを増やすなど調整しましょう。

さて、アニメーションができたらアニメーションファイルの設定をします。私も詳しくはわかりませんがRootTを使うアニメーションはちゃんと設定しないとRootTの値が反映されません。

ひとまず下の画像のようにしておけば動きます。もしアニメーションファイルのインスペクターにこのような項目がないという場合はサンプルのアニメーションを複製して使ってください。

f:id:It__was__rainy:20210312022140p:plain

 

 ここまで設定できたらあとはステートに放り込んでおけばジャンプモーションの変更は完了です。

手の動きをつけたければトラッキングコントールをanimationに設定してきましょう。

※【SmallHop】【ShortFall】のトラッキングコントールをanimationにした場合、【Standing】のステートにもトラッキングコントールを追加し、animationにした部分をtrackingに設定してください。特定の挙動時にトラッキングが切れたままになります。(3/12追記)

 

 3.ジャンプモーションの切り替え

ここまでジャンプモーションの変更について紹介してきましたが、新しく変数を設定すればオブジェクトのオンオフと同様にジャンプモーションをexpressionmenuで切り替えることができます。

まずは新しくInt型変数を設定しましょう(2つだけの切り替えならboolでOK)。

下の画像のようにステートを追加して、遷移条件は既存のものと同様に設定しておきます(赤が上昇”SmallHop”、青が下降”ShortFall”のステートと同様です。矢印がごちゃごちゃしていますがひとつずつ丁寧に見比べていきましょう)。

f:id:It__was__rainy:20210312022819j:plain

 

あとはstandingからの遷移条件に先ほど設定した変数を追加すればOKです。

 

f:id:It__was__rainy:20210312023258j:plainf:id:It__was__rainy:20210312023318j:plain


expressionparametersに変数を追加してexpressionmenuの設定もしましょう。このあたりの詳細な説明は割愛します。parametersとmenuは設定済みのものをサンプルに入れているのでわからなければ参考にしてください。

 

長くなりましたが以上でジャンプモーションの設定はすべて終了です。お疲れ様でした。

 サンプルアニメーションの入ったunitypackageはこちらからどうぞ↓

VRC用ジャンプアニメーションサンプル - 雨宿り - BOOTH

 

 

【blender】VRCアバターの裏面透明問題を解決したい

f:id:It__was__rainy:20210223030453p:plain

上の画像を見てください。

大きい葉っぱは裏側が見えているのに、小さい葉っぱは命を刈り取る形をして見えますね。

VRCで見かけるアバターやアクセサリーには、このように裏側が見えないことが良くあります。「スカートやロングヘアの内側が透明に見える」という経験がある人も多いかと思います。

この記事は、その現象をなんとか解決しようとして発見した、私なりの解決策を紹介するものです。また、ある程度unityとblenderの知識・操作感を把握している人向けの記事になります(裏側透明問題を解決したいという段階に来てる人は基本的なことはできると思いますので)。

 

 

まずは、問題の3Dモデルをblenderに放り込んで状態を見てみましょう。

今回は上記の葉っぱの傘をモデルケースとしてみていきます。

f:id:It__was__rainy:20210223032500p:plain

見えるかな?これ、メッシュが一層しかないんですよね。

より分かりやすいように、面の裏表を表示させてみます。(オーバーレイ→面の向き  にチェック)

f:id:It__was__rainy:20210223032818p:plain

青が表、赤が裏です。

裏側から見ると真っ赤になっています。

通常3Dモデルは青いほうしか描画されないので、赤くなっている方向から見ると透明に見えます。

「シェーダーによっては見えるんだけど・・・」というケースは、シェーダーの「カリングoff」という機能を使って、テクスチャを裏側に透けるようにしているんだと思います。詳しくはカリングoffについて調べてみてね。

じゃあこれをどうするのかというと、裏側からも青い面が見えるようにしてあげればいいわけです。私のやり方は

1 裏側を描画したい面を選択

2 メッシュを複製

3 位置を調整

3 面の裏表を反転

4 (場合によって)隙間を埋めるなど微調整

やってることは非常に単純というか原始的というかアナログです。なにか素晴らしい技術を期待していた人、ごめんなさい。

 

では実際の手順を見ていきましょう。

 

1 面を選択します。(shift押しながら選択すると複数選択)

f:id:It__was__rainy:20210223040041p:plain

 

2 メッシュを複製します(複製したらマウスを動かさずに左クリックorEnterキー)。

f:id:It__was__rainy:20210223040108p:plain

 

3 位置を調整します(G+XとかYとかZ)。葉っぱの裏を描画したいので、少しだけ下にずらします。スカートの裏側に移動させたい場合などはスケールも少しだけ調整する必要があるかもしれません(Sキー)。

f:id:It__was__rainy:20210223040252p:plain

 

4 面を反転させます。

f:id:It__was__rainy:20210223040452p:plain

 

5 裏側も青くなりました。(気になる人は隙間を埋めるなどしましょう)

f:id:It__was__rainy:20210223040735p:plain

UVマップは複製元と同一(たぶん)なので適用されるテクスチャは元の部分と同じになります。スカートの裏側など見た目も変えたい場合は、まあ、別途調べてください…。

 

以上、裏側透明問題を力技で解決する解説でした。正直PCアバターはシェーダーで解決できるので、Quest対応しない場合はあまり関係ないかもしれません。ただ、PCアバターでもセーフティの関係で描画されないことがあるようなので、知ってて損はないかも?

 

 

UnityでVRCアバター改変する時に見たことあるエラー集(blenderもちょこっと)

【追記】予想以上に多くの人に認知されたようなのでちょこっと追記。この記事は私が遭遇したことのあるエラー(と思い通りにいかなかったところ)を備忘録的にまとめたものです。詳しいことはそれぞれ検索したほうが分かるかと思います。ちょっとした索引程度に役立てていただければ幸いです。

 

【エラー文】

This avater is short. This is probably shorter than you want.

→「このアバターは思っているよりも身長が低いかも」って聞かれてるだけなのでエラーとかじゃない。気にしなくてOK。

f:id:It__was__rainy:20210203140214j:plain

 

ArgumentException:EditorSceneManager.Setactivefailed;you can not save a preview scene.

→assetからhierarchyにファイルをD&Dしているか?ダブルクリックだとupload時にエラーになる。理由はわからない。

 

This avater does not contain an animator, and wil not animate in VRChat.

A VRC_SceneDescriptor or VRC_AvaterDescriptor is required to build VRChat SDK Content.

→inspectorにanimatorまたはVRC_Avaterdescriptorが入っていないよーというメッセージ。

①なんの手も加えていない設定済みアバターをuploadしたい場合

→fbxの方をhierarchyに入れていないか?入れるのはprefabかunitypackage(青いほう)。

f:id:It__was__rainy:20210203141144j:plain
f:id:It__was__rainy:20210203140936j:plain


blenderとかで改変してfbxファイルを持ってきている場合

→add componentからanimator/VRCavaterdiscriptor入れて設定する。元アバターからコピペするのが楽だけどたまにエラー吐かれる。理由はわからない。(私は解決できたことがない)

f:id:It__was__rainy:20210203135046j:plain
f:id:It__was__rainy:20210203134846j:plain

 

f:id:It__was__rainy:20210203141307j:plain


This avater uses Visemes but face Mesh is not specified.

→VRCavaterdiscriptorの中にあるfacemeshがNoneになってるかも。元アバターからコピペ(ry

f:id:It__was__rainy:20210203140347j:plain
f:id:It__was__rainy:20210203140419j:plain



Filenotfoundexception〜

f:id:It__was__rainy:20210203141532j:plain

→missingになってるscriptがどこかのinspectorにあるかも。それを取り除く。よくある(※私調べ)のは2.0→3.0にした後のVRC_avaterdescriptor(2.0)の残骸とかダイナミックボーンとか。
そんなものがなくてもこのエラー吐かれることがある。私の場合は時間空けて再チャレンジしたら何故か成功した。何が原因だったのかさっぱりわからない。

f:id:It__was__rainy:20210203140507j:plain

 

Cannot restructure Prefab instance

→遭遇するのは①ダイナミックボーンが消せないぞ!って時

または②オブジェクトを追従させたいのにbone(amature)の子に入ってくれないぞ!って時

アバター本体ならこのままopen prefabしてダイナミックボーンを消せばいいんじゃないかな?(たぶん。私はやったことない)

f:id:It__was__rainy:20210203191448p:plain

アクセサリーとかならそれ自体をunpack prefabしちゃえば消せる。

f:id:It__was__rainy:20210203191509p:plain

②これややこしいんだけど、dropする場所のせいみたい。

f:id:It__was__rainy:20210203192154p:plainf:id:It__was__rainy:20210203192204p:plain

左が正解。右がだめ。

この、boneとboneの隙間じゃなくて、親boneの名前に重ねるように入れなきゃだめみたい。理由はわからない。

 


Humanoid avater must have head, hands and feet bones mapped.

Your avater is humanoid, bou its feet/upper arms aren't specified!

This avater is not imported as a humanoid rig and will not play VRChat's provided animation set.

→boneに不備があるというメッセージ。市販アバターのfbxファイルはbone(amarture)は設定してあるからよほどいじるか自作じゃない限り問題ない。問題あるならimportかなんかから間違ってる可能性がある。unityは割とポンコツなのでたまに問題なくてもこのエラー吐く。時間空けるなり再起動なりすると成功するかも。しないならわからない。

f:id:It__was__rainy:20210203140559j:plainf:id:It__was__rainy:20210203190136j:plain


Invalid AABB ~

→詳細不明だがtransformの数値に不備があるときのエラーっぽい?unityの再起動などで直った報告もある。謎エラー。

 

Error saving blueprint.(2/4追記)

アバターに割り当てられているIDの不備。私は削除したアバターのIDをそのまま使おうとしたときに遭遇。「Pipeline Manager(script)」から「Detach(optional)」(IDを空にして新しいアバターIDを割り当てる)で解決。

f:id:It__was__rainy:20210204143206p:plainf:id:It__was__rainy:20210204143232j:plain

 

Asset bundle upload failed(2/8追記)

→upload時に見た。なんのエラーかわかりません!でもほぼほぼunityの機嫌が悪いだけなのでやり直すなり再起動するなりでたいてい解決する。(2/24追記修正)

f:id:It__was__rainy:20210208070855p:plain


 

【エラーじゃないけど思ったのと違うやつとか】 

 

Q.Quest対応の為にシェーダー変えたら裏側透明になった(2/24追記)

unityでは多分どうにもならんのでblenderで頑張るしかないんじゃないか。メッシュかテクスチャを修正するんだと思うけどわからない。私は諦めて透明のまま。

簡易的かつ原始的ですが一応解決できました。詳しくは以下(手前味噌)

【blender】VRCアバターの裏面透明問題を解決したい - It__was__rainy’s blog (hatenablog.com)

 

Q.パーティクルをWorld設定にしたい

→rigidbodyなりIkなんちゃらを使う方法が多いと思うけどQuestでは使えない。PCのみと割り切るかlocalで妥協。

 

Q.パーティクル出ない

→「Play on awake」にチェック入れてるか?入れてて出ないならわからない。

f:id:It__was__rainy:20210203194430j:plain

 

 Q.シェーダーを変更できない

f:id:It__was__rainy:20210203141654j:plain

→fbxファイルの方をワンクリックしてmaterialのタブのlocationをuse external materials(Legacy)にするといじれるようになる。

f:id:It__was__rainy:20210203141735j:plain

 

Q.オブジェクトがピンクor黒

→シェーダーをいろいろ変えてみる。黒いほうは直す方法もあるので詳しくは調べてくれ。

 

Q.リアルで気を付けの姿勢なのにアバターの手が変な位置にある

→view pointのZ軸を下げるとマシになる。詳しいことは他に解説してくれてる人がいるので探してくれ。

f:id:It__was__rainy:20210203185543j:plain

 

Q.エラー出てないのにbuildできない or upload失敗する

→とりあえずよくあるらしいのがSDKコントロールパネルの「setting」の「publish」のとこにチェック入ってるパターン。他はよくわからない。

f:id:It__was__rainy:20210203195048j:plain

 

Q.アバターいじってたら表情がお亡くなりに(2/24追記)

→animator/VRC_avaterdiscripterを元アバターからコピペしてる場合、元アバターはscene上に残したまま非表示にする。また、オブジェクトの階層や名前を変えるとanimatorwindowを開いたときにメッシュを参照できなくなるので注意。あと同一scene上で複製したアバター扱ってるときはメッシュの参照とかちゃんとしてるか確認する。(参照元が、非表示にした複製元のメッシュになっていることがままある。特にリップシンク

↓body→bodeyminiに名前を変更したため参照元のメッシュが全部missingになっている

f:id:It__was__rainy:20210208070655j:plain

 

おまけ~blender編~

頂点いじったらリップシンクがいらんとこにくっついてきた

→動かしたくない頂点を選択、「頂点」から「シェイプに反映」

f:id:It__was__rainy:20210203142147p:plain

 

編集モードとオブジェクトモードで見えてる世界が違う

→「シェイプキー」で「Basis」を選んでるかどうか

f:id:It__was__rainy:20210203142445j:plain