UnityのuGUIはImageやRawImage等単一スプライトの表示機能はちゃんと用意されていますが、これは1ゲームオブジェクトに付き1スプライトしか表示できません。
例えばマップチップを使いたいとき…なんかにはちょっと無駄感があります。
というわけで、uGUIを使って、好きなメッシュを表示する仕組みを調べました。
※Unity5.2からOnFillVBOは非推奨になりました。(というか表示されなかった…)
→ 以下の記事を参照してください。
Graphic
uGUIではGraphicクラスを継承しているものがCanvasRendererの描画対象になります。
このGraphicクラスを自分で継承してカスタマイズすることで好きなものを描画することが出来ます。
このクラスで特に重要なものがOnFillVBOとmainTextureです。
まず継承する
まずはGraphicを継承したクラスを用意します。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { }
ついでに、GraphicはCanvasRendererとRectTransformが必須なので属性をつけて要求しておきます。
これだけで真っ白の四角が表示されます。
OnFillVBO
このOnFillVBOをオーバーライドして独自のメッシュを生成します。
このメソッドは定義がvoid OnFillVBO( List<UIVertex> vbo )となっています。
引数のvboに頂点情報をAddメソッドで追加する事でそれがポリゴンとして描画されます。
以下具体例として試しに台形を描画します。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// <summary> /// 独自のUI描画 /// </summary> [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { /// <summary> /// ここでメッシュを生成する /// </summary> /// <param name="vbo">Vbo.</param> protected override void OnFillVBO( List<UIVertex> vbo ) { UIVertex v = UIVertex.simpleVert; // 左下 v.position = new Vector3( -100, 0, 0 ); v.color = Color.white; vbo.Add( v ); // 右下 v.position = new Vector3( 100, 0, 0 ); v.color = Color.yellow; vbo.Add( v ); // 右上 v.position = new Vector3( 50, 100, 0 ); v.color = Color.red; vbo.Add( v ); // 左上 v.position = new Vector3( -50, 100, 0 ); v.color = Color.green; vbo.Add( v ); } }
vbo.Addで頂点を追加します。
4頂点で1つの四角が描画されます。
わかりやすくする為すべての頂点で色を変更してます。
次に台形を2つに増殖させてみます。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// <summary> /// 独自のUI描画 /// </summary> [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { /// <summary> /// ここでメッシュを生成する /// </summary> /// <param name="vbo">Vbo.</param> protected override void OnFillVBO( List<UIVertex> vbo ) { UIVertex v = UIVertex.simpleVert; // 左下 v.position = new Vector3( -100, 0, 0 ); v.color = Color.white; vbo.Add( v ); // 右下 v.position = new Vector3( 100, 0, 0 ); v.color = Color.yellow; vbo.Add( v ); // 右上 v.position = new Vector3( 50, 100, 0 ); v.color = Color.red; vbo.Add( v ); // 左上 v.position = new Vector3( -50, 100, 0 ); v.color = Color.green; vbo.Add( v ); // 2つめの四角を300下に // 左下 v.position = new Vector3( -100, -300, 0 ); v.color = Color.white; vbo.Add( v ); // 右下 v.position = new Vector3( 100, -300, 0 ); v.color = Color.yellow; vbo.Add( v ); // 右上 v.position = new Vector3( 50, -200, 0 ); v.color = Color.red; vbo.Add( v ); // 左上 v.position = new Vector3( -50, -200, 0 ); v.color = Color.green; vbo.Add( v ); } }
4頂点毎に四角が1つ作られるという仕様のようです。
ちなみに3頂点だけにしたらUnityが死にました…
mainTextureの指定
テクスチャの設定方法ですが、これはmainTextureプロパティをオーバーライドして、設定したいテクスチャを返すようにします。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// <summary> /// 独自のUI描画 /// </summary> [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { /// <summary> /// メッシュに設定するテクスチャの指定 /// </summary> public override Texture mainTexture { get { // ここで設定したいテクスチャを返すようにする return base.mainTexture; } } }
Inspector上でテクスチャを設定して、このgetプロパティで返すようにします。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// <summary> /// 独自のUI描画 /// </summary> [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { /// <summary> /// メッシュに設定したいテクスチャをここで指定 /// </summary> [SerializeField] private Texture texture_; /// <summary> /// メッシュに設定するテクスチャの指定 /// </summary> public override Texture mainTexture { get { // ここで設定したいテクスチャを返すようにする return texture_; } } }
次にUVの指定を行います。
ついでに頂点カラーの指定があると、少し見づらいので白に変更してます。
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// <summary> /// 独自のUI描画 /// </summary> [RequireComponent( typeof( CanvasRenderer ) )] [RequireComponent( typeof( RectTransform ) )] public class MyGraphic : Graphic { /// <summary> /// メッシュに設定したいテクスチャをここで指定 /// </summary> [SerializeField] private Texture texture_; /// <summary> /// メッシュに設定するテクスチャの指定 /// </summary> public override Texture mainTexture { get { // ここで設定したいテクスチャを返すようにする return texture_; } } /// <summary> /// ここでメッシュを生成する /// </summary> /// <param name="vbo">Vbo.</param> protected override void OnFillVBO( List<UIVertex> vbo ) { UIVertex v = UIVertex.simpleVert; // 台形 // 左下 v.position = new Vector3( -100, 0, 0 ); v.uv0 = new Vector2( 0, 0 ); vbo.Add( v ); // 右下 v.position = new Vector3( 100, 0, 0 ); v.uv0 = new Vector2( 1, 0 ); vbo.Add( v ); // 右上 v.position = new Vector3( 50, 100, 0 ); v.uv0 = new Vector2( 1, 1 ); vbo.Add( v ); // 左上 v.position = new Vector3( -50, 100, 0 ); v.uv0 = new Vector2( 0, 1 ); vbo.Add( v ); // 正方形 // 左下 v.position = new Vector3( -50, -200, 0 ); v.uv0 = new Vector2( 0, 0 ); vbo.Add( v ); // 右下 v.position = new Vector3( 50, -200, 0 ); v.uv0 = new Vector2( 1, 0 ); vbo.Add( v ); // 右上 v.position = new Vector3( 50, -100, 0 ); v.uv0 = new Vector2( 1, 1 ); vbo.Add( v ); // 左上 v.position = new Vector3( -50, -100, 0 ); v.uv0 = new Vector2( 0, 1 ); vbo.Add( v ); } }
台形だとテクスチャがゆがんでわかりにくいので下は正方形にしてます。
注意点1
マテリアルも指定できますが_MainTexプロパティは上記のmainTextureの返り値で上書きされてしまいます。
そのためマテリアル側のテクスチャの反映がされない…!?という時は_MainTexにテクスチャを指定していないか確認してみてください。
注意点2
OnFillVBOは更新が必要なタイミングでしか呼ばれません。
具体的にはGraphicのSet**Dirtyメソッドが呼ばれたり、Canvasの設定が何か変更されたり…といったタイミングです。
なので頂点のアニメーションをこのメソッドに入れたりしているとうまく動かないです。
注意点3
マテリアルは1つしか設定できません。
MeshRendererだとサブメッシュを利用する事で複数のマテリアルが利用できましたが、こっちはそういうのはありません。
そういう場合はゲームオブジェクトを別に分ける他ありません。
感想
基本はこんな感じです。
これでマップチップ実装したくなった時も無駄に大量のゲームオブジェクトを量産しなくてよくなります。
NGUIがベースになってるだけあって、 この辺りは良く似ているなーと思いました。