Unityな日々(Unity Geek)

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

軸周りの回転の方向

回転は一見単純な操作だが、すぐ忘れるので備忘録代わりに書いておく。


プリミティブをの円柱(Cylinder)を新規作成した場合、

transform.right :グローバルX方向&ローカルX方向=半径方向1
transform.up :グローバルY方向&ローカルY方向=円柱の軸方向
transform.forwad: グローバルZ方向&ローカルZ方向=半径方向2

となる。
f:id:yasuda0404:20141017191018p:plain


この円柱を

transform.Rotate(new Vector3(90.0f, 0.0f, 0.0f))

と、X軸周りに90度回転させる。(円柱の軸は水平方向に向く)
このとき、

transform.right :グローバルX方向&ローカル半径方向1
transform.up :グローバルZ方向&ローカル軸方向
transform.forwad: グローバル-Y方向&ローカル半径方向2

となる。
f:id:yasuda0404:20141017191251p:plain

つまり、ある軸まわりの回転は、その軸を手前から向こうに向かう方向から見たとき、反時計回りの回転となる。ねじ回しでいうと、ねじを外す方向。

当然のことだが、ローカル座標は物体にくっついて方向を変える。円柱の場合、軸方向は常にローカルY軸となる。


この円柱を軸方向に動かしたい時、どうするか。

obj.transform.Translate(10*obj.transform.up);

と書いてしまいそうだが、これではだめ。Translate()はデフォルト(=Translateの第二引数が`Space.Self`の状態)ではグローバル座標で方向を指定するからだ。
下の画像は、青い球の中心に生成した円柱を、青い球の半径分、円柱の軸方向に動かそうとしたもの。しかし、軸方向ではなく円柱の半径方向に動いてしまっている。これは、グローバル座標で(0.0, 0.0, 1.0)であるtransform.upを、円柱のローカル座標で動かしているため、矛盾が生じてしまったのだ。つまり、円柱の軸は水平を向いているが、円柱ローカルのZは下を向いているので、下方に移動してしまう。
f:id:yasuda0404:20141017194946p:plain


円柱の軸方向に動かすには、Translateの第二引数に'Space.World'を指定し、

obj.transform.Translate(10*obj.transform.up, Space.World);

とすればよい。これで、グローバルで見ているtransform.upを、正しくグローバル座標で移動できる。
f:id:yasuda0404:20141017194956p:plain



ランダムに与えた緯度・経度の示す地球表面上の地点に、垂直に円柱を立てるには次のようにすればよい。

void Update () {
  if(count < 20) {
    cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
    cylinder.transform.position = Vector3.zero;
    Debug.Log (cylinder.transform.up);	//(0.0, 1.0, 0.0)
    cylinder.transform.localScale = new Vector3(cylinderRadius, cylinderHeight, cylinderRadius);
    lat =  Random.Range(-80.0f,80.0f);	//latitude
    lng = Random.Range(-180.0f,180.0f);	//longitude

    cylinder.transform.Rotate(new Vector3(-90.0f, 0.0f, 0.0f), Space.Self);	//rotate 90 around X to equator
    Debug.Log (cylinder.transform.up);	//(0.0, 0.0, 1.0)
    cylinder.transform.Rotate(new Vector3(0.0f, 0.0f, -lng), Space.Self);	//rotate in longitude direction
    cylinder.transform.Rotate(new Vector3(lat, 0.0f, 0.0f), Space.Self);

    cylinder.transform.Translate((sphereRadius+cylinderHeight)*cylinder.transform.up, Space.World);
  };
  count++;
}

f:id:yasuda0404:20141017201230p:plain

Unityで使える広義の配列

参考:http://wiki.unity3d.com/index.php?title=Which_Kind_Of_Array_Or_Collection_Should_I_Use%3F

Unityの広義の配列―コレクション―はいくつか種類がある。それぞれ宣言や代入、値の取出しの記述が異なる。また、自由度が高い反面、使わないほうがよいものもある。



1)ビルトイン配列(Built-in Array)

もっとも基本的な配列で、もっとも高速。ただし宣言時に要素数を指定する必要がある。が、宣言後にリサイズは可能。(System.Array.Resize(ref myArray, size))で行う。)

C#での宣言、代入、リサイズは次のように行う。ただし、多次元のビルトイン配列はリサイズできない。

TheType myArray = new TheType[lengthOfArray];  // declaration
int[] myNumbers = new int[10];                 // declaration example using ints
GameObject[] enemies = new GameObject[16];       // declaration example using GameObjects
int howBig = myArray.Length;               // get the length of the array
myArray[i] = newValue;                     // set a value at position i
TheType thisValue = myArray[i];            // get a value from position i
System.Array.Resize(ref myArray, size);    //resize array
 
string[] weapons = new string[] { "Sword", "Knife", "Gun" };
float[] alphas = new float[] { 1.0f }; 


2)Javascript配列(Javascript Array)

標準的な.netクラスにはない、Unity独自の配列。ただし名前通り、javascriptスクリプトでのみつかえ、C#型では使えない。

このため、サイズ変更可能、要素の型を宣言する必要がないなど柔軟性が高いが、反面、性能的にはもっとも遅い。

Javascript Arrayは基本的にはArrayListにラッパーをかけたものとみなせ、同様のことはObjectリストで実現できる。しかも速い。Javascript Arrayは「過去の遺産」と考えた方がよさそう。


3)アレイリスト(ArrayList

Javascript Array以上に柔軟性がある(たとえば要素数を変更できるなど)が、性能上は遅い(*)。またArrayList自体には型はなく、任意の型を要素として持つことができる。型の混在も可能。

ただし、Generic Listの登場によりArrayListの存在意義はなくなったといわれる。旧式のコードとの互換性上のものと考えた方がよさそう。

(*)性能面のハンデの多くは宣言時に要素数を宣言しないため、という報告もある。つまり、ArrayListでも要素数を固定すれば、速度は改善するらしい。

ArrayList myArrayList = new ArrayList();    // declaration
myArrayList.Add(anItem);                    // add an item to the end of the array
myArrayList[i] = newValue;                  // change the value stored at position i
TheType thisItem = (TheType) myArray[i];    // retrieve an item from position i
myArray.RemoveAt(i);                        // remove an item from position i
var howBig = myArray.Count;                 // get the number of items in the ArrayList


4)ハッシュテーブル(Hashtable)

'key-value'方式で要素を参照する配列。
key, valueともにどんな型でもよい(untype)。型の混在も可能。(keyは通常はString型であることがおおいが。)このため、Hashtableから要素を取り出す際は、ユーザがCastしてやる必要がある。
ただし、ArrayListよりもGeneric Listが良いのと同じように、近年は、HashtableよりDictionaryを使う方が良い。

Hashtable myHashtable = new Hashtable();                 // declaration
myHashtable[anyKey] = newValue;                          // insert or change the value for the given key
ValueType thisValue = (ValueType)myHashtable[theKey];    // retrieve a value for the given key
int howBig = myHashtable.Count;                          // get the number of items in the Hashtable
myHashtable.Remove(theKey);                              // remove the key & value pair from the Hashtable, for the given key.

5) ジェネリック・リスト(Generic List)

Generic Listは、Listとも呼ばれる。要素数可変、要素の追加・削除ができる点ではJavascript, ArrayListと同等の柔軟性がある。違いは、定義時に型を指定する点。宣言した型の要素しか保持できないため

  • 要素の値を取出す際、型キャストが不要。
  • ArrayListより速い

という利点がある。
つまり、特定の型のみ扱うことがわかっている場合は、ArrayListよりGeneric Listを使う方が良い。

using System.Collections.Generic;

List<Type> myList = new List<Type>();                 // declaration
List<int> someNumbers = new List<int>();              // a real-world example of declaring a List of 'ints'
List<GameObject> enemies = new List<GameObject>();    // a real-world example of declaring a List of 'GameObjects'
myList.Add(theItem);                                  // add an item to the end of the List
myList[i] = newItem;                                  // change the value in the List at position i
Type thisItem = List[i];                              // retrieve the item at position i
myList.RemoveAt(i);                                   // remove the item from position i


6) ジェネリック・ディクショナリー(Generic Dictionary)

HashtableのGneric List版。宣言時にkey, valueの型を指定する。このため、Gneric Listと同じ利点が得られる。

using System.Collections.Generic;

// declaration:
Dictionary<KeyType,ValueType> myDictionary = new Dictionary<KeyType,ValueType>();
 
// and a real-world declaration example (where 'Person' is a custom class):
Dictionary<string,Person> myContacts = new Dictionary<string,Person>();
 
myDictionary[anyKey] = newValue;                 // insert or change the value for the given key
ValueType thisValue = myDictionary[theKey];      // retrieve a value for the given key
int howBig = myDictionary.Count;                 // get the number of items in the Hashtable
myDictionary.Remove(theKey);                     // remove the key & value pair from the Hashtable, for the given key.


7) 2次元配列(2D Array)

以上はすべて1次元の配列について述べてきた。Unityでは2次元以上の配列を使うこともできる。以下は2次元配列についてのべるが、3次元、4次元…といった多次元配列を扱うこともできる。

「一般の」2次元配列の宣言では、外側・内側の2要素の要素数を指定する。

// declaration:
string[,] myArray = new string[16,4];            // a 16 x 4 array of strings
 
// and a real-world declaration example (where 'Tile' is a user-created custom class):
Tile[,] map = new Tile[32,32];                   // create an array to hold a map of 32x32 tiles
 
myArray[x,y] = newValue;                         // set the value at a given location in the array
ValueType thisValue = myArray[x,y];              // retrieve a value from a given location in the array
int width = myArray.GetLength(0);                // get the length of 1st dimension of the array 
int length = myArray.GetLength(1);               // get the length of 2nd dimension of the array


一方「ジャグ(jagged)」2次元配列では、(内側の)要素の数がそろっていなくても良い。

var myJaggedArray = [ [1, 2, 3], [4, 5], [6, 7, 8, 9, 10] ]; // a jagged int array

myArray[x][y] = newValue;
ValueType thisValue = myArray[x][y]; 

ちなみに、「ビルトイン配列(たとえば、GameObject[])を要素にもつジェネリック・リスト」を作るには次のように書く。

GameObject[] go1;
GameObject[] go2;
GameObject[] go3;
List<GameObject[]> gos = new List<GameObject[]>{go1,go2,go3};

以上、Unityで扱われる広義の配列について述べた。利用指針は次のようになるだろう。

・大きさがわかっているケースは、ビルトイン配列を使う。メモリ効率も速度ももっともよい。
ArrayListのかわりにGneric Listを、Hashtableの代わりにGeneric Dictionaryを使う。
Javascript Arrayは使わない。

要するに、型のキャストを避けるべき。
開発時はArrayListやHashtableを使ってもよいが、リリース時はGnericに変換するとよいだろう。

実行の順番:OnEnable, Awake, OnLevelWasLoaded, Start

Unityのスクリプト内で初期化を行う関数はいくつかある。

ひとつのスクリプトの中で実行される順番は次の通りらしい。(出典はこれ

Awake
OnEnable
OnLevelWasLoaded
Start

しかし、スクリプトを無効にした場合(Script.enabled=false)は、次の通り

Awake
OnLevelWasLoaded

すなわち、OnEnable, Startは実行されない

Awake, Startはひとつのオブジェクトについて、一度しか呼ばれない。
しかし、OnEnableは、Enableされるたびに呼ばれる、というのも違い。


また、異なるスクリプトどのAwake(あるいはOnEnable、Start)が先に呼ばれるかはデフォルトでは不定。


明示的にどのスクリプトを先に実行するかを指定するには、Edit>Project Settings...>Script Execution Order Settings で行う。
'Default Time'より先に実行したいスクリプトを上に、後に実行したいものを下に置く。上ほど先に実行される。
指定したスクリプト以外は'DefaultTime’に実行されるがその順番は不定である。

インスペクタで、カスタムクラスの中にある変数にアクセスする

わかりにくいタイトルだが、具体的な例で説明する。

下のように、メインであるクラス(下の例では、Physics)のPublic変数にカスタム・クラス(SubData)
を指定しているとする。、

using UnityEngine;
using System.Collections;

public class SubData {
	public int data1;
	public int data2;
	private int data3;
}

public class Physics : MonoBehaviour {
	public SubData data;
	// 途中省略
}

この場合、インスペクターから、SubDataクラスは見えない。つまり、data1, data2も見えない。
f:id:yasuda0404:20130913160031p:plain



インスペクタからSubDataの中にある変数を見えるようにするには、Runtime Attributeの、[System.Serializable]を指定する。

[System.Serializable]
public class SubData {
	public int data1;
	public int data2;
	private int data3;
}

こうすれば、インスペクタから、SubDataのPublic変数が見えるようになる。SubDataクラスのPrivate変数には当然アクセスはできない。

f:id:yasuda0404:20130913160413p:plain

コンポーネントが存在するかどうかを見る

GameObjectに対し、あるコンポーネントを持っているかを調べ、なければ足す(あるいは、処理を変える)場合の手順

例えば、あるGameObjectに対し、rigidbodyコンポーネントがなければ新たに加え、rigidbodyコンポーネントを返す例。

if(rigidbody == null){
	return transform.gameObject.AddComponent("Rigidbody") as Rigidbody;
}else{
	return rigidbody;
}

'rigidbody == null'は、rigidbodyコンポーネントがない場合に真となる。

Componentを足すだけなら、

GameObject.AddComponent("コンポーネント名")

でよいが、Componentを加えると同時に、そのコンポーネントを変数として取得する場合、
変数 = GameObject.AddComponent(”コンポーネント名”) as コンポーネント
のように一行で行える。
AddCompnent()の引数はComponent型のため、コンポーネントの型に応じたキャスト(上の例ではRigidbody)が必要。


カスタムクラス・コンポーネントの場合は、次のように書ける。

if(ball.GetComponent<Ball>()){
	//if Ball does not exist
	ballRB = ball.GetComponent<Ball>().getRB();
}else{
	ballRB = ball.rigidbody;
}

一行目は、

if(ball.GetComponent<Ball>() != null){

としてもよい。

                  • -

追記:2013/9/13

特定のコンポーネントなければ加える別な方法として、Runtime Attributeの[Require Component(typeOf(コンポーネント名))]
を使う手もある。エディター上で見えるので、必ず組にして使う必要があるコンポーネントならこの方法がわかりやすい。

using UnityEngine;
using System.Collections;
[RequireComponent (typeof (Rigidbody))]
public class Test : MonoBehaviour {
.........

とすれば、Scriptをアタッチした時点で、アタッチしたゲームオブジェクトがRigidbodyコンポーネントを持っていなければ自動的に加えられる。

timeScaleでアニメーション速度を変更する

Time.timeScaleでFixedUpdate()の時間間隔を調整できる。

Time.timeScale=1.0 で、デフォルトの速度(時間間隔)
Time.timeScale=0.5で、速度を半分(時間間隔を2倍)にする
Time.timeScale=0.0は、速度ゼロ、すなわち、FixedUpdate()を停止する


具体的な事例で見てみる。

子オブジェクトのスクリプト。一定速度で回転する。

using UnityEngine;
using System.Collections;

public class revolution : MonoBehaviour {
	
    // Update is called once per frame
        void FixedUpdate () {
        transform.RotateAround(Vector3.up, 0.02f);
    }
}


親オブジェクト:一定速度で上昇するスクリプト。スペースキーでTime.timeScale=0.0f/1.0fにトグルする。これによって、アニメーションの停止・作動を切り替える。

using UnityEngine;
using System.Collections;

public class move : MonoBehaviour {
	
	// Use this for initialization
	    void Start () {
	}
	
	// Update is called once per frame
	void Update () {
	    if (Input.GetKeyDown ("space")){
            print ("space key was pressed Time.timeScale="+Time.timeScale);
	    Time.timeScale = Mathf.Abs (1.0f-Time.timeScale);
	}

	}
	void FixedUpdate(){
		transform.Translate(0.01f*Vector3.up);
	}
	
}


ここで注意するのは次の2点。

1)timeScale=0.0fで停止するのは、FixedUpdate()のみ。Update()はTime.timeScaleの値に関係なく、動き続ける。
 つまり、上の事例では、親オブジェクトのUpdate()内のInput.GetKeyDownによるキー入力は有効のままである。

2)親オブジェクトでTime.timeScale=0.0fとすると、子オブジェクトを含む全てのFixedUpdate()が停止する。上の例では、親オブジェクトの上昇が止まるだけでなく、子オブジェクトの回転も止まる。
 親子関係にあるオブジェクトだけではなく、シーン内のすべてのオブジェクトでFixedUpdate()は停止する。

オブジェクトの一部をマスクする

Unityで、GameObjectの一部にマスクをかけるには、DepthMaskを使う。
参考:Unity - Manual: ShaderLab syntax: SubShader Tags

1. Maskとなるオブジェクトの設定

1-1. 新規シェーダを作成。Maskシェーダのスクリプト(下記)を記述する。

Maskオブジェクトの順番を、レギュラー・ジオメトリとマスクをかけるジオメトリの間に設定している。

Shader "Masked/Mask" {
	
	SubShader {
		// Render the mask after regular geometry, but before masked geometry and
		// transparent things.
		
		Tags {"Queue" = "Geometry+10" }
		
		// Don't draw in the RGBA channels; just the depth buffer
		
		ColorMask 0
		ZWrite On
		
		// Do nothing specific in the pass:
		
		Pass {}
	}
}

肝は、SubShaderの中の、

Tags {"Queue" = "Geometry+10" }

の部分。Queueはレンダリングの順番を指定するタグ。デフォルトの設定値は次の通り。数字が小さいものから先にレンダリングされる。

Queue Value
Background 1000
Geometry 2000
AlphaTest 2450
Transparent 3000
Overlay 4000


Geometryは一般のオブジェクトを指す。上のスクリプトでは、Geometryのデフォルト値に10を加えた値、すなわち2010をQueueタグに設定していることになる。

1-2. 新しいMaterialを作り、Maskシェーダをアタッチする。

f:id:yasuda0404:20130401142827p:plain

1-3. このMaterialを、Maskとして使用したいオブジェクトにアタッチ。

2.Maskをかけるオブジェクトの設定

2-1. Maskをかけたいオブジェクトに、任意のMaterialを設定する。
2-2. 同オブジェクトに、次のスクリプトをアタッチ。

  

/*
	SetRenderQueue.cs
	
	Sets the RenderQueue of an object's materials on Awake. This will instance
	the materials, so the script won't interfere with other renderers that
	reference the same materials.
*/

using UnityEngine;

[AddComponentMenu("Rendering/SetRenderQueue")]

public class SetRenderQueue : MonoBehaviour {
	
	[SerializeField]
	protected int[] m_queues = new int[]{3000};
	
	protected void Awake() {
		Material[] materials = renderer.materials;
		for (int i = 0; i < materials.Length && i < m_queues.Length; ++i) {
			materials[i].renderQueue = m_queues[i];
		}
	}
}

[SerializeField]は、public変数ではないが、Inspectorウィンドウから値を設定できるようにする修飾子。
このスクリプトは、MaskをかけたいオブジェクトのRenderQueueを3000にしている。これによって、Regular Geometry(2000)→Mask(2010)→Maskをかけたいオブジェクト(3000)の順番にレンダリングされ、MaskをかけたいオブジェクトのみにMaskがかかることになる。


要は、RenderQueueの値を相対的に変えることで、レンダリングの順番を変更し、Maskをかけることになる。

Mouse入力 Event, OnMouse, Input

Mouse入力(および、タッチセンサー入力)を受け取るには、次の3つの方法がある。

1)Event
キー入力とMouse操作が発生した時に発信されるEventを受け取る。
ただし、Eventは、OnGUIの中のみで受け取れる(ようだ)

using UnityEngine;
using System.Collections;

public class RotateObject : MonoBehaviour {
	void OnGUI(){
		Event e = Event.current;
		if(e.button == 0 && e.isMouse){
			Debug.Log (e.delta);
		}
	}
}

e.buttonは、マウスボタンの種類。

e.button 0:左ボタン、1:右ボタン、2:中ボタン

e.isMouse == true は、マウスイベントであることを示す。


2)OnMouse
MonoBehaviourのメソッドとして用意されている、Mouse関連の関数を使う方法
Scriptのアタッチ先は、GUIElemmentかColliderを持つオブジェクトのみで、オブジェクトとマウスとの関係が重要

OnMouseEnter オブジェクト上に入った時に一度だけ呼ばれる。ボタンの状態は無関係
OnMouseExit オブジェクトから出た時に一度だけ呼ばれる。ボタンの状態は無関係
OnMouseDown オブジェクト上で左ボタンを押した。オブジェクト外で左ボタンを押してからオブジェクト内に入っても呼ばれない
OnMouseDrag マウス左ボタンを押したまま動かした。押したままオブジェクト外にでても有効
OnMouseOver オブジェクト上に入っている状態。OnMouseEnterと違って何度も呼ばれる
OnMouseUp オブジェクト上で左ボタンをアップ。オブジェクト外でアップしても呼ばれる。
OnMouseUpAsButton オブジェクト上で左ボタンをアップ。オブジェクト外でアップした場合は呼ばれない。


3)Input
アタッチされたオブジェクトと関係なくMouseの動作を取得する。通常は、Update()、FixedUpdate()の中で使用する。

GetMouseButton 左ボタンをおした状態。毎回呼ばれる。
GetMouseButtonDown 左ボタンをおした動作。押した際に一度だけ呼ばれる
GetMouseButtonUp 左ボタンを話した動作。一度だけ呼ばれる

*引数はすべて共通で、0:左ボタン、1:右ボタン、2:中ボタン

LineRendererを使う

例えば、空間上に引出線を描画する場合、線に相当するオブジェクトを置くよりも、LineRendererを使ったほうがスマート。

1)Scriptの例
  下の例は、Transformの配列(transforms)に格納した座標間にラインを描画するもの。

using UnityEngine;
using System.Collections;

public class LineTest : MonoBehaviour {
	
	public Transform[] transforms;
	LineRenderer lineRenderer;
	
	// Use this for initialization
	void Awake () {
		lineRenderer = GetComponent<LineRenderer>();
		lineRenderer.SetVertexCount(transforms.Length);
	}
	
	// Update is called once per frame
	void Update () {
		for(int i=0; i<transforms.Length;i++){
			lineRenderer.SetPosition(i, transforms[i].position);
		}
	}
}

2)上のScriptのアタッチ先オブジェクトに、LineRendererコンポーネントをアタッチする。ラインの色や太さなどはLineRendererコンポーネントで指定する。
f:id:yasuda0404:20130325174039p:plain
f:id:yasuda0404:20130325174048p:plain

3Dプロジェクタを設定する

Unityから3Dモニタ/プロジェクタに出力する際の設定覚書。
ちなみにサンプルの環境は、GeForce680のHDMIポートに、SANYO PDG-DWL2500(Panasonic CT230)とMitsubishi RDT231WMがつながっている状態である。DWL2500は解像度1280X800の3Dプロジェクタ、RDT231WMはFullHD(1920X1080)の液晶モニタだ。

nVIDIAコントロールパネルを開き、ディスプレイ-デスクトップのサイズと位置の調整で、3DVisionを動かすために、3Dプロジェクタのリフレッシュレートを120Hzにする。

次に、解像度を機器のスペックに合わせる。DWL2500の場合、1280X720。(注:DW2500のスペック上は1280X800まで表示できるはずだが、なぜか120Hzでは1280X720が上限。これはGPUの制約?)
f:id:yasuda0404:20130303113959p:plain

RDT231WMはスケーリングモードを「全画面表示」にしておく。こうしないと、後でDWL2500をプライマリにしてクローン化した際、RDT231WM側も全画面に表示されないため。解像度も1280X720にしているが、ここはFullHDのままでもよい。
f:id:yasuda0404:20130303113949p:plain


ステレオスコピック3D-ステレオスコピック3Dを設定します、で、
「ステレオスコピック3Dを有効にする」にチェックマークを入れる。
f:id:yasuda0404:20130303114008p:plain

次に、「ステレオスコピック3Dディスプレイのタイプ」に、3Dプロジェクタ、今回の場合、Sanyo PDG-DWL2500を選択する。(チェックマークだけ入れて、このディスプレイ選択をよく忘れるので注意!) 初めて動かす際は、「ステレオスコピック3Dのテスト」を実施して、3DVisionの機器設定を行う。
f:id:yasuda0404:20130303114015p:plain

ディスプレイ-複数ディスプレイの設定で、DWL2500の画面アイコン上で右クリック。
DWL2500を「プライマリする」を選択し、次に、
DWL2500上「クローンに使用」を選択。モニタ2(RDT231WM)をクローン化する。
f:id:yasuda0404:20130303114021p:plain

アイコンが下のような状態になる。
f:id:yasuda0404:20130303115623p:plain

以上で3Dの設定が完了。Unityから作成したEXEファイルを、解像度1280X720, フルスクリーン("Windowed"のチェックを外した状態)で実行すれば、立体視投影される。
f:id:yasuda0404:20130303120057p:plain


【注】もうひとつ、キーボードで'cntrl+T'を押すと、3D表示がオン・オフにトグルされる。この状態変化はドライバ側では認識されないので注意。実際、ドライバはちゃんと設定したのに3D表示されないことがあり、原因は'cntrl+T'だったことがある。

Prefabのインスタンスを生成する

Project内に次のPrefab (BallPrefab)があるとする。
f:id:yasuda0404:20130302160328p:plain

このPrefabをシーン内に生成するには、Instantiate()を使う。
たとえば次のScriptを作り、シーン内のGameObjectにアタッチ。

using UnityEngine;
using System.Collections;

public class MainScript : MonoBehaviour {
	public GameObject ball;
	
	// Use this for initialization
	void Start () {
		for (int i=0 ; i<3 ; ++i) {
      		      Instantiate(ball,new Vector3(Random.Range(-0.5f,0.5f)*10,i*10+10,Random.Range(-0.5f,0.5f)*10),Quaternion.identity);
    	        }
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

Instantiateの引数は、複製したいオブジェクト(Object)、位置(Vector3D)、回転(Quaternion)。
public変数として定義した'ball'に、Project内のPrefabをひもづける。
f:id:yasuda0404:20130302160647p:plain

Instantiate()の戻り値は'Object'のため、戻り値は使用するにはキャストが必要。
RigidBodyにキャスト:

	void Init () {
		Rigidbody[] obj = new Rigidbody[3];
		for (int i=0 ; i<3 ; ++i) {
      		obj[i] = Instantiate(ball,new Vector3(Random.Range(-0.5f,0.5f)*10,i*10+10,Random.Range(-0.5f,0.5f)*10),Quaternion.identity) as Rigidbody;
    	}

GameObjectにキャスト:

	void Init () {
		GameObject[] obj = new GameObject[3];
		for (int i=0 ; i<3 ; ++i) {
      		obj[i] = Instantiate(ball,new Vector3(Random.Range(-0.5f,0.5f)*10,i*10+10,Random.Range(-0.5f,0.5f)*10),Quaternion.identity) as GameObject;
    	}


このシーンを実行すると、Project内のBallPrefabがランダムな位置に生成される。
f:id:yasuda0404:20130302160835p:plain

                                                                      • -

[追記:2015/3/9]

上の手順では、インスタンス化したGameObjectはシーンの最上位階層におかれる。インスタンス化したGameObjectを特定のGameObjectの下(子供)におきたい場合は、

obj[i] = Instantiate(ball,new Vector3(Random.Range(-0.5f,0.5f)*10,i*10+10,Random.Range(-0.5f,0.5f)*10),Quaternion.identity) as GameObject;
obj[i].transform.parent = targetGameObject.transform;
    	}

と、transform.parentをつなぎなおせばいよい。

Textureの設定

オブジェクトへのTextureの貼付は
1)AssetにTexure画像を読み込み、
2) その画像をMaterialに貼り、
3) そのMaterialをObjectに適用する
と言う手順になる。

この時、Texture画像の設定によってレンダリング後の見え方が違う。Assetで画像データを選択した状態でInspectorで設定する。(以下、そろそろ4にアップグレードしなければと思いつつ、まだ3.5で動かしてます…)
f:id:yasuda0404:20130228161813p:plain

1.Filter Mode
1-1) Pointは画像そのまま表示
f:id:yasuda0404:20130228161916p:plain

1-2) Bilnear/Trilinearはカメラがある程度近づくとブラーをかける。
Bilnearの場合、
f:id:yasuda0404:20130228161940p:plain

1-3) Trilinearの場合
f:id:yasuda0404:20130228161957p:plain

Bilinear/Trilinearはカメラが近づいた際のブラーのかけ方が違う。


2.Max Size
画像の最大サイズのデフォルトは1024px.である。 1.の事例はすべてMax Size1024pxのためカメラが近づくと粗さが目立つ。

2-1) Max Sizeを4096pxにすると、カメラが近づいても精細に表示できる。次は4096px/'point'の場合。1-1)と比べると差は一目瞭然。
f:id:yasuda0404:20130228162145p:plain

2-2) 4096px/'bilnear'だとこんな感じ。画像解像度を上げると、同じカメラ距離でもブラーのかかりは弱くなる。
f:id:yasuda0404:20130228162219p:plain

KINET SDKを使う その2・スクリプト解析編

KINECT SDKを使う その1・環境設定編 - Unityな日々(Unity Geek)の続き

KinectInterop.cs
Kinectとのインターフェースを司どる最下層のスクリプト

public interfrace KinectInterface :Kinectとのインターフェース定義。実装はKinectSensor.csで定義される

KinectSensor.cs
Marshalクラスを用いてデータを取得している。たとえば、

ColorBuffer cb = (ColorBuffer)Marshal.PtrToStructure(buf.m_pBuffer,typeof(ColorBuffer));

は、MarshalクラスのPtrToStructureメソッドを用いて、ポインタ(buf.m_pBuffer)の示すバッファデータを、ColorBufferにマーシャリング(型変換)する。ColorBuffer構造体は、KinectInterop.csに定義されている。

C#はマネージ環境、一方Kinect DLL APIはアンマネージ環境で実装されているため、マーシャリングが必要となるらしい。マーシャリングについてはこのあたりを参考に。

Unity/KINECT SDKスクリプトの中で中心的役割を果たすSkeletonWrapperは、Kinect Prefabにアタッチされている。

Meshの三角ポリゴンを反転する

http://wiki.unity3d.com/index.php?title=ReverseNormals
http://forum.unity3d.com/threads/101018-How-do-I-get-the-normal-of-each-triangle-in-mesh

インポートした3DモデルのMeshを次のスクリプトで調べてみた。

print ("normals="+normals.Length+"  triangles="+triangles.Length+"  vertices="+verts.Length+" vertexCount="+mesh.vertexCount);

次のような出力が得られた

normals=1620 triangles=5691 vertices=1620 vertexCount=1620

これから推察するに、
vertices.Length と、vertexCountは同じようだ。

書式付で数値を文字に変換する

数値を文字に変換するのは、toString()関数だが、同時に書式も指定できる。
たとえば、小数点以下第一位まで表示した文字は、

数値変数.toString("#.0")

とする。

他にも多くの書式設定がある。
カスタム数値書式指定文字列


なお、toString()は、日付時刻の書式付変換でも使う。
bear.mini : [.NET] DateTime.ToString() 書式指定文字列 まとめ - カスタム DateTime 書式指定文字列編