Udonの変数にキーフレームが打てない
#記事
#おすすめ記事
#VRC演出ワールド制作固有のTips
Udonの変数にはキーフレームが打てません。
https://scrapbox.io/files/6951110c50e5bef7715b3f3f.png
しかしタイムラインのアニメーションカーブからUdonに数値を送りたいことは非常によくあります。
私が知っている対処は以下の2つです。
空のゲームオブジェクトのTransform等にキーフレームを打ち、その値をUpdate()やLateUpdate()で取得して使う方法
変数が少ないケースではすぐに使えて楽かもしれません。
フェイクライトの座標のように、むしろTransformを使いたいものについてはこちらを使っています。
ダミーのシェーダーを作成し、そのマテリアルをダミーのMeshRenderer(MeshFilterは不要)に割り当てキーフレームを打ち、同様にMaterial.GetFloat等で取得してその値を変数のように使う方法
ダミーのシェーダーのPropertyに登録していくことで手軽に変数を増やせるので私は基本こちらを選んでいます。
ShaderのPropertyなのでColorやVectorが使えます。
コード例(抜粋)
ShaderGlobalに書き込んでいますが、そうしたいことが多いというだけです。
見たままですが、GetValidFloatで取得した時点でUdon内で好きに使うことができます。
code:udonsample.cs
public Renderer dummyRenderer;
public Material dummyMaterial;
public MaterialPropertyBlock dummyPropBlock;
public int propCount = 0;
public string[] propnames;
public int[] proptypes;
private int[] propids;
// ダミーマテリアルに存在するプロパティの型と値を取得して、同名のシェーダーグローバル変数に書き込む
// propなんちゃらはEditor側のコードで自動で埋めている
public void UpdatePropArray()
{
for(int i = 0; i < propCount; i++)
{
switch(proptypesi)
{
case 0:
VRCShader.SetGlobalColor(propidsi, GetValidColor(propnamesi));
break;
case 1:
VRCShader.SetGlobalVector(propidsi, GetValidVector(propnamesi));
break;
case 2:
VRCShader.SetGlobalFloat(propidsi, GetValidFloat(propnamesi));
break;
case 3:
VRCShader.SetGlobalFloat(propidsi, GetValidFloat(propnamesi));
break;
case 5:
VRCShader.SetGlobalInteger(propidsi, GetValidInteger(propnamesi));
break;
default:
break;
}
}
}
// MaterialPropertyBlockがあるケースがあるので、それを考慮してGetFloatする
// ColorやVectorも同様
float GetValidFloat(string name)
{
return !dummyPropBlock.HasFloat(name) ? dummyMaterial.GetFloat(name) : dummyPropBlock.GetFloat(name);
}
void Update()
{
if(!isInit) return;
dummyRenderer.GetPropertyBlock(dummyPropBlock);
UpdatePropArray();
}
code:dummy.shader
Shader "Utils/DummyProp"
{
Properties
{
// VRCShaderGlobalに入れる場合は接頭辞「_Udon」が必須
_Udon_DummyProp ("", float) = 1
}
SubShader
{
// シェーダー自体は変数の宣言含め何もいらない
Pass
{
ZWrite Off
ColorMask 0
}
}
}
以下も基本的には同じです。
MeshRendererを持たないため、CRTのマテリアルやGPUPの計算用マテリアルにキーフレームが打てない
取得した値をMaterial.SetFloatやVRCShader.SetGlobalFloat等でマテリアルプロパティかグローバル変数に送ります。
複数枚のマテリアルで共通の値を使いたいことが多いので、私は最初からグローバル変数に送るようにしています。
環境光やFogを制御したい
環境光も近い仕組みで扱うことが可能で、シーンのEnvironment LightingのSourceをGradientに変更し、渡す先をRenderSettings.ambientSkyColor、ambientEquatorColor、ambientGroundColorにすればとりあえず変更できます。
ベイク済みのモデルは環境光の影響を受けなくなるので、シェーダー側を調整してベイク済みでも環境光を受けるようにしたりします。
LightProbeがベイク済みだったり、VRCLVがある場合はそちらが優先されます。
RenderSettings.fogColorやfogDensityに渡せばFogも操作できます。
私はこの辺り一式をまとめ、Editor上の非再生時に動作するほぼ同じ内容のC#スクリプトとセットにしてずっと使っています。
補足
GetFloatやSetFloat、VRCShader.SetGlobalFloat等は大した負荷ではなく、低速なUdon#でそれなりの回数・毎フレーム実行しても大抵は問題になりません。
例えばVRCLVでAuto Update Volumesをオンにしているときはそういう処理が走っています。
スマホゲームのライブ演出制作においてはTimelineのカスタムトラックを作る方向でスクリプトとの連携をしているようです。
具体例
『学マス』ひとつのライブを制作するのに半年以上。アイドルたちの“らしさ”から振り付けを考え、汗の表現は5種類を使い分け。UnityのTimelineで実現する妥協なき精神【CEDEC2024】 | ゲーム・エンタメ最新情報のファミ通.com
これはCEDiLに会員登録(無料)すれば資料全文がダウンロードできるのでオススメです。
UnityのTimelineを活用したライブ制作のこだわり~「学園アイドルマスター」制作事例~
VRChat以外のバーチャルライブ的なものにおいてもその方が妥当と思われます。
VRChatはカスタムトラックが使えずデフォルトのトラック群で対応しなければならない特殊な環境といえます。
カスタムトラックがVRChat上で実行できないというだけなので、そこから中間形式の何かを出力してUdon#やシェーダーで読むことで、制作作業においてカスタムトラックを使うことは可能なようです。
具体例
RESONARK 4 ELVES Pt.1 制作後記|septem47
このアプローチは私はまだやったことがないですが、ぜひ試してみたいと思っています。