Rigidbodyをスクリプトで操作してみる


## 前提条件

シーン内にCubeを作りカメラから見える位置に置く。背景とかライトは適宜。

![設定例](https://www.evernote.com/shard/s22/sh/8a56e363-0389-4c00-8db4-23b4a8734c1d/7daec7ade13a84441f72a39bab1d278b/deep/0/Main.unity---OkayamaWalk---PC,-Mac-&-Linux-Standalone.png)


### Quaternionで毎フレームごとに回転

<code>Quaternion.Euler</code>に適当な数値を与えるとx,y,zのそれぞれの方向に回転させたrotation情報を返してくれる。<code>Time.time</code>は時間経過と共にカウントアップされる標準のカウンタ。

- この例ではRigidbodyは使っていないのでRigidbodyコンポーネントが設定されていなくても影響なし。
- Rigidbodyが付いているならuse gravityのチェックを外しておくと回転を見守りやすい。
- transform.rotation.xなどを地道に加算してもいいが面倒くさい。

```
#pragma strict

var step:int = 10;

function Start () {

}

function Update () {
    transform.rotation = Quaternion.Euler(
    	Time.time * this.step,
    	Time.time * this.step,
    	0
    );

}
```

### Rigidbody.AddForceで飛ばす

<code>Vector3.forward</code>は実際には(0,0,-1)のVectorになっていて奥を表現する。速度を考慮したい時は任意の数値を掛け算するとより加速する。
<code>ForceMode.VelocityChange</code>は質量を無視して加速させる。

```
#pragma strict

function Start () {

	rigidbody.AddForce(
		Vector3.up * 10,
		ForceMode.VelocityChange
	);

}

function Update () {

}
```

### Jumpボタンを押した時に飛ばす

Input.GetButtonDownでキーボードやゲームパッドを抽象化して扱う。GetButtonにすると押しっぱなしで連続的に判定される。
<code>Jump</code>は頭文字が大文字なっていて標準ではスペースキー。

```
#pragma strict

function Start () {

}

function Update () {

	if (Input.GetButtonDown('Jump')) {
		rigidbody.AddForce(
			Vector3.up * 10,
			ForceMode.VelocityChange
		);	
	}

}
```

### Y以外の座標を固定する

インスペクタからFreeze Postionの各項目にチェックを付けるのと一緒。

```
#pragma strict

function Start () {

	rigidbody.constraints = (
	   RigidbodyConstraints.FreezePositionX |
	   RigidbodyConstraints.FreezePositionZ
	);

}

function Update () {

	if (Input.GetButtonDown('Jump')) {
		rigidbody.AddForce(
			Vector3.up * 10,
			ForceMode.VelocityChange
		);	
	}

}
```

### 前方に適当なオブジェクトを出現させ、飛ばす

```
#pragma strict

var bullet:GameObject;

function Start () {

    rigidbody.constraints = (
       RigidbodyConstraints.FreezePositionX |
       RigidbodyConstraints.FreezePositionZ
    );

}

function Update () {

    if (Input.GetButtonDown('Jump')) {
    	var new_ballet = Instantiate(
    		bullet,
    		transform.position + Vector3.forward,
    		Quaternion.identity
    	);
        new_ballet.rigidbody.AddForce(
            Vector3.forward * 30 + Vector3.up * 10,
            ForceMode.VelocityChange
        );  
    }

}
```

### 飛ばしたオブジェクトを弾ませてみる

Phisics Materialを作ってパラメーターを設定、飛ばすオブジェクトのコライダーにドラッグしてアタッチ。

- Dynamic Friction
  - 動摩擦係数、移動から停止が早くなる。
- Static Friction
  - 静止摩擦係数、動き出すのに必要な力が大きくなる。
- Bounciness
  - 跳ね返り度合い。1で損失なし。
- Friction Combine Mode
  - 異なるオブジェクトの摩擦をどう結合するか、平均化、最小化、最大化、乗算
- Bounce Combine
  - 異なるオブジェクトの跳ね返り度合いをどう結合するか。

### キーを押した方向に射出する

正面以外にも飛ばせるようにする。<code>Input.GetAxis</code>の値をrotationに反映させて砲台を回転させる。それだけでは大勢に影響がないが、射出時に指定するrotationに砲台のrotationを使い、AddRelativeForceを使うことで弾丸の向きに向かって力を加えることができる。

```
#pragma strict

var bullet:GameObject;

function Start () {

    rigidbody.constraints = (
       RigidbodyConstraints.FreezePositionX |
       RigidbodyConstraints.FreezePositionZ
    );

}

function Update () {

    if (Input.GetButtonDown('Jump')) {
    	var new_ballet = Instantiate(
    		bullet,
    		transform.position + Vector3.forward,
    		transform.rotation
    	);
        new_ballet.rigidbody.AddRelativeForce(
            Vector3.forward * 30 + Vector3.up * 10,
            ForceMode.VelocityChange
        );  
    }
    
    transform.rotation = Quaternion.Euler(
    	0,
    	transform.rotation.y + Input.GetAxis('Horizontal') * 20,
    	0
    );

}
```

### 奈落に落ちた弾を消す

弾に対してスクリプトを追加。Y座標が低くなったら消滅させる。

```
#pragma strict

var limit_y:float = -20;

function Start () {

}

function Update () {
	 
	if (transform.position.y < this.limit_y) {
		Destroy(gameObject);
	}

}
```

### 射出した弾と標的のヒット判定をする

ヒット判定を行うには標的になるオブジェクトにもrigidbodyを設定してから、下記のスクリプトを設定する。rigidbodyが設定されていないと衝突があっても<code>OnCollisionEnter</code>などが実行されない。

標的になるオブジェクトは落下したりはじけ飛んでも困るので下記の設定をする。

- Use Gravityのチェックを外す(落下しなくなる)
- Is Kinematicのチェックを付ける(衝突によって移動や回転がおきなくなる)

当たるとコンソールにログが出る。


```
#pragma strict

function Start () {

}

function Update () {

}

function OnCollisionEnter(collision : Collision) {
	Debug.Log('Hit');
}
```

https://www.youtube.com/watch?v=3OleT8ZRYqc

### 標的にヒットした弾を消す

ヒットしてきたオブジェクトはCollisionから取得できる。
衝突した全てのオブジェクトが入ってくるのでオブジェクトに設定したタグを判定して目的のオブジェクトに対して処理を行う。

タグはインスペクタで設定しておく。クローン元かプレハブに設定すると楽。

![tag](https://www.evernote.com/shard/s22/sh/f3d57a98-7c4a-46f4-b489-b29a018e3da2/73fed3a7cafc8e32fa6dca662d0b1d74/deep/0/Main.unity---OkayamaWalk---Web-Player.png)


```
#pragma strict

function Start () {

}

function Update () {

}

function OnCollisionEnter(collision : Collision) {

	if (collision.gameObject.CompareTag('bullet')) {
		Destroy(collision.gameObject);
	}
	
}
```

### 標的を前進させ、ヒット時に戻す

ゲームっぽくするために標的を動かす。
弾に当たった場合はしばらくの間、上空に移動し、元の位置まで落下してからスタート。

```
#pragma strict

	var speed:float = 0.1;
	var initial_z = -10;

function Start () {

}

function Update () {

	if (transform.position.y >= 2) {
		transform.position += Vector3.down * this.speed;
		return;
	}
	transform.position += Vector3.back * this.speed;
	if (transform.position.z < -40) {
		transform.position.z = this.initial_z;
	}

}

function OnCollisionEnter(collision : Collision) {

	if (collision.gameObject.CompareTag('bullet')) {
		Destroy(collision.gameObject);
		transform.position.z = this.initial_z;
		transform.position.y = Random.Range(2,40);
		this.speed = Random.Range(0.1, 0.7);
	}
	
}
```

### 得点を付ける

敵を倒した時に点数を加える。

スコア表示用の3DTextを作成し、スクリプトを付加する。

```
#pragma strict

var score:int;
var textmesh:TextMesh;
	
function Start () {
	this.textmesh = this.GetComponent('TextMesh');
}

function Update () {
	this.textmesh.text = this.score.ToString('00000000000');
}

function CountScore (delta:float) {

	this.score += delta;

}
```

CountScoreを呼び出せばスコアを加算し、その内容が反映される。

標的のスクリプトからはスコア表示オブジェクトをタグ経由で取得し、SendMessageで処理を呼び出す。

```
#pragma strict

	var speed:float = 0.1;
	var initial_z = -10;
	var score_object:GameObject;

function Start () {
	this.score_object = GameObject.FindGameObjectWithTag('score');
}

function Update () {

	if (transform.position.y >= 2) {
		transform.position += Vector3.down * this.speed;
		return;
	}
	transform.position += Vector3.back * this.speed;
	if (transform.position.z < -40) {
		transform.position.z = this.initial_z;
	}

}

function OnCollisionEnter(collision : Collision) {

	if (collision.gameObject.CompareTag('bullet')) {
		Destroy(collision.gameObject);
		transform.position.z = this.initial_z;
		transform.position.y = Random.Range(2,40);
		this.speed = Random.Range(0.1, 0.7);
		
		score_object.SendMessage('CountScore', this.speed * 100);
	}
	
}
```