Unity備忘録、STG制作3:弾の発射に関する一考察

※この記事は2016年当時に在籍していた某社内での新技術の開拓（自分はUnityを選択した）とモチベーションアップ目的で作成したものの...（以下略）。

#背景
Webブラウザ版(STG制作1を参照) では、敵弾の生成＆移動処理はこんな感じでやってました。

```BulletTest.cs
protected override void Move(){
	frame++;
	if (frame % interval == 0) {
		for (float i = 0f; i < 360f; i += 20) {
			GameObject clone = (GameObject)Instantiate (bullet, transform.position, Quaternion.identity);
			clone.GetComponent<Rigidbody2D>().velocity = GetVelocity (i, 3);
			}
		}

	}
}

protected Vector2 GetVelocity(float direction, float speed) {
	// Setting velocity.
	Vector2 v;
	v.x = Mathf.Cos (Mathf.Deg2Rad * direction) * speed;
	v.y = Mathf.Sin (Mathf.Deg2Rad * direction) * speed;
	return v;
}
```
Instantiate()で弾を生成し、Rigidbody2Dのvelocityに速度成分を与えることで、下記図のような感じの放射状にばら撒く弾幕が生成できます。
![01.png](https://qiita-image-store.s3.amazonaws.com/0/162164/8cabb61c-e826-2947-bea7-e8ec9857974a.png)


#Rigidbody2Dのvelocityを使った時の問題点
velocityに値を設定すると、Update関数で移動処理を実装しなくてもオブジェクトが勝手に移動するようになります。勝手に・・・、勝手に？
つまりフレーム更新間隔に関係なく、単位時間あたりの移動量を保証した動作になります。
なのでSTG制作2で挙げた問題が発生します。

#Update関数に移動処理を実装する
という訳なので、弾オブジェクトにUpdate関数を追加して移動処理を実装します。
移動処理は入門書でもお馴染みのtransform.Translate関数を使えばいいんじゃないですかね。
合わせてBulletTest.csも修正します。

```BulletMove.cs
void Update () {
	transform.Translate (velocity);
}

public void SetBulletVelocity(Vector2 vel){
	velocity = vel;
}
```

```BulletTest.cs
protected override void Move(){
	frame++;
	if (frame % interval == 0) {
		for (float i = 0f; i < 360f; i += 20) {
			GameObject clone = (GameObject)Instantiate (bullet, transform.position, Quaternion.identity);
			clone.GetComponent<BulletMove> ().SetBulletVelocity (GetVelocity (i, 3) / 60);  //60FPSで動作させるので60で割る
			}
		}

	}
}
```

#そして予期せぬ問題が起こった
静止画だと分かりづらいが、実行したら発射地点をグルグル回るようになりました。
![02.png](https://qiita-image-store.s3.amazonaws.com/0/162164/d8e518c9-913b-83eb-b51f-0eaf307878ae.png)


円運動するような処理を入れた覚えは無いのだが、思い当たる物が一点ありました。
弾の回転アニメーションをさせるために、下記のAnimatorコンポーネントを弾オブジェクトにアタッチしていました。
何でこれが悪さをするのかは分からないが、とりあえず無効にしてみたところグルグル回る現象は消えました。
![03.png](https://qiita-image-store.s3.amazonaws.com/0/162164/fc15420c-4fb8-fa4d-bcd4-114d4e60baf8.png)

うーん、Translateを使う場合はAnimatorを使ってはいけないのか？と、次は回転アニメーションもコーディングしてみました。

```BulletMove.cs
void Update () {
	transform.Translate (velocity);

	//弾を回転させる
	Quaternion rotation = new Quaternion ();
	rotation.eulerAngles = new Vector3 (0, 0, rotate);
	transform.rotation = rotation;
	rotate += 6f;
}
```

が、しかし結果はまたしてもグルグル現象が出てしまいました。

#解決策
まったく原因が分からなかったので、FBのUnity助け合い所に相談に乗ってもらったところ、
「Translateを使う場合は、ローカル座標系かワールド座標系を指定しなければいけない。」
と教えてもらいました。
なんだそれ？　自分の持ってる本にはそんなこと書いてなかったぞ？　ということでスクリプトリファレンスを確認。
https://docs.unity3d.com/ja/current/ScriptReference/Transform.Translate.html

どうやらデフォルトがローカル座標で、引数にSpace.Worldを指定するとワールド座標になるようです。
つまり、Translate以外の移動や回転動作はワールド座標系だったために、おかしな挙動になったようです。

そこで再度コードを修正。

```BulletMove.cs
void Update () {
	transform.Translate (velocity, Space.World);

	//弾を回転させる
	Quaternion rotation = new Quaternion ();
	rotation.eulerAngles = new Vector3 (0, 0, rotate);
	transform.rotation = rotation;
	rotate += 6f;
}
```

結果として、コードで回転させる場合も、Animatorで回転させる場合も意図通りの動作ができるようになりました。

#動作確認
スマホ実機で確認。左がvelocityの場合、右がTranslateの場合。
低負荷の場合は見た目は特に変わらず。
![04.png](https://qiita-image-store.s3.amazonaws.com/0/162164/2fa91997-5b0d-c991-1163-c2dd02177f2e.png)


高負荷になると結構違いが出てるのが分かります。
velocityの場合は弾の間隔が一定ではなくなっているため、弾密度が薄くなっています。
Translateの場合は弾は等間隔だが、弾オブジェクトが多いため、FPSがvelocityよりも低下しています。
![05.png](https://qiita-image-store.s3.amazonaws.com/0/162164/4510a9bb-fb6f-d556-ad54-ec365f898332.png)

以上