始めに
uGUIを使わないUIを作るため、自作でテクスチャをマテリアルに設定するコンポーネントを作ります。
今回は備忘録的な内容なので結構あっさり目にまとめます。
やりたいこと
一番の希望はuGUIの Image コンポーネントのようなものになります。挙動としては次の通り。
- 1つのマテリアルを複数のオブジェクトで共有できる
- マテリアルの削除漏れが発生しない
- Edit Mode でもInspectorのテクスチャの変更が反映される
マテリアルの注意点
上記のやりたいことに関しての簡単な注意点をまとめます。
マテリアルを共有した場合、変更は別オブジェクトでも適用される
マテリアルを共有している場合、一つのオブジェクトでマテリアルに画像を設定しても別オブジェクトにも反映されます。(同じマテリアルなので当たり前っちゃ当たり前)
また、スクリプトで Renderer.sharedMaterial
からマテリアルを変更した場合も同様になります。(sharedMaterial
は共有しているマテリアルを参照するため)
では、同じマテリアルを使って異なるテクスチャを設定する方法で Renderer.material
を使う方法があります。 こちらからは設定されたマテリアルをインスタンス化したものを変更します。
Renderer.material は破棄をする
先程出てきた Renderer.material
は自動的に破棄されるものではないので、Destory
しない場合そのまま残り続けます。
なので、OnDestory
などで生成した material
を破棄する必要があります。こちらの内容については以下の記事がとても参考になります。
Edit Mode でもInspectorの変更を反映する
基本的にMonoBehaviourはPlay Mode時にしか動きませんが、いくつかの方法で Edit Mode からでも動作させられます。
今回調べて見つけた記事でこちらが網羅的に解説されていて参考になりました。
上記の記事から、今回選択した方法は2つ。
OnValidate()
を使う
Unity公式では次のように説明されています。
この関数はスクリプトがロードされた時やインスペクターの値が変更されたときに呼び出されます(この呼出はエディター上のみ)
なので、Inspectorでテクスチャを変更するたびに呼ばれるので、ここで更新処理を書けばマテリアルに変更が適用されそうです。
[ExecuteAlways]
を使う
OnValidate()
だけですと、Material の生成と破棄が行えません。なので、[ExecuteAlways]
を使って Awake()
と OnDestory()
も呼ばれるようにします。
Inspectorの変更を反映するコンポーネントを作る
以上のことを注意して、Inspectorの変更を反映するコンポーネントを作りました。
このコンポーネントを作るに当たって、こちらの記事のコードを参考にしました。
(こちらの記事ではOnValidate()
を使わずに Update()
を使用、new Material(_renderer.sharedMaterial)
でマテリアルを作れることを知った)
主に EditorApplication.IsPlaying
を使って Edit Mode なのかを判別をしてマテリアル生成方法を分岐しています。
using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif [ExecuteAlways] public class TextureSetting : MonoBehaviour { [SerializeField] private Texture2D _texture; [SerializeField] private Renderer _renderer; private Material _material; private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private void Awake() { #if UNITY_EDITOR _material = EditorApplication.isPlaying ? _renderer.material : new Material(_renderer.sharedMaterial); #else _material = _renderer.material; #endif _material.SetTexture(MainTex, _texture); _renderer.material = _material; } #if UNITY_EDITOR private void OnValidate() { if (EditorApplication.isPlaying || _material == null) // Material生成前に呼ばれることがある { return; } _material.SetTexture(MainTex, _texture); _renderer.material = _material; } #endif private void OnDestroy() { if (_material != null) { #if UNITY_EDITOR if (EditorApplication.isPlaying) { Destroy(_material); } else { DestroyImmediate(_material); } #else Destroy(_material); #endif } } }
こちらの結果は次のようになります。(コンポーネントの名前が異なるのは気にしないでください) Inspectorを変更したら反映されることが確認できました。
まとめ
このコンポーネントによって、オブジェクトごとにマテリアルを作る必要がなくなりました。
また、変更が Edit Mode でも反映されるので、作ろうとしているオブジェクト群やPrefabの構成をすぐに確認できます。
注意として、まだ完全に動作確認ができたわけではないので、もし不具合などがあればコードを修正する予定です。
使用した画像
今回しようした画像はいらすとやのものになります。