2013年11月9日土曜日

【Unity,NGUI3.x】UIPanelとDrawCall

NGUI使っててDrawCallが減らせなくて困った。
原因は単純だったけど解決したのでメモ。

環境

・Mac
・Unity 4.2.2.f1
・NGUI 3.0.3g

つまづいたこと

NGUIでUIを大量に配置してたらDrawCallが30超えた。
同じUIAtlas使ってるしDepthも揃えたのになんでだろなーって思ってた。

原因

同じUIAtlasを使っていてもUIPanelを複数配置するとDrawCallが別れるんですね。
以前どこかで「まとめて動かすようなものはPanelに入れる」って読んだ気がしてUIPanel量産してましたw

ダメなパターン

Root(UIRoot)
┗Camera(Camera,UICamera)
 ┗Anchor(UIAnchor)
  ┗Panel(UIPanel)
   ┣Panel1(UIPanel)  ←まとめて動かす用
   ┃ ┗Sprite(UISprite)
   ┗Panel2(UIPanel)  ←まとめて動かす用
     ┗Sprite(UISprite)

2つのSpriteはどちらも同じAtlasを参照してますけどDrawCallは2ですね。

Panel1のInspector見てみてもDrawCallが別れてるのが分かります。

対応

じゃあどうするか?UIPanelを1つだけにすればいいんです。
Panel1,Panel2からUIPanelコンポーネントをむしり取りましょう。

大丈夫なパターン

Root(UIRoot)
┗Camera(Camera,UICamera)
 ┗Anchor(UIAnchor)
  ┗Panel(UIPanel)
   ┣Panel1(空のGameObject)  ←まとめて動かす用
   ┃ ┗Sprite(UISprite)
   ┗Panel2(空のGameObject)  ←まとめて動かす用
     ┗Sprite(UISprite)

これだけでDrawCallは1つになりました。
DrawCallは親Panelに集約されてますね。


Panel1,2は空のGameObjectになってしまったけど何の問題もなさそうです。

まとめ

DrawCallをとにかく減らしたいなら1つのUIPanelに配置する。
でも同時に表示しないUI(ゲーム画面用、ポーズ画面用など)はこだわる必要ないので別々のUIPanelにした方が楽に扱えそうですね。

疑問

DrawCallが増えてでもUIPanelを別けたい場合はあるだろうか?
パーティクル表示を(Atlas、Panelセットで)分離したいときとか?

参考

 ここでは別Panelでも1Callだったって書いてあるけど仕様変わったのかな。
 最初読んだとき良くわからなかったけど今じっくり読んでやっと分かった。やったこと同じだね。


楽しいUnityライフを!

2013年11月7日木曜日

【Unity,C#】RGBではなくHSVによる色指定

今日はこんなの作ってみた話。

ことの発端はNGUIでボタンを作ってて、
統一感のあるカラフルなボタンを作りたくなったこと。
Unityのエディタ上ならなら色選択ダイアログのスライダー動かすだけなんだけど、
スクリプトでやろうとしたらできなくて困った。
これ
ColorクラスはRGBでしか指定できない。
RGBをどう変えたら同じ雰囲気の別の色に変えられるか分からなかった。

調べてみたらHSVっていう方法で色を指定できればいいみたい。

HSV色空間


HSV色空間

RGBは名前の通り赤緑青の比率だけど、
HSVだと以下の3つで色を決定するらしい。
・Hue(色相)
  赤、青、黄色と言った色
・Saturation(彩度)
  色の鮮やかさ
・Value(明度)
  色の明るさ

なのでSとVを固定してHだけを変更すればやりたいことが実現できそう。

UnityEngine.Colorクラス

http://docs.unity3d.com/Documentation/ScriptReference/Color.html
RGBしか扱えないみたい。
当然コンストラクタもRGBしか用意してない。

HSVとRGBを変換できるものが必要。

調べた

EditorGUIUtility.HSVToRGB
  標準でも変換クラスはあるみたい。
HSVの値からRGBを返すユーティリティスクリプト
  作ってる人いた。でもちょっと物足りない。

ちょっと欲しい感じの見つからなかったので作ってみた。
ここに変換式も全部載ってたし。

作ったクラス

ColorHSV.cs

using System;
 
namespace UnityEngine
{
 /// <summary>
 /// HSV色空間を扱うクラス
 /// </summary>
 public static class ColorHSV
 {
  
  
  /// <summary>
  /// HSV色空間による指定でUnityEngine.Colorを作成します。
  /// </summary>
  /// <param name="h">色相(Hue) 0-360</param>
  /// <param name="s">彩度(Saturation) 0-255</param>
  /// <param name="v">明度(Value) 0-255</param>
  /// <returns></returns>
  public static Color FromHsv(int h, int s, int v)
  {
   while (h >= 360) h -= 360;
   while (h < 0) h += 360;
   if (s > 255) s = 255;
   if (s < 0) s = 0;
   if (v > 255) v = 255;
   if (v < 0) v = 0;
   
   return FromHsv((float)h, (float)s / 255.0f, (float)v / 255.0f);
  }
 
   
  
  
  /// <summary>
  /// HSV色空間による指定でUnityEngine.Colorを作成します。
  /// </summary>
  /// <param name="h">色相(Hue) 0.0-360.0</param>
  /// <param name="s">彩度(Saturation) 0.0-1.0</param>
  /// <param name="v">明度(Value) 0.0-1.0</param>
  /// <returns></returns>
  private static Color FromHsv(float h, float s, float v)
  {
   if (h > 360.0) throw new ArgumentOutOfRangeException("h", h, "0~360で指定してください。");
   if (h < 0.0) throw new ArgumentOutOfRangeException("h", h, "0~360で指定してください。");
   if (s > 1.0) throw new ArgumentOutOfRangeException("s", s, "0.0~1.0で指定してください。");
   if (s < 0.0) throw new ArgumentOutOfRangeException("s", s, "0.0~1.0で指定してください。");
   if (v > 1.0) throw new ArgumentOutOfRangeException("v", v, "0.0~1.0で指定してください。");
   if (v < 0.0) throw new ArgumentOutOfRangeException("v", v, "0.0~1.0で指定してください。");
   
   Color resColor = Color.clear;
   
   if (s == 0.0) //Gray
   {
    int rgb = Convert.ToInt16((float)(v * 255));
    resColor = new Color(rgb, rgb, rgb);
   }
   else
   {
    int Hi = (int)(Mathf.Floor(h / 60.0f) % 6.0f);
    float f = (h / 60.0f) - Hi;
    
    float p = v * (1 - s);
    float q = v * (1 - f * s);
    float t = v * (1 - (1 - f) * s);
    
    float r = 0.0f;
    float g = 0.0f;
    float b = 0.0f;
    
    switch (Hi)
    {
     case 0: r = v; g = t; b = p; break;
     case 1: r = q; g = v; b = p; break;
     case 2: r = p; g = v; b = t; break;
     case 3: r = p; g = q; b = v; break;    
     case 4: r = t; g = p; b = v; break;
     case 5: r = v; g = p; b = q; break;   
     default: break;
    }
    
    resColor = new Color(r,g,b);
   }
   
   return resColor;
  } 
 }
  
   
  
  
 /// <summary>
 /// UnityEngine.ColorのHSV色空間への拡張
 /// </summary>
 public static class ColorExtension
 {
  /// <summary>
  /// 色相(Hue)
  /// 0-360
  /// </summary>
  public static int h(this Color c)
  {
   float min = Mathf.Min(new float[]{c.r, c.g, c.b});
   float max = Mathf.Max(new float[]{c.r, c.g, c.b});
   
   if (max == 0) return 0;
   
   float h = 0;
   
   if (max == c.r) h = 60 * (c.g - c.b) / (max - min) + 0;
   else if (max == c.g) h = 60 * (c.b - c.r) / (max - min) + 120;
   else if (max == c.b) h = 60 * (c.r - c.g) / (max - min) + 240;
   
   if (h < 0) h += 360;
   
   return (int)Mathf.Round(h);
  }
  
   
  
  
  /// <summary>
  /// 彩度(Saturation)
  /// 0-255
  /// </summary>
  public static int s(this Color c)
  {
   float min = Mathf.Min(new float[]{c.r, c.g, c.b});
   float max = Mathf.Max(new float[]{c.r, c.g, c.b});
   
   if (max == 0) return 0;
   return (int)(255 * (max - min) / max);
  }
  
  
  /// <summary>
  /// 明度(Value)
  /// 0-255
  /// </summary>
  public static int v(this Color c)
  {
   return (int)(255.0f * Mathf.Max(new float[]{c.r, c.g, c.b}));
  }
  
   
  
  
  /// <summary>
  /// 現在の色を基準にHSV色空間を移動します。
  /// </summary>
  /// <param name="c"></param>
  /// <param name="offsetH">色相(Hue)オフセット値</param>
  /// <param name="offsetS">彩度(Saturation)オフセット値</param>
  /// <param name="offsetV">明度(Value)オフセット値</param>
  /// <returns></returns>
  public static Color Offset(this Color c, int offsetH,int offsetS,int offsetV)
  {
   int newH = (int)(c.h() + offsetH);
   int newS = (int)(c.s() + offsetS);
   int newV = (int)(c.v() + offsetV);

   return ColorHSV.FromHsv(newH,newS,newV);
  }
  
  
  /// <summary>
  /// 現在の色を文字列として返します。
  /// </summary>
  /// <returns></returns>
  public static string ToString2(this Color c)
  {
   return string.Format("R={0}, G={1}, B={2}, H={3}, S={4}, V={5}", 
   new object[]{
   c.r
   ,c.g
   ,c.b
   ,c.h()
   ,c.s()
   ,c.v()});
  }
 }
}

簡単な説明

1ファイル追加するだけで使えるように気をつけた。

ColorHSVクラス

HSV指定でUnityEngine.Colorのインスタンスを作るクラス
Color color = ColorHSV.FromHsv(0,100,255);

 H:0~360で指定
 S:0~255で指定
 V:0~255で指定
 S,Vは0~255ではなく0~1指定が一般的なのかな?
 RGBが0~255だったりするからそうしちゃった。
 色詳しくないし標準は知らない。

ColorExtensionクラス

UnityEngine.Colorクラスへの拡張メソッド群。

 Colorインスタンスから直接H、S、V取得可能に。
Color color = Color.red;
int h = color.h();
int s = color.s();
int v = color.v();

 ColorインスタンスからH、S、V指定でオフセット可能に。
Color color = ColorHSV(0,100,255);
Color color2 = color.Offset(30,0,0);
これ結構気に入ってる。

遊んでみた

NGUIのUISpriteを360枚配置、Hを1度ずつ変えてみた。
きもちわるっ!
まぁでも機能的には実現できたし満足。

Unity系記事まとめ

2013年11月3日日曜日

【Unity,NGUI3.x】Gridを使って等間隔に配置

ステージセレクトとかで1画面に25個とかボタンが並んでることありますよね。
どーやるのか調べてみたらNGUIに便利なクラスがありました。

Gridっていうものを使うらしいです。

試してみたので残しておきます。

作ったサンプル

適当なSpriteを等間隔に配置してみた。

環境

・Mac
・Unity 4.2.2.f1
・NGUI 3.0.3g

作り方

プロジェクトを用意

・NGUI3.0をインポート
  Examplesは使わないので削除
・以下の素材をインポート
  http://www.tasharen.com/ngui/tutorial3assets.unitypackage
・ProjectビューのMain Cameraも使わないので削除

パネルを用意

・NGUI->Create->Panel
 設定は規定のまま。

Spriteを追加

・NGUI->Create->Sprite
適当に画像を選んでサイズを調整。
これを横5列、縦5列に並べてみようと思います。

Gridを配置

・Game Object->Create Empty
・Panel直下に移動
・Transform.Scaleを1,1,1に変更←忘れがち!
 X,Y,Zの左にある[S]を押すだけで1,1,1になります。
・名前をGridに変更
・Gridを選んだ状態で、Component->NGUI->Interaction->Grid
これでUIGridコンポーネントを持ったオブジェクトができました。
こいつが綺麗に並べてくれるらしい。

並べるSpriteの準備

SpriteをGrid直下に移動し、25個コピーします。
当然まだ重なってますね。

Gridの設定

・Arrangement = Horizontal(水平)
・Max Per Line = 5(横5列)
・Cell Width = 65
・Cell Height = 65
ここでReposition Nowを押すと…
Gridの座標から右へ5個、改行されながら計25個。
綺麗に並びました。あー楽ちん。

まとめ

・等間隔に配置するにはUIGridコンポーネントを使う。
・直下に色々置いてRepositionNowするだけ。

Gridの座標から右(下)に向かって伸びてくみたい。
並んだ数の中心にPivotが欲しいけどそこは仕方ないのかな。
あと空のGameObjectを追加するところ、一発でPanel直下に生成できないのかな?
ショートカットとかありそうなものだけど。

Unity系記事まとめ
Related Posts Plugin for WordPress, Blogger...