初めに
Timelineの処理shaderの値を変更する
「MaterialPropertyBlock is used to modify these values」
と言うメッセージが出る。
これ自体は、下記のブログで説明してあるように
Materialのオブジェクト作らず簡単にスクリプトで変更できるって言うとても便利なものです。
しかし今回は、スクリプトでshaderの値を変更してるのですが、Timelineの処理とMaterialPropertyBlock の処理が挟まると
意図したことが出来ないのでいろいろ調べました。
Timeline単体の機能としては優秀なのだが、今回は邪魔になった
初めにの所で書いたMaterialPropertyBlockで値が変更が出来なくなり、スクリプトで値が変更できなくなります。。
色々調べて、悩んだ末に。。
限定的に変更可能にした!
今回は、Timelineで値を変更したものをいつでも確認できその値をTimelineでキーを打ちたかったので
Timelineがレコーディングモードにした際にMaterialPropertyBlockで値が変更できなくなるのを解除したかったので
スクリプト
スクリプトはMaterialTsetのスクリプトのエディタ拡張で書きました。
MaterialTsetは何も処理をしてなく、TimelinenのPlayableDirectorとスクリプトで変更したかったMaterialを指定するだけ
using UnityEngine;
using UnityEngine.Playables;
public class MaterialTset : MonoBehaviour
{
[SerializeField]
PlayableDirector _playableDirector;
[SerializeField]
private Material _mat;
}
エディタ拡張の部分はこんな感じ
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityEditor.Timeline;
[CustomEditor(typeof(MaterialTset), true)]
public class MaterialTsetEditor : Editor
{
private Vector2 v;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
var mat = serializedObject.FindProperty("_mat").objectReferenceValue as Material;
v = mat.mainTextureOffset;
if (GUILayout.Button("+"))
{
v.x += 0.1f;
}
if (GUILayout.Button("-"))
{
v.x -= 0.1f;
}
mat.mainTextureOffset = v;
TimelineRecordingCheckFunc(serializedObject);
serializedObject.ApplyModifiedProperties();
}
public void TimelineRecordingCheckFunc(SerializedObject serobj)
{
var playableDirector = serobj.FindProperty("_playableDirector").objectReferenceValue as PlayableDirector;
if (playableDirector == null)
{
return;
}
var timelineAsset = playableDirector.playableAsset as TimelineAsset;
foreach (var timelineClip in timelineAsset.GetRootTracks())
{
// タイムラインのそれぞれのタイプを取得
var type = timelineClip.GetType();
// アニメーショントラックか判定
if (typeof(UnityEngine.Timeline.AnimationTrack) == type)
{
// TrackAssetをAnimationTrackで取得
var animationTrack = timelineClip as AnimationTrack;
// playableDirectorから各トラックを取得する
var binding = playableDirector.GetGenericBinding(animationTrack) as Animator;
// ちゃんとオブジェクトがセットされているか
// レコーディング状態をチェック
if (binding && animationTrack.IsRecording())
{
// トラックにセットされいる、オブジェクトの子供に
// MaterialPropertyBlock がセットされいるかチェック
var hasPropertyBlock = false;
var renderers = binding.GetComponentsInChildren<MeshRenderer>();
foreach (var renderer in renderers)
{
if (renderer)
{
if (renderer.HasPropertyBlock())
{
hasPropertyBlock = true;
}
}
}
// チェックの親オブジェクトをアクティブを一瞬とく
if (hasPropertyBlock)
{
// ここが重要
binding.gameObject.SetActive(false);
binding.gameObject.SetActive(true);
}
}
}
}
}
}
大事なところは、
レコーディング中かみてる所
var binding = playableDirector.GetGenericBinding(animationTrack) as Animator;
// ちゃんとオブジェクトがセットされているか
// レコーディング状態をチェック
if (binding && animationTrack.IsRecording())
今回のしたかった事を実際に実現して箇所をとチェックに使った関数
MeshRendererに登録されているMaterialにMaterialPropertyBlockで値が変更されているかチェックするのに
Renderer.HasPropertyBlockを使います
チェックして使用されていたら親のオブジェクトをSetActiveでOn Offするだけ
// チェックの親オブジェクトをアクティブを一瞬とく
if (hasPropertyBlock)
{
// ここが重要
binding.gameObject.SetActive(false);
binding.gameObject.SetActive(true);
}
実現はできたがこれでいいのか。。
たぶん、親のオブジェクトがTimelineでAnimationTrackで登録された際にAnimatorが自動で登録されるのだが、
このAnimatorがMaterialPropertyBlockを設定して言うのかとおもわれそれを
SetActiveでOn Offにすると、MaterialPropertyBlockが外れるのだと思います。
注意
今現在Unityのバージョンの2020 LTSのバージョンは2020.3.25f1ではTimelineのバージョンは1.4.8
これは、レコーディング状態を確認するIsRecording関数1.5.0以降に追加された機能だからです。
なので、Timelineを1.5.0以降にしてください。
最後に
同じように悩むことなどしないと思いますが
もし、同じように悩んでたどり着いた方で、もっといい方法あったらおしえてください。。
いつもならUnity のフォーラムあたりで書いてあったりし自信がもてるのだけど。。
今回ばかりは、少しづつ調べて実現したので自信がない。。