8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

サムザップ #1Advent Calendar 2019

Day 22

uGUIでオーバードローを減らそう

Last updated at Posted at 2019-12-23

本記事は、サムザップ Advent Calendar 2019 #1 の12/22の記事です。

はじめに

ゲームのUIって画像が何枚も重なり合って動作が重くなってしまう現象が発生しがち。
そんなUIのオーバードローを減らしてゲームの動作を軽くしようというお話です。

サンプルUI

こんなUIがあったとしましょう
(無理やり重ねた感)
GameView-s.png

オーバードロー

このUIのオーバードローを見てみると…
Overdraw1-s.png
色が明るくなっているところが何枚も重なり合っているところです。
これはUnityのSceneViewで左上のプルダウンからOverdrawを選択すると表示されます。

描画範囲

斜めになっている四角い画像は色がついている部分だけじゃなく全体が描画されてしまいます。
(透明だから見えないだけ)
Sprite-View-s.png
透明で見えない部分まで描画されてしまうなんてもったいない。

透明部分を描画しない方法

実は簡単にできます。
画像とImageのInspectorを設定するだけ。

画像のInspectorはMeshTypeをTightにするだけ!
Inspector-Sprite.png
ImageのInspectorはUseSpriteMeshにチェックを入れるだけ!
Inspector-Image.png

そうすると描画されていない部分のオーバードローが消えました!
Overdraw2-s.png

効果

問題はこれをやることがどれほど効果があるのかってことですよね。
この程度のオーバードローだとあんまり変わらないのでもっと重ねてみたいと思います。

Use Sprite Mesh チェックなし

Test1.png
適当な三角形の画像を作成して画面内に3000枚表示してみました。
FPSも表示してみましたが約30fpsぐらいでした。

Use Sprite Mesh チェックあり

Test2.png
約50fpsでした。
※検証にはAndroid端末(GalaxyS9)を使用しました。

サンプルコード

Test.cs
public class Test : MonoBehaviour
{
    [SerializeField] RectTransform rectTransform = null;
    [SerializeField] GameObject goImage = null;
    [SerializeField] Text txtFps = null;
    int frames;
    float prevTime;

    // Start is called before the first frame update
    void Start()
    {
        float width = rectTransform.rect.width;
        float height = rectTransform.rect.height;
        for( int i = 0; i < 3000; i++ )
        {
            GameObject go = Instantiate( goImage, rectTransform );
            go.SetActive( true );
            go.transform.localPosition = new Vector2(
                Random.Range( -width * 0.5f, width * 0.5f ),
                Random.Range( -height * 0.5f, height * 0.5f )
            );
        }
        frames = 0;
        prevTime = Time.realtimeSinceStartup;
    }

    // Update is called once per frame
    void Update()
    {
        ++frames;
        float time = Time.realtimeSinceStartup - prevTime;
        if( time >= 0.5f )
        {
            txtFps.text = string.Format( "FPS:{0:f1}", frames / time );
            frames = 0;
            prevTime = Time.realtimeSinceStartup;
        }
    }
}

仕組みの解説

普通に描画すると四角形で描画されてしまうのにどうやって色の付いてる部分だけを描画しているのかを解説します。
といっても解説するほどのことはなく画像をMeshとして切り出しています。
その証拠にGameViewでStatusを見てみると
Test1-Status.png
3000枚の画像をUseSpriteMeshにチェックを入れず描画した方はVertsが12.0k(1万2千頂点)なのに対し
UseSpriteMeshにチェックを入れた方は117.0k(11万7千頂点)となっています。
Test2-Status.png

まとめ

頂点数が増えてもオーバードローが減れば動作は軽くなる!

それでは明日は@kotaroyさんの記事です。
お楽しみに!

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?