UIでSpriteを使う
2D的なUIを組む時に uGUI をお使いの方は多いと思います。uGUI,便利ですよね。
ところが,これらのUnity標準機能を組み合わせて使おうとすると罠にハマる場合があります。uGUI は矩形以外のSpriteに対応していないからです。
「え? UI.Image使えばSprite貼れるじゃないの。」
そうなんですが,この UI.Image,その名前の通り Sprite のテクスチャを参照しているだけです。なので次のような不具合が発生します。
https://gyazo.com/c914c404d136316b539237f2fcf3ccfd
UI.Imageに配置されたSprite
ちゃんと表示されているように見えますが,ここには大きな問題が2つ存在します。
透明部分のオーバードローが発生し描画負荷に無駄が生じている
SpriteAtlas 生成時の Allow Rotation/Tight Packing に対応していない
1は無視できたとしても2が使えないのは致命的です。
実際にPlayModeに入ってみましょう。
https://gyazo.com/93c6778540848291a337d6faa24388bc
SpriteAtlas適用時
ゴミが表示されていたり,上下が反転したり,散々です。
それもそのはず,実際のアトラスがどうなっているかというと…
https://gyazo.com/aa16e5988b1d5cf714bcd097ad932b2e
atlas1
これでは矩形にしか対応できない UI.Image ではどうしようもありません。
SpriteAtlas の設定で,Allow Rotation/Tight Packing を禁止すれば大丈夫ですが,それでは SpriteAtlas を使う利点が大幅に損なわれてしまいます。
どうすればこの問題を解決できるでしょうか。
これらの問題の原因は,UI.Image が Sprite の Mesh を使わないからです。
つまり,「使ってやればよい」のです。
UI.Canvas に描画するには,UI.Graphicを継承したコンポーネントを作成する必要があります。今回は,Sprite の Mesh を使う UI.Graphic コンポーネントを作ってみます。
code:CanvasSprite.cs
using System;
using UnityEngine;
using UnityEngine.UI;
public class CanvasSprite : Graphic
{
private Sprite m_sprite;
public Sprite sprite
{
get { return m_sprite; }
set
{
m_sprite = value;
SetMaterialDirty();
gameObject.SetActive(m_sprite != null);
}
}
public override Texture mainTexture
{
get { return sprite ? sprite.texture : null; }
}
protected override void OnPopulateMesh(VertexHelper vh)
{
if (sprite)
{
Vector2 vertScale;
vertScale.x = rectTransform.rect.width / sprite.rect.width * sprite.pixelsPerUnit;
vertScale.y = rectTransform.rect.height/ sprite.rect.height* sprite.pixelsPerUnit;
vh.Clear();
var verts = sprite.vertices;
var uvs = sprite.uv;
for (int i = 0; i < verts.Length; i++)
{
vh.AddVert(Vector2.Scale(vertsi, vertScale), color, uvsi); }
var triangles = sprite.triangles;
for (int i = 0; i < triangles.Length / 3; i++)
{
vh.AddTriangle(
}
}
}
}
やっていることは単純で,Sprite の Mesh を流し込んでテクスチャを設定しているだけです。
細かいポイントとしては,rectTransform に合わせて頂点座標をスケールしている点と,実行時の動的なSpriteの切り替えに対応するために UI.Graphic.SetMaterialDirty() を呼んでいる点でしょうか。この辺りが適当だと運用で苦労するのでご注意ください。
使い方は,UI.Image をこの CanvasSprite に入れ替えれるだけです。
描画結果を見てみましょう。
https://gyazo.com/075155ea0ddc0fad875dc750a2bc8cc7
CanvasSpriteによる描画
はい,正しく描画されていますね。
将来的には UI.Image が矩形以外の Sprite をサポートするかもしれませんが,それまではこのような工夫が必要です。ご活用ください。
Unity 2018.3以降について
Unity2018.3以降のUI.Imageには,Spriteのメッシュを使用するモードが追加されており,上記スクリプトは不要となりました。
Chaola.icon