Unityな日々(Unity Geek)

Unityで可視化アプリを開発するための試行錯誤の覚書

C#からJavascriptのクラスを呼びだせない時

C#からJavascriptのクラスは、通常のC#クラスと同じように呼び出せる。たとえば、

[SerializeField]protected FPSInputControllerREI fpsInputController; //Javascript Class

ところが、これでは「該当クラスがない」というエラーが出る。

Assets/Scripts/REIControllerDangeon.cs(15,43): error CS0246: The type or namespace name `FPSInputControllerREI' could not be found. Are you missing a using directive or an assembly reference?

この原因は、アセットの読み込み順番で、C#スクリプトと実行する際にJavascriptが読み込まれていないことにある。

Javascriptのクラスを先に読み込ませるために、読み込ませたいJSファイルは、

  • Plugins
  • StandardAssets

などのフォルダにいれる。これでエラーはなくなるはず。

角度指定と外部呼出しに関するベンチマーク

Unityでは、角度は、オイラー角とクォータニオンの2つの方法で扱える。また、回転を設定する際、角度の絶対値と、Rotate()で相対角変化を与える方法がある。いったいどれがもっとも効率的、すなわち高速なのだろうか?

また、あるオブジェクトのクラスから他のオブジェクトを操作する際、操作する側のクラスに操作される側のGameObjectなりTransformコンポーネントを保持して操作を加えるのと、操作される側にクラスを作ってそのPublicメソッドを呼び出すのと、どちらが速いのだろう。

そういう疑問がおきたので、ベンチマークしてみた。

比較結果

ベンチマークスクリプトは下に記す。各方法とも10万回ループの経過時間。

No. 処理内容 時間(mSec)
1-1 オイラー角で設定する 130
1-2 Rotate()関数を使って相対的にまわす 146
1-3 クォータニオンで設定する 123
1-11 ターゲット側のPublic関数に角度をを渡す(外部関数の中身は1-1と同等) 129
1-12 ターゲット側のPublic関数に角度ベクトルをを渡す(外部関数の中身は1-1と同等) 130
1-1A 1-1と同じ処理で、毎回transformコンポーネントを探す 139
1-12A 1-12と同じ処理でループの中で毎回同階層にある外部クラス(コンポーネント)を接続する 191
1-12B 1-12と同じ処理でループの中で毎回外部GameObjectを探す 241
結論
  • 角度の指定はEulerでもQuaternionでも、違いはそんなにない。Quaternionのほうが若干(5%程度) 速い程度。
  • 回転の際、角度を直接指定するより、Rotate()を使うほうが10%程度時間がかかる。
  • Public関数の呼び出しはコストがかからない。つまり、外部コンポーネントを自クラスに取り込んで自クラス内で操作しても、外部コンポーネントのPublic関数を呼び出してその中で操作しても、時間に差はない。
  • (やはり)外部コンポーネント・外部クラスの呼び出しは時間がかかる。呼び出しは最初にやって内部変数にリンクを格納しておくのがベター
ベンチマークスクリプト
// Bench Mark for Rotation & Function Call
// First Version 2015/03/14 by A.Y.@XOOMS
// Last Update   2015/mm/dd by A.Y.@XOOMS
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Xooms;

namespace Xooms{
    public class BenchMark1 : MonoBehaviour {
        
        //Public Variables
        public Transform targetTrans;

        //Private Variables
        GameObject targetGameObject;
        bool firstTime = true;
        Stopwatch sw = new Stopwatch();
        int maxItr = 100000;
        float delA = 1.0f;
        DummyCube dummyCube;

        //Public Functions

        //--------------
        // Initialization
        void Start () {
            targetGameObject = targetTrans.gameObject;
            dummyCube = targetTrans.GetComponent<DummyCube>();
        }
    
        //
        void Update () {
            if(firstTime) {
                firstTime = false;
                //1.回転関連ベンチマーク
                //1-1 オイラー角で設定する
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    targetTrans.eulerAngles = v;
                }
                sw.Stop();
                DebugOut("1-1",sw.ElapsedMilliseconds);
                //
                //1-2 Rotate()関数を使って相対的にまわす
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    targetTrans.Rotate(i*delA*Vector3.up);
                }
                sw.Stop();
                DebugOut("1-2", sw.ElapsedMilliseconds);
                //
                //1-3 クォータニオンで設定する
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    targetTrans.rotation = Quaternion.Euler(0.0f, i*delA, 0.0f);
                }
                sw.Stop();
                DebugOut("1-3", sw.ElapsedMilliseconds);
                //
                //1-1A 1-1と同じ処理で、毎回transformコンポーネントを探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    targetGameObject.transform.eulerAngles = v;
                }
                sw.Stop();
                DebugOut("1-1A",sw.ElapsedMilliseconds);

                //
                //1-11 ターゲット側のPublic関数に角度をを渡す(外部関数の中身は1-1と同等)
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    dummyCube.RotateByAngle(i*delA);
                }
                sw.Stop();
                DebugOut("1-11",sw.ElapsedMilliseconds);

                //1-12 ターゲット側のPublic関数に角度ベクトルをを渡す(外部関数の中身は1-1と同等)
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    dummyCube.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12",sw.ElapsedMilliseconds);

                //1-12A 1-12と同じ処理でループの中で毎回外部クラス(コンポーネント)を探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    DummyCube target = targetTrans.GetComponent<DummyCube>();
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    target.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12A",sw.ElapsedMilliseconds);

                //1-12B 1-12と同じ処理でループの中で毎回外部GameObjectを探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    GameObject go = GameObject.Find("DummyCube");
                    DummyCube target =go.GetComponent<DummyCube>();
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    target.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12B",sw.ElapsedMilliseconds);
            }
        }

        private void DebugOut(string title, float timeMsec){
            UnityEngine.Debug.Log(title+ ": "+timeMsec.ToString("f1")+" msec");
        }
    }
}

uGUIのテキストにブラーがかかってしまう問題

uGUI(Canvas UIシステム)のTextUI要素にブラーがかかってしまう問題は、既知のバグのようだ。

現時点では、対応策はピクセル解像度の大きなテキストを作り、Scaleを小さくして縮小する、という手しかないようだ。

Text UI要素のインスペクタは次のような感じになる。RectTransformのWidth/Hight、FontSizeを大きくし、RectTransformのScaleを0.1とか0.01とかに小さくする。

f:id:yasuda0404:20150312141620p:plain

しかし、この設定でもすこしぼやけた感じがする。早急な対応をお願いします!

uGUI: スクリプトからUIのパラメータを変更する

uGUIへユーザが操作した際にメソッドを呼び出すのとは逆に、スクリプトからUIの状態やパラメータを変更したい場合もある。代表的な例を下にあげる。

テキスト

uGUIのテキストを変更するには、テキストGameObjectのTextコンポーネントを取得し、このText.textプロパティを変更すればよい。

サンプルスクリプトは次。

ポイントは、UnityEngine.UIネームスペースを追加すること!

using UnityEngine;
using UnityEngine.UI;

public class Example : MonoBehaviour
{

 [SerializeField]Text btnText;
  [SerializeField]Toggle toggle;
  [SerializeField]Slider slider;

  private bool isOn = false;
  
  REIPacket packet=new REIPacket();

  public void OnOff()
  {
      isOn = !isOn;
      if(isOn) {
         btnText.text = "On";
         toggle.isOn  = true;
         slider.value = 1.0f;
         slider.Select();
      } else {
         btnText.text = "Off";
         toggle.isOn  = false;
         slider.value = 0.0f;
         toggle.Select();
      }
  }

トグル(チェックボックス)状態

Toggleコンポーネントbool isOnを変更する。

スライダの値

Sliderコンポーネントvalueを変更する。

フォーカス(共通)

各UIコンポーネント.Select()メソッドを呼ぶ。該当UIにフォーカスが移り、その他はフォーカスが外れる。

※注)ゲームコントローラやキーボードからの操作を可能にするよう、EventSystemのFirst Selectedに、最初にハイライトするUI要素を設定しておくこと!

スクリプトのテンプレートを追加する

スクリプトを新規作成する際に、独自のテンプレートを使いたいことがあるとおもう。

その場合は、Unityのインスールフォルダ/Editor/Data/Resources/ScriptTemplates/の下にある、

  • 80-Javascript-NewBehaviourScript.js.txt
  • 81-C# Script-NewBehaviourScript.cs.txt
  • 82-Boo Script-NewBehaviourScript.boo.txt
  • 83-Shader-NewShader.shader.txt
  • 84-Compute Shader-NewComputeShader.compute.txt

を修正するか、あるいは、新規にテンプレートファイルを追加する。

僕の場合、主に使うC#用テンプレートにヘッダコメントとNamespaceをデフォルトで入れたテンプレートを作っている。

// Title
// First Version 2015/mm/dd by A.Y.@XOOMS
// Last Update   2015/mm/dd by A.Y.@XOOMS
//
using UnityEngine;
using System.Collections;
using Xooms;

namespace Xooms{
    public class #SCRIPTNAME# : MonoBehaviour {

        // Initialization
        void Start () {
    
        }
    
        //
        void Update () {
    
        }
    }
}

これを、オリジナルのC#テンプレートに上書きしてもよいが、オリジナルのテンプレートは残したい場合は、別名で保存する。

ファイル名は、'番号+表示名-スクリプト種類.拡張子.txt'となり、'-'ハイフンより前の、番号+表示名は自由に設定できる。番号は数字のほかアルファベットでもいいようだ。要は番号部分のテキストの小さい順番に、メニューに並ぶ。

たとえば81A-C#XOOMS Script-NewXoomsScript.cs.txtという別の名前で保存すると、正規のC#テンプレートの次に、オリジナルテンプレートが表示される。

f:id:yasuda0404:20150309164902p:plain

Materialを比較する

あるオブジェクトにアサインされている特定のマテリアルを別のマテリアルに置き換えたかったので、次のようなコードを書いた。

[SerializeField]protected Renderer rend; //Target Renderer
[SerializeField]protected Material matFrom; //Change from this material
[SerializeField]protected Material matTo;   //Change to this material

<途中省略>

void Change()
{
    Material[] mats = rend.sharedMaterials;
    Debug.Log("ChangeMaterial: mats.Length= "+mats.Length);
    for(int i=0;i<mats.Length;i++) {
        Debug.Log(i+" Spec Int "+mats[i].GetFloat("_SpecInt"));
        if(mats[i] == matFrom) {
            Debug.Log("ChangeMaterial:Material("+i+" changed");
            mats[i]= matTo;     //Replace the material
        }
    }
    rend.materials = mats;  //Reassign Materials
}

ところがこれは機能しない。マテリアルはRunTimeでインスタンス化されるが、インスペクタでアサインしたマテリアルはインスタンスではない。このため、mats[i] == matFromという比較が真にならない。

そこで、マテリアルの名前で比較しようと思い、比較分の箇所を

if((mats[i].name == matFrom.name)...

と書いたが、これもうまくいかない。マテリアルがインスタンス化されると名前も変わるからだ。

結局、

if(mats[i].name == (matFrom.name+" (Instance)")) {

と、元の名前の後に" (Instance)"を加えれば、一応うまくいった。

しかし、もっとスマートな比較方法がないものだろうか…。

"9x9 Slice" Spriteを作成する

UI用のイメージを設定する場合、イメージを拡大しても周囲のラインは画像が荒れるので拡大したくない。これを実現するのが、「9x9スライス」というテクニック。要は、4つの角はスケーリングせず、中間の画像のみ拡大するという手法だ。

これをUnityで実現するには、SpriteとBorderを使う。

まずPhotoshopなどでUI用の画像を作る。シンプルなものなら縦横16pxの正方形にする。PNG形式で書き出す(アルファを使えるPNGがよい)。

f:id:yasuda0404:20150307182000p:plain

Unityで、この画像をアセットして読み込む。

  • Texture Type Sprite(2D and UI)
  • Sprite Mode Single

に設定。

f:id:yasuda0404:20150307182013p:plain

し、Sprite Editorを開き、左下の入力フォームでL/R/B/Tを適当に設定し、9分割のボーダーを引く。Applyで変更を反映。

f:id:yasuda0404:20150307182021p:plain

後は、各UI要素のImageコンポーネントSource Imageに、上で作成したSpriteを適用する。UI要素を拡大しても、エッジラインは保たれる。

f:id:yasuda0404:20150307182601p:plain

CanvasUI(uGUI)要素にアニメーションを設定する

CanvasUI(uGUI)のインタラクティブ要素(Button, Toggle, Sliderなど)は、「操作状態」に応じて「外観」を変更できる。

操作状態

各UI要素の状態には

  • Normal
  • Highlighted
  • Pressed
  • Disabled

の4通りがある。'Highlighted'はフォーカスがあたった状態で、マウスならなくてもよいが、ゲームコントローラで制御する場合は必須。どの要素が選択されているかをフィードバックしなければならないからだ。

外観

外観の変更は各コンポーネントTransitionで行い、

  • none 外観を変えない
  • Color Tine RGBAの変更
  • Sprite Swap 画像の変更
  • Animation アニメーションの付加

の4つの設定がある。

f:id:yasuda0404:20150307155309p:plain

アニメーション

TransitionでAnimationを選択。Animationを使うにはAnimatorコンポーネントが必要。'Auto Generate Animation Controller'をクリックし、AnimationControllerをアセット内に保存する。デフォルトでは、たとえばButton.Controllerのような名前になる。

f:id:yasuda0404:20150307154919p:plain

同時に、AnimatorコンポーネントがUI要素に追加される。

f:id:yasuda0404:20150307160633p:plain

アニメーションはAnimation Window(Window - Animation)で編集する。

f:id:yasuda0404:20150307160901p:plain

Animation Windowの左上、再生ボタンの下をクリックし、編集したいアニメーションを選択する。

Canvas UI(uGUI)のイベント処理

CanvasUI(uGUI)システムでは、ユーザの操作をEventで授受する。たとえばButton要素の場合、インスペクタのButtonスクリプト・ブロックの一番下に、OnClick()という項目がある。ここにOnClick()イベントを受け取る関数と渡す引数を設定する。

f:id:yasuda0404:20150307145538p:plain

タブの"+"をクリックし、左下の枠にイベントを渡すGameObjectを設定し、右上の枠にイベントを送る関数をドロップダウンリストから設定する。左下は関数に渡す引数で、

  • なし
  • float
  • int
  • string
  • bool
  • Object

のいずれか。最後の"Object"はUnityのほとんどのクラス(たとえばGameObject, Component, Monobehaviourなど)のベースクラスなので、実質的にはほとんどのクラスを引数に指定できる。たとえばTransformや、GameObjectそのものも渡すことができる。

ユーザー定義関数を呼び出すサンプル

たとえば、ボタン要素のクリック時のアクションとして、

1) UIControllerのButtonClick()メソッドに、"CustomButton"というstringを渡す場合:

f:id:yasuda0404:20150307142006p:plain

2) ButtonClick2()に、カメラのTransformコンポーネントを渡す場合:

f:id:yasuda0404:20150307141924p:plain

等というように設定する。

なお、UI要素から呼び出すユーザ定義関数はpublic voidのみ、渡せる引数の個数は0か1であることに注意。

ちなみに受け側の関数は次のようになる。

public void ButtonClick(string name)
    {
        Debug.Log("Button "+name+" clicked.");
    }

    public void ButtonClick2(Transform cameraTransform)
    {
        Debug.Log("Camera Position "+cameraTransform.position);
    }
その他のイベント

OnClick()以外のイベントを生成・処理したい場合は、EventTriggerを使う。

イベントを生成したいUI要素を選択し、Component-Event-EventTriggerを選択。

f:id:yasuda0404:20150307144247p:plain

UI要素にEventTriggerコンポーネントが追加される。Add New Event Typeをクリックして該当するイベントタイプを選択、後はOnClick()と同様に、イベント受信先のGameObject、イベントを受け取る関数、引数を設定する。

f:id:yasuda0404:20150307144540p:plain

EventTriggerを使えば自由にEventを生成できる。たとえば、UIImage要素はデフォルトではイベントを持っていないが、EventTriggerを使ってOnClickPointer Enterイベントを追加することができる。

参考:

qiita.com

Canvas(uGUI)の基本レイアウト

Unity4.6から導入された新GUI、"uGUI"。従来のGUIより柔軟な設定ができるuGUIの導入についてまとめた。

CanvasUI要素をシーンに配置する

ヒエラルキビュー、Create-UIで、CanvasのUI要素が表示される。CanvasUIは、まず土台となるCanvasをシーンに配置し、その上に各UI要素を乗せていくという概念。

しかし、いきなりUI要素を選択しても、自動的にCanvasを配置してくれる。ここではUI要素のPanelを選択してみる。

f:id:yasuda0404:20150307111027p:plain

するとこのように、ヒエラルキビューに、Canvas-Panelが配置される。EventSystemというオブジェクトも自動生成される。

f:id:yasuda0404:20150307111600p:plain

続いて、Create-UIでButtonを選択。こんな感じになる。

f:id:yasuda0404:20150307112753p:plain

f:id:yasuda0404:20150307111019p:plain

Canvasコンポーネント

以下、Canvas特有のコンポーネントについて。

Canvasのインスペクタで指定するRender Modeには、次の3つのモードがある。

  • Screen Space - Overlay すべてのカメラのレンダリング画面上にオーバーレイ表示する
  • Screen Space - Camera 特定のカメラの画角いっぱいになるよう自動拡大される
  • World Space シーン内の3D空間に配置する

OverlayとCameraは機能的には似ているが、OverlayはPost処理としてレンダリング画面に上書きされるのに対し、Cameraはあくまでも3D空間上のオブジェクトとしてカメラの画角いっぱいに正対して表示される。このため、Cameraの場合は、そのカメラのみに有効であることと、カメラの近くにオブジェクトがある場合、Canvasはその背後に表示される、という違いがある。Unity Manal-Canvas

ここではWorld Spaceを選択し、メインカメラから見える位置に移動する。

f:id:yasuda0404:20150307111035p:plain

Rect Transformコンポーネント

CanvasUI要素の配置はRect Transformコンポーネントがつかさどる。これはtransformコンポーネントと似ているが、UI設定に便利なように違いもある。

Scale

移動・回転は一般のGameObjectと同じように扱える。

大きさの変更は"Scale Tool"と"Rect Tool"の2通りの方法がある。"Scale Tool"は文字通りScaleを変更するが、"Rect Tool"をRect Transformコンポーネントを持つオブジェクト(すなわちCanvasUI要素)に適用した場合、Scaleは変わらない。その代わりにWidth/Heightが変更される。スケール変更との違いは、フォントサイズやボーダーライン太さなどに影響しないこと

Pivot

PivotはUI要素の2次元平面上の回転中心。左下が(0,0)、右上が(1,1)の相対座標であたえる。ヒエラルキビュー上では、Pivot点は中抜きの青丸で表示される。PivotモードではPivotを中心とした回転に、Centerモードではオブジェクト中心を基準に回転させることができる。

"Rect Tool"は、シーン上のUI要素の辺または角をつかんでドラッグすることで行える。また、

f:id:yasuda0404:20150307111002p:plain f:id:yasuda0404:20150307111011p:plain

Anchor

CanvasUIの要素の位置はすべて親要素にたいする相対位置で指定する。この相対位置を定義するのがAnchor。ヒエラルキビュー上は(4つの)中抜きひし形で示される。

AnchorにはMax/Minの2つの座標がある。

Max/Minを同じ値にすると、Anchorha一点になる。たとえば、親要素の右下コーナーにAnchorを設定すると、親要素のサイズが変わっても、子要素の右下からの位置は一定に保たれる。

Max/Minに違う値を設定すると、位置とともに子要素のサイズも変わる。たとえば、親要素の左下と右下にAnchorを設定すれば、親要素の幅が変わったときに、親要素との左右のすき間が一定となるように、子要素の幅が変わる。

Unity APIにあるアニメーションが参考になる。

なお、Anchorにはプリセットが用意されているので、通常はここから選べば事足りる(と思う)。厳密に指定したい場合はインスペクタで数値を入力することもできる。

f:id:yasuda0404:20150307121623p:plain

余談

Overlayモードでは、Canvasはシーン上でZ方向にしか動かせないらしい。もっともどこに配置してもいいわけだが。また、Z座標が±1000を越えるとカメラスクリーンに表示されなくなるようなので、Z座標で表示非表示を変更できる?

ジェネリックリストのジェネリックリスト(ジェネリックリストの入れ子)を作る

以前、「Unityで使える広義の配列」で書いたように、最近のC#では、ArrayListよりGeneric Listが推奨されている。

要素の型を指定できるので、よりロバストなコードがかけるGeneric Listだが、Generic Listの要素にGeneric Listを指定する、すなわち、Generic Listの入れ子はできるのだろうか?

答えはYes.

もともとGeneric ListはJavascript配列のように後から要素を追加(Addメソッドを使う)できるのが便利。なので、Generic ListのGeneric Listが可能なら、いわゆる「ジャグ配列」のようなリストを作ることができる。

これをやりたかった理由として、ツリー階層構造にあるGameObjectから、下の階層のGameObjectを自動的に取り出してGeneric List化したかったことがある。たとえば、

f:id:yasuda0404:20150306214040p:plain

のようなGameObjectの階層があり、このトップのMenuObjectのみから下の子GameObjectを自動的にリスト化したかった(なお、今回は階層数は固定とする)。

このスクリプトが次。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Test2 : MonoBehaviour {

    [SerializeField] protected Transform top;  //'MenuObject'を指定

    private List<GameObject> layer1;
    private List<List<GameObject>> layer2;

    // Use this for initialization
    void Start () {
        layer1 = new List<GameObject>();    //第1階層のGneric List
        layer2 = new List<List<GameObject>>();   //第2階層のGeneric List
        foreach(Transform elem1 in top) {
            layer1.Add(elem1.gameObject);
            List<GameObject> list = new List<GameObject>();
            foreach(Transform elem2 in elem1) {
                list.Add(elem2.gameObject);
            }
            layer2.Add(list);
        }

        //チェック1 Generic List的に扱う
        foreach(List<GameObject> list in layer2) {
            foreach(GameObject elem in list) {
                Debug.Log("child:"+elem.name);  //第2階層の要素
            }
        }

        //チェック2 ArryList的に扱う
        for(int i = 0; i < layer1.Count; i++) {
            Debug.Log("layer1["+i+"] = "+layer1[i].name);   //第1階層の要素
            for(int j = 0; j < layer2[i].Count; j++) {
                Debug.Log("layer2["+i+"]["+j+"] = "+layer2[i][j].name); //第2階層の要素
            }
        }
    }
    
<<<以下略>>>

layer1が第1階層のGameObjectのGeneric List、layer2が第2階層のGameObjectからなるGeneric Listになる。

上の"Check Process"は、layer1, layer2の要素のGameObjectの名前をDebug.Logで出力するものだが、Generic ListはArrayListのように要素インデックスでも指定できる。(たとえば、layer2[i][j])。場合によってはインデックスで指定したほうが便利なときもあるので、使い分ければよいと思う。

インスペクタでプルダウンリストを使う

インスペクタにプルダウンリストを表示するには、enumを使う。

たとえば、iTweenプラグインのイージングタイプをプルダウンリストで指定するには、

public enum EaseType {
        easeInQuad,
        easeOutQuad,
        easeInOutQuad,
        easeInCubic,
        easeOutCubic,
        easeInOutCubic,
        easeInQuart,
        easeOutQuart,
        tQuart,
        easeInQuint,
        easeOutQuint,
        easeInOutQuint,
        easeInSine,
        easeOutSine,
        easeInOutSine,
        easeInExpo,
        easeOutExpo,
        easeInOutExpo,
        easeInCirc,
        easeOutCirc,
        easeInOutCirc,
        linear, 
        spring,
        easeInBounce,
        easeOutBounce,
        easeInOutBounce,
        easeInBack,
        easeOutBack,
        easeInOutBack,
        easeInElastic,
        easeOutElastic,
        easeInOutElastic
    }
    [SerializeField] EaseType easeType = EaseType.easeOutQuart;

のように書けばいい。

public enum..enumクラス("EaseTyp")を定義し、EaseType easeTypeでそのクラスの変数を定義している。

インスペクタには次のように表示される。

f:id:yasuda0404:20150306202549p:plain

ところで、enumの実態は配列のIndex番号の整数なので「演算」ができる。たとえば、

Debug.Log("EaseType.easeInBounce "+(EaseType.easeInBounce+1)); //'easeOutBounce' を出力

となる。

これはこれで便利なのだが、iTweenのイージングタイプのように、文字列(string)変数として使いたい場合もある。

この解決策は、ToString()メソッドを使うこと。

string easetype = easeType.ToString();   //文字列に変換

とすれば、"easeInBounce"などの文字列を得ることができる。

レンダリングの品質設定

レンダリングの品質設定は Edit-Project Settings-Quality でおこなう。

各パラメータを個別に設定もできるが、Levelsに"Fastest"~"Fantastic"までのプリセットがあり、それを選択することもできる。また、カスタム品質レベルを登録することも可能。

f:id:yasuda0404:20150224094541p:plain

個人的にはAnti Aliasingは画質に大きく影響すると思うのだが、Unityのプリセットは低めに抑えられている。(Fantasticでも"2X Multi Sampling")。これを、"4X"、"8X"にあげると画質はかなり向上する。もちろん、パフォーマンスとのトレードオフになる。

覚えておくと作業がはかどるショートカット

シーンビューの操作と重複するが、シーン編集時に作業がはかどるUnity標準のショートカットキーをまとめた。

選択モード

ショートカット 操作
q ハンドモード
w オブジェクトの移動モード
e オブジェクトの回転モード
r オブジェクトの拡大・縮小モード
t 矩形モード
z Center/Pivot切り替え
x Global/Local切り替え
Shift+Space ビューの拡大・縮小(*1)

(*1) Play中のゲームビューは拡大・縮小できない

なお、キーアサインは、Edit-Preferences...で変更できる。

C#のプロパティを理解する

プロパティの使用 (C# プログラミング ガイド)

C#にかぎらずオブジェクト指向言語のクラス内の変数を外部から読み出したり、変数に値を設定する場合は、get/setメソッドを使うのが基本。しかし、変数ごとにいちいちget/setメソッドを設定するのは煩雑になってしまう。

そこでC#では「プロパティ」というしくみがつくられた。プロパティは、クラスの外から見ればget/setメソッドのように見え、クラス内部から見れば普通の変数として扱える。

次のサンプルコードでは、int Monthにgetアクセサとsetアクセサが設定されている。getアクセサはクラス外から値を読むときに実行され、setアクセサは値を設定するときに実行される。 setは、プロパティと同じ方のvalueという暗黙のパラメータを介して値を設定する。

public class Date
{
    private int month = 7;  // Backing store

    public int Month
    {
        get
        {
            return month;
        }
        set
        {
            if ((value > 0) && (value < 13))
            {
                month = value;
            }
        }
    }
}

プロパティを使うメリットは、メソッドを増やさずにメソッドと同等のことができること。たとえば、値が読まれた時にデバッグ出力したり、イベントを発行する、値が設定される際に範囲チェックを行なう(上の例)、などだ。