ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

VRChat用にクリックしたら水位が下がる水鉄砲を作る

いきなりまとめ

  • Amplify Shader を使ってローカル座標のある高さ以下のピクセルに水色をブレンドする
  • 高さの閾値をプロパティにしてUdon Sharp からアクセスできるようにしておく
  • スクリプトからマテリアルを取得するにはGetComponent().material
  • マテリアルのプロパティへのアクセスにはmaterial.GetFloat()とmaterial.SetFloat()を使う

まずはモデルを用意

いきなりですが水鉄砲のモデルを用意します。といっても私には無理なので三分クッキング張りに友人に作ってもらいました。

f:id:prupru_prune:20220206211621p:plain
水鉄砲のモデルは友人が作ってくれました
テクスチャも込みで作ってもらったのでとりあえずAlbedo(ベースカラー)もあらかじめ用意されてる体で行きましょう。

必要なコンポーネントを付与

インスペクター上のAdd Componentから以下のコンポーネントを付与しておきます

  • VRC Pickup
  • Rigidbody(VRC Pickup を付与すると自動的に付与される)
  • Mesh Collider (Box Collider 等でも可)
  • Udon Behaviour

また、VRC Pickup のAuto HoldとMesh ColliderのConvexを有効にしておきましょう。

f:id:prupru_prune:20220206224741p:plain
VRC PickupとMesh Colliderの設定
後述しますが、VRC Pickup のExact Gripの設定をしておくとよさげです。

Amplify Shaderでシェーダーを作る

プロジェクトウインドウ->右クリック->Create で新規のMaterialを作成しましょう。同様に、プロジェクトウインドウ->右クリック->Create->Amplify Shader で新規のSurfaceを作成します。Surfaceを作成した直後Amplify Shaderのノードエディタが開くと思うので、次の画像のようにシェーダーを設定します。

f:id:prupru_prune:20220206231254p:plain
Shaderノードの様子. テクスチャは用意してもらいました.
簡単にシェーダーの説明をすると、stepノードを用いてWaterAmmount以下の部分だけ水色をブレンドするという感じです。このWaterAmmountの値ですが、自分で用意したモデルごとにいい感じの値を設定してください。

ノードを選択した状態で左上の灰色の四角ボタンを押すと設定の詳細を開くことが出来るので、赤枠で囲まれたノードの設定をしましょう。左側のFloatノードは水鉄砲の水量を表すFloatノードです。このノードを選択したのちに左上の詳細ボタンを押してTypeをPropertyにしておきます。こうすることでスクリプトからアクセスすることが出来るようになります。また、NameもWaterAmmountとしておきましょう。

f:id:prupru_prune:20220206231747p:plain
スクリプトからいじるプロパティの設定.

次に、右側のOutputノードを選択してRender TypeとRender QueueをどちらもTransparentにします。

f:id:prupru_prune:20220206232454p:plain
Outputノードの設定

ちなみに、stepノードに入力するローカル座標がy座標ではなくz座標にしたのは、モデルの鉛直方向のローカル座標軸がz軸であったためです。

f:id:prupru_prune:20220206233632p:plain
ローカル座標軸の方向が重要

ここまでやればほぼ完成です。このシェーダーを適用したマテリアルを水鉄砲のゲームオブジェクトに付与すると次のように水が入ったような半透明の水鉄砲が出来ているはずです。

f:id:prupru_prune:20220206233124p:plain
シェーダーのおかげで水鉄砲ぽくなった

これだけでもそれっぽいですが、クリックごとに変化を持たせるためにスクリプトの力を借ります。

スクリプト実装する

スクリプトを実装といっても、クリックするごとにWaterAmmountの値を増減させるだけです。マテリアルのプロパティへのアクセス方法が分からず少し時間を溶かしましたが、(いきなりまとめにもあるように)ゲッターとセッターを通して行うようです。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class watergun : UdonSharpBehaviour
{
    private Material mt;
    private float wa;
    private float ini;
    void Start()
    {
        mt = GetComponent<Renderer>().material;
        //アンダーバーを忘れないように
        wa = mt.GetFloat("_WaterAmmount");
        ini  = wa;
    }
    public override void OnPickupUseDown()
    {
        //1/10ずつ水位が下がる
        wa -= ini/10.0f;
        mt.SetFloat("_WaterAmmount",wa);
    }
    public override void OnDrop()
    {
        //オブジェクトを落とすと元の水位に戻る
        mt.SetFloat("_WaterAmmount", ini);
        wa = ini;
    }

}

material.GetFloat()やmaterial.SetFloat()はAmplify Shaderに特有なのかと思ったのですが、ちゃんとUnityの公式スクリプトリファレンスに書かれてました。

Exact Grip の設定

以上でほぼほぼ完成なのですが、実際にVRChatに持ってくると水鉄砲の向きが意図してない方向になります。

f:id:prupru_prune:20220207000948p:plain
Exact Grip を設定しないと向きがあべこべに
なのでVRC Pickup コンポーネントの設定に戻ってExact Grip を登録しましょう。Exact Grip にTransform を指定するとオブジェクトをピックアップした時にそのTransformの場所を手に握るようになります。水鉄砲のゲームオブジェクトの子要素にEmptyを追加して、そのEmptyのTransformをグリップの位置に設定しましょう。
f:id:prupru_prune:20220207001744p:plain
子要素にEmptyを追加
f:id:prupru_prune:20220207001813p:plain
Emptyのtransformをグリップの位置に設定
f:id:prupru_prune:20220207002423p:plain
VRC Pickup でExact Grip と Orientation を設定
これで大丈夫なはず、いざ鎌倉!
f:id:prupru_prune:20220207002804p:plain
下を向く水鉄砲
うーん、水鉄砲が下を向いていますね。ということでEmptyのtransformのRotationをいじり、z軸が正面を向くようにしてやりましょう。
f:id:prupru_prune:20220207003315p:plain
Emptyを親オブジェクトに対して回転させておく
これで今度こそ水鉄砲になりました。 streamable.com