最近Unityを触り始め、さらにC#の勉強も同時にやっているので、色々知ったことを細々とまとめていきます。なので、随時更新していきます。
##Table of Contents
- transformのコントロール
- タイムステップによる操作
- 生成
- 取得・検索
- エディタ
- Leapmotionを使う
- デバッグ
- コリジョン(Collision)
- マウス操作
- スマホ関連
- 通信関連
- テクスチャ操作
- Tips
transformのコントロール
回転をQuaternionでがんばる
通常は transform.Rotate
などを使って回転させるのが早いですが、物理演算にまかせているオブジェクトを transform
から直接操作するのはあまりよくありません。
そこで利用するのが Rigidbody.MoveRotateion(quaternion)
です。
これは引数に渡した角度に「変更する」ものなので、徐々に回転、みたいな操作ができません。
なので、自分でQuaternionを計算してやる必要があります。
Quaternionのx, y, z, wのそれぞれの要素は角度がそのまま入っているわけではありません。
Quaternionの各要素は以下の構成になっています。
(x, y, z, w) = (x \sin(\frac{\theta}{2}}), y \sin(\frac{\theta}{2}}), z \sin(\frac{\theta}{2}}), \cos(\frac{\theta}{2}))
x, y, zはそれぞれ軸となるベクトルです。
それに、半分の角度の $sin$ を掛けたものがx, y, z、そして半分の角度の $cos$ がwになります。
ということで、前置きはこれくらいにして実際に計算すると以下のようになります。
float angleSpeed = 0.5f;
Quaternion rot = transform.rotation;
float angle = angleSpeed * Mathf.Deg2Rad;
float y = Mathf.Sin(angle * 0.5f);
float w = Mathf.Cos(angle * 0.5f);
rot *= new Quaternion (0f, y, 0f, w);
GetComponent<Rigidbody> ().MoveRotation (rot);
上の例では、Y軸だけ徐々に回転する、という操作です。
説明の通り、Y軸に対して半分の角度の値を与えているのが分かるかと思います。
あとは生成したクォータニオンを、もともとの rotation
に掛けてやることで目的の回転を表したクォータニオンを手に入れることができます。
それをあとは指定してやるだけです。
タイムステップによる操作
タイムステップの時間を得る
タイマーなどを自作する場合、今、開始からどれくらい時間が経ったのかを知りたいはずです。
その場合は以下のようにTime.deltaTime
を使うことで実現できます。
float timer;
float waitTime = 5000.0f;
void Update() {
timer += Time.deltaTime;
if (timer > waitTime) {
//do something.
}
}
###一定時間後に処理をする
void Start()
{
Invoke("MethodName", 5); //5秒後にMethodNameメソッドを実行する
}
###一定時間後にオブジェクトを削除する
弾丸とか、生成したあと消えてほしいオブジェクトがあると思います。
消えるタイミングが時間駆動の場合はDestroy
メソッドを使うことで実現できます。
void Start() {
Destroy(gameObject, 3); //3秒後にGameObjectを削除
}
###コルーチンを使った遅延処理
遅延処理など、時間の処理はコルーチンを使っても実現できます。
void Start() {
StartCoroutine(WaitSomething());
}
IEnumerator WaitSomething() {
yield return new WaitForSeconds(2.0f);
//do something.
}
##生成
###空のGameObjectを作る
単純にGameObjectクラスにnameを与えて生成するだけ。
GameObject emptyObj = new GameObject("object name");
###プリミティブなGameObjectを作る
専用のメソッドから作成する。
//Cubeオブジェクトを生成
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
PrimitiveTypeはenum型で、以下が定義されている。
public enum PrimitiveType
{
Sphere,
Capsule,
Cylinder,
Cube,
Plane,
Quad
}
prefabから複製を作る
[SerializeField]
private GameObject prefab;
void Start()
{
//InstantiateはObject型が返るので、GameObject型にキャストする
GameObject clone = Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
}
動的にMaterialを生成する
動的に生成する場合は、UnityEngine.Material
クラスを使う。
using UnityEngine;
// 中略
Material material = new Material(Shader.Find("Diffuse"));
material.color = Color.red;
Materialクラスの引数は、シェーダをstringで与える必要がある。
ただ、基本的なシェーダはいくつか準備されているようなので、Shaderクラスから探して設定してやることで生成できる。
Findメソッドに渡すテキストは、UnityのMaterialのInspectorで指定できるもの(と思われる)。
取得・検索
GameObjectのサイズを取得する
float width = gameObject.renderer.bounds.size.x;
renderer.bounds.sizeから取得する
###マテリアルなど、Unityのライブラリに入っているデータを取得する
例えば、動的にマテリアルを変更する場合。
GameObject obj = GameObject.Find("obj name");
Material material = Resources.Load("path/to/item") as Material;
obj.renderer.material = material;
path/to/item
の部分は、Assetsフォルダ内にResources
フォルダを作成し、その中に入っている要素への相対パスとなる。
仮に、HandPrimary
にアクセスする場合は
Material material = Resources.Load("Materials/HandPrimary") as Material;
となる。
###GameObjectにアタッチされたスクリプトへの参照を得る
参照を得たいケースとしては、別のGameObjectにアタッチされたスクリプト内で定義された変数を参照したい、という例。
static変数(グローバル変数)経由はNG。
取得方法は該当GameObjectを見つけ、さらにアタッチされたスクリプトのコンポーネントを取得することで参照を得る。C#で書くと以下。
GameObject targetObj = GameObject.Find("someObjectName");
TargetScriptName script = targetObj.GetComponent<TargetScriptName>();
Debug.Log(script.targetVariable); //=>なにがしかの値
ここで、TargetScriptName型
はスクリプトの名前。
具体的にはファイル名。(多分)
もし「HogeScript.cs」がファイル名だとしたらHogeScript script = …
という書き方になる。
詳細は以下を参照
必見!Unity初心者が学ぶ「別スクリプトの変数やメソッドへの参照」
###GameObjectの階層構造を検索して該当オブジェクトを得る
上記のような構造になっている場合、rootにスクリプトがアタッチされているとすると、
Transform mixTransform = transform.Find("body/arm");
とすることで、armオブジェクトを取得することができる。
###FindChildメソッドを使って子のGameObjectを得る
//子供のGameObjectのparticle systemを取得する例
ParticleSystem particle = transform.FindChild("ChildGameObjectName").GetComponent<ParticleSystem>();
##エディタ
###private変数をエディタから操作する
通常、publicにするとエディタから編集することができる。
しかし、プログラム上はやはりprivateにしたいときもある。
そんなときは属性
を使って、以下のようにすることでエディタから編集させつつ、privateなメンバを宣言できる。
[SerializeField]
private float hoge = 1;
##Leapmotionを使う
UnityのFree版でLeapmotionの開発をしていると、dllが読み込めない?ため、通常のPlayモードではライブラリが読み込めず動作しない。
以下の記事を参考に色々やってみることでPlayモードでも指の検出ができるようになった。
http://pierresemaan.com/getting-the-leap-to-work-with-unity-free-version-not-pro/
Leapmotionのサンプルに入っているUnitySandboxの中身を解説した記事。色々参考になります。
http://d.hatena.ne.jp/hecomi/20130723/1374598279
##デバッグ
ブレークポイントなどで停止させるには、MonoDevelop側からUnityを起動しないとならないらしい。
MonoDevelopを起動し、Unityを閉じたら「実行>デバッグ」を実行するとUnityが立ち上がりブレークポイントでブレークするようになる。
##コリジョン(Collision)
###OnCollisionEnterを利用する
Collisionの検出には、ColliderとRigidbodyのコンポーネントをそれぞれ検出させたいもの同士に追加しないと検出されない。参考:
http://gamesonytablet.blogspot.jp/2012/11/unityontriggeroncollision.html
##マウス操作
###マウスの当たり判定
マウスと該当オブジェクトとの衝突検出。
考え方は2次元に存在するマウスを3D空間に逆投影変換する。
まず、マウスの位置からカメラ視点方向に向かって真っ直ぐな光線を飛ばし(概念的な意味で)、その光線と接触しているかを判定することで行う。
計算方法など詳しくはこちらを参考に→ その48 スクリーン座標でワールド空間の地面を指す
Unityではそのへんのめんどくさいことをやってくれるメソッドがあるので、以下のようにして判定できる。
void Update () {
Ray ray;
RaycastHit hit;
float distance = 1000.0f; //光線を伸ばす距離
//メインカメラのスクリーン上のポイントを光線に変換
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//光線がhitしているオブジェクトがあるかチェック
//もし該当のオブジェクトがあればhitに格納される
if (Physics.Raycast(ray, out hit, distance))
{
//該当オブジェクトの判別にtagを利用。ここはやりたいことに応じて適宜変更する。
if (hit.collider.gameObject.tag == "Touchable")
{
selectedObject = hit.collider.gameObject;
}
else
{
selectedObject = null;
}
}
else
{
selectedObject = null;
}
}
###クリックされた位置を取る
本を参考にさせてもらった、マウスの位置に応じてオブジェクトを移動する処理のサンプル。
public bool UnprojectMousePosition(out Vector3 world_position, Vector3 mouse_position)
{
bool ret;
float depth;
//チェック用のPlaneオブジェクトを生成する
Plane plane = new Plane(Vector3.up, new Vector3(0.0f, transform.position.y, 0.0f));
//当たり判定用のRay(光線)を生成
Ray ray = Camera.main.ScreenPointToRay(mouse_position);
//planeとの当たり判定をチェック
if (plane.Raycast(ray, out depth))
{
//マウスがあたっていたら位置を補正してその値を返す
world_position = ray.origin + ray.direction * depth;
ret = true;
}
else
{
world_position = Vector3.zero;
ret = false;
}
return ret;
}
###マウスの位置にオブジェクトを移動する
カメラのScreenToWorldPoint
を使えば比較的簡単に実装できるよう。
public void MoveToMouseposition()
{
//マウスの位置を取得
Vector3 screen_point = Input.mousePosition;
//マウス位置のZ座標を2.0fに設定
screen_point.z = 2.0f;
//上記マウス位置を元に、ワールド空間の位置を取得
Vector3 world_point = Camera.main.ScreenToWorldPoint(screen_point);
//取得した位置をGameObjectのpositionに設定する
transform.position = world_point;
}
ポイントは、マウス位置に明示的にZ座標の値を設定しているところ。
元々スクリーン上のマウス座標はZ軸の値を持っていないので(2次元なので)、表示したい位置を明示的に指定してやる必要がある。
カメラの位置から見てなので、基本的にマイナス方向に値を設定すると画面内に映らなくなるので注意。
##スマホ関連
###スマホのジャイロセンサにアクセス
Quaternion gyro = Input.gyro.attitude;
で取れるらしい。まだ使ったことないけどいずれ使いそうなのでメモw
##通信関連
###HTTP通信を利用する
HTTP通信は、UnityEngine.WWW
クラスを使う。
private string res;
// Use this for initialization
void Start () {
StartCoroutine (Download());
}
IEnumerator Download()
{
WWW www = new WWW("http://css-eblog.com/");
yield return www;
res = "";
Debug.Log(www.text);
}
読み込みは非同期になるため、コルーチンを利用する必要がある。
##テクスチャ操作
###テクスチャでスプライトアニメーション(の準備)
テクスチャのオフセットをいじるには、renderer.material.mainTextureOffst
をいじる。
あるいはrenderer.material.SetTextureOffst("_MainTex", offset)
のようにしてもいい。
ここで_MainTex
はUnityで作成したマテリアルが自動で生成するシェーダ内に定義されているテクスチャの変数ぽい。
これに値を設定することで、アニメーションを実現している。
using UnityEngine;
using System.Collections;
public class SpriteAnimScript : MonoBehaviour {
private Material material;
private float offsetX = 0.0f;
private float offsetSpeed = 0.1f;
// Use this for initialization
void Start () {
this.material = renderer.material;
}
// Update is called once per frame
void Update () {
offsetX += offsetSpeed * Time.deltaTime;
Vector2 offset = new Vector2(offsetX, 0.0f);
this.material.SetTextureOffset("_MainTex", offset);
//こっちでも動く
//this.material.mainTextureOffset = offset;
}
}
※offsetの値は整数を与えると100%の移動になるので画面上はまったく動いていないように見えるので注意。UV座標かな。
こちらの公式Wikiを参考にしました → Animating Tiled texture
##Tips
###通常のPlaneをBillboardにする
パーティクルシステムはビルボードになっているので常にカメラ方向に面を向けてくれます。
しかしどうやらUnityにはデフォルトでビルボードを実行する機能がないらしいので、自前で書く必要がある。
コード自体は非常にシンプルで、以下のものをビルボード化したいオブジェクトにアタッチするだけ。
Camera m_Camera = Camera.main;
//以下でrotationを常にカメラ方向に向ける
this.transform.LookAt (this.transform.position + m_Camera.transform.rotation * Vector3.back, m_Camera.transform.rotation * Vector3.up * 270);
//Planeの場合、デフォルトの回転は面がup方向を向いているので追加でX軸に90度回転させてやる
this.transform.Rotate(90, 0, 0);
##役立ちリンク