目的
Unity の一般的なアセットやスクリプトは、Project Settings にある Preset Manager で、
各パラメーターのデフォルト値を設定できます。
アセットの場合、更に配置場所によって違うデフォ値を適用できます。
しかし、アニメーションステートや、アニメーション遷移など、一部のアセットではデフォルト値を設定できません。
ステートの Write Defaults を常にオフにしたい場合などでは、毎回わざわざオフにする手間が増えて、とても困ります。
この記事では、この問題を解決するための簡単な手法を紹介します。
手法概要
Unity の コンポーネント・アセットの新規作成イベント にフックして、
コールバックで新規コンポーネント・アセットの値を調整することで、擬似的にデフォルト値を設定できます。
手法詳細
Unity 2020 から、ObjectChangeEvents
という新しいクラスが追加されました。
ObjectChangeEvents
はアセットの作成・削除や、シーン上に GameObject・コンポーネントの作成、親子関係の変更など、
多岐に渡る処理のイベントコールバックを提供しています。
今回は、ObjectChangeEvents
の「新規コンポーネント・アセットのイベントコールバック」を利用して、
作成された直後に値をすぐ設定することで、擬似的にデフォルト値を変えます。
イベント種類(ObjectChangeKind
)については、
- 新規アセット:
CreateAssetObject
- 新規 GameObject:
CreateGameObjectHierarchy
- 新規コンポーネント:
ChangeGameObjectStructure
このような感じです。
この手法のメリットとしては、イベントは自動的に発火されますので、一度処理を書いてしまえば、
Inspector 上では特に何もしなくてもデフォ値が自動で変わります ので、プリセットを適用する手間もありません。
例:アニメステートの Write Defaults をデフォルトオフにする
例として、先程挙げた「アニメーションステートの Write Defaults をデフォルトオフにする」のやり方を紹介します。
今回は下記のようなステップのコード書きます:
-
ObjectChangeEvents
にコールバックをフックする - コールバック内で、「新規アニメーションステート作成」のイベントかをチェックする
- 新規アニメーションステートの Write Defaults を
false
にする
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
/// <summary>
/// アニメーションステートのデフォルト値を設定するクラス
/// </summary>
public class AnimatorStateDefaultParamSetter
{
/// <summary>
/// エディターロード時に、イベントにフックする
/// </summary>
[InitializeOnLoadMethod]
private static void HookToObjectChangeEventStream()
{
ObjectChangeEvents.changesPublished += CheckNewAnimatorState;
}
/// <summary>
/// 新規アニメーションステートのチェック
/// </summary>
private static void CheckNewAnimatorState(ref ObjectChangeEventStream stream)
{
for (var i = 0; i < stream.length; i++)
{
// 新規アセット作成イベントのみを処理する
if (stream.GetEventType(i) is not ObjectChangeKind.CreateAssetObject) continue;
// アニメーターステートのみを処理する
stream.GetCreateAssetObjectEvent(i, out var createAssetEventData);
if (Resources.InstanceIDToObject(createAssetEventData.instanceId) is not AnimatorState state) continue;
// Write Default をオフにする
state.writeDefaultValues = false;
}
}
}
結果:
新規作成されたアニメーションステートの Write Defaults が無事オフになっています。
応用
上のスクリプト例ではアセットの値をコード上で直接設定していますので、デフォ値の変更は再コンパイルを伴います。
より柔軟で便利に設定したい場合は、シングルトンの ScriptableObject
にして、インスペクター上で任意に Preset を設定できるようにすると良いでしょう。
ただし、[ExcludeFromPreset]
アトリビュートのつけたものはプリセットが作成できないので、その時はコード上で値を直接設定するしかありません。
// 重複コードを一部省略...
[SerializeField] private Preset[] presets;
private void ObjectChangeEventStreamCallback(ref ObjectChangeEventStream stream)
{
for (var i = 0; i < stream.length; i++)
{
// イベント種類のチェック...
var targetObj = Resources.InstanceIDToObject(createAssetEventData.instanceId);
if (targetObj) {
// Preset が適用できるなら、適用する
foreach (var preset in presets) {
if (preset.CanBeAppliedTo(targetObj)) preset.ApplyTo(targetObj);
}
}
}
}
結
今回は ObjectChangeEvents
を利用して、アニメステート・遷移のデフォ値を変える方法を紹介しました。
この方法では、デフォルト値が設定できないアセットでも、新規作成時に値を代入することで、擬似的にデフォルト値にしています。
この方法応用すれば、より強力なツールになれると思います。