Unityで斜方投射の物理実験（3/3）球の斜方投射

# きっかけ
Unityで物理シミュレーションができることを知ったので挑戦してみることに。
実験するにあたってUnityの基本的なことにも触れられたし、
やったこと忘れそうなのでメモ残しておくために投稿

# 環境
Unity 2019.1.10f1 (64-bit)

# 世話になったサイト
　このサイトの説明が超丁寧だったのですごい分かりやすかった
　https://ekulabo.com/tutorial-input

# 手順（概要）
そもそもUnity素人故、段階を分けて試す
1. Unityを起動する　　　　　※済
2. 球の自然落下の物理実験　　※済
3. 球の跳ね返りの物理実験　　※済
4. 球の斜方投射の物理実験　　←イマココ

# 4. 球の斜方投射の物理実験
配置決めがつらかったので参考サイトをそのまま終了した。
変更箇所のみ記載。あとはデフォルト。
まずはMainCamera
　Position：15, 4, -20
![コメント 2019-07-20 225147.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/89965c2e-ea4b-75f3-45b8-b6a847dd1db1.gif)

　Sphere
　position：20, 0.5, 0
![コメント 2019-07-20 225159.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/777d7d93-eb07-9d3c-d976-8c2fe39d0e96.gif)

　Plane
　position：35, 0, 0
　Scale：4, 1, 1
![コメント 2019-07-20 225212.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/95d743ec-f3ba-20b2-b36e-02f3cc5ccc03.gif)

# ここからが本題（斜方投射スクリプトの作成）
自然落下や跳ね返り（紹介していないけど摩擦など）は既存のコンポーネントが用意されているけど
斜方投射をするために角度決めたりどんだけの強さだったりするどうやって力を与えるのか？については
UnityがAPIを用意しているのでそれを使ってコンポーネントを自作するようだ。

ついにか…と同時にここまではノンプログラミングで作れるUnityのすごさに驚かされた。
さて、作り方は…ちょっと参考サイトの手順だと微妙にうまくいかなかったので少し自己流。

Sphere選択 > [Add Component] > NewScriptクリック
Scriptの名前を決める。名前は「SphereBooster」にした。
![コメント 2019-07-20 230048.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/6740c76c-da0e-c08c-3c2e-e1c6fb0e7b71.gif)

…と同時にVBcodeが起動。
あらかじめテンプレートが用意されている。。。なんて優しいんだ。。。

補足すると
　void Start()は、再生ボタンクリック時に実行する関数
　void Update()は、なんらかのアクションを行う際に実行される関数
![コメント 2019-07-20 225610.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/97590ca7-e3dc-102b-39d6-80c71c8d0da0.gif)

void Start()に以下のコードを記入。
再生ボタンクリックと同時に、X軸から45度の向きに球を飛ばす想定。

```
	void Start () {
		// 力を加える向きをVector3型で定義
		// 今回はX軸から45度の向きに射出するため、XとYを1:1にする
		Vector3 forceDirection = new Vector3(1.0f, 1.0f, 0f);

		// 上の向きに加わる力の大きさを定義
		float forceMagnitude = 10.0f;

		// 向きと大きさからSphereに加わる力を計算する
		Vector3 force = forceMagnitude * forceDirection;

		// SphereオブジェクトのRigidbodyコンポーネントへの参照を取得
		Rigidbody rb = gameObject.GetComponent<Rigidbody>();
		
		// 力を加えるメソッド
		// ForceMode.Impulseは撃力
		rb.AddForce(force, ForceMode.Impulse);
	}
```
![コメント 2019-07-20 225631.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/5b4c8bc7-8155-05b1-b566-e4e4c45a5c6c.gif)

毎度のことながら紹介できないが、再生ボタンクリックしたら確かに飛んでった。そして、planeに着地してそのまま転がり奈落へ…

続いて、さらに改造。今度はクリックしたら飛んでいくように改造…の前に
クリックしたら何かアクションするってことが出来るようにする。
編集は、「SphereBooster」右クリック　> Open C#Project
![コメント 2019-07-20 230227.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/c165c6e7-e066-c1c2-9369-828bc49444f3.gif)

今度はUpdate()を編集。
if (Input.GetKeyUp(KeyCode.Space))｛｝
だけで、イベント起動できるのか～。。。楽だなぁ。
今回の実装だと、スペースキーを押すと、ログを吐く想定。

```
	void Update () {
		// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
		// KeyCode.Spaceはスペースキーを表す
		if (Input.GetKeyUp(KeyCode.Space)){
			// Debug.LogでUnityのコンソールに出力できる
			Debug.Log("スペースキーが押されたよ。");
		}
```

![コメント 2019-07-20 230712.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/2eb8e014-7392-70ff-0a08-d2736d636799.gif)

気づいてみたら、
SphereのInspectorに「SphereBooster(Script)」の欄が出来てた。
なるほど、、、こんな感じで出てくるのね。

![コメント 2019-07-20 230514.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/936d40ac-3a7a-404a-1097-c3a41b5ce297.gif)

画像残してないけど、再生ボタンを押すと
ぱっと見さっきと変わらず斜方投射してるけど、
スペースを押すたびに、画面のConsoleに「スペースキーが押されたよ。」が出るようになった。


![コメント 2019-07-20 230817.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/90df0bed-2a9e-56c1-7ede-62c1bd12739f.gif)

ここからは。。。なぜか画像が残ってないが、ソースだけ残す。
Start()の斜方投射のコードをさっきのスペースおしたときのif文の中に差し込んだのが↓↓
やってみると、スペースが押されるたびに、斜方投射していく感じになった。
なるほど、、、押すたびに飛んでいくので2段ジャンプ3段ジャンプしてるみたいな感じになった。

'''
public class SphereBooster : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
		// KeyCode.Spaceはスペースキーを表す
		if (Input.GetKeyUp(KeyCode.Space)){
			// ボールを飛ばす処理をStart()からここに移動させる

			// 力を加える方向
			Vector3 forceDirection = new Vector3(1.0f, 1.0f, 0f);

			// 加える力の大きさ
			float forceMagnitude = 10.0f;

			// 向きと力の計算
			Vector3 force = forceMagnitude * forceDirection;

			// SphereオブジェクトのRigidbodyコンポーネントへの参照を取得
			Rigidbody rb = gameObject.GetComponent<Rigidbody>();

			// 力を加えるメソッド
			rb.AddForce(force, ForceMode.Impulse);
		}
	}
}
'''

…で、参考サイトではほかにもこのスクリプトにもろもろ問題があるようで最終的に全て直したコードがコレ。

```
public class SphereBooster : MonoBehaviour {

	// 飛行中フラグ
	bool isFlying = false;

	// ボタン押下フラグ
	bool isBoostPressed = false;

	// Sphereオブジェクトの初期位置格納用ベクトル
	Vector3 initPosition = Vector3.zero;

	void Start(){
		initPosition = gameObject.transform.position;
	}
	
	void Update(){
		// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
		if (Input.GetKeyUp(KeyCode.Space)){
			isBoostPressed = true;
		}
	}

	void FixedUpdate(){
		if (isBoostPressed){
			if (isFlying){
				// 飛行中の処理

				// 運動の停止
				Rigidbody rb = gameObject.GetComponent<Rigidbody>();
				rb.velocity = Vector3.zero;
				rb.angularVelocity = Vector3.zero;

				// 初期位置に移動させる
				gameObject.transform.position = initPosition;
			} else {
				// ボールを飛ばす処理

				// 力を加える方向
				Vector3 forceDirection = new Vector3(1.0f, 1.0f, 0f);

				// 加える力の大きさ
				float forceMagnitude = 10.0f;

				// 向きと力の計算
				Vector3 force = forceMagnitude * forceDirection;

				// 力を加えるメソッド
				Rigidbody rb = gameObject.GetComponent<Rigidbody>();
				rb.AddForce(force, ForceMode.Impulse);
			}
			// 飛行中フラグの切り替え
			isFlying = !isFlying;

			// どちらの処理をしてもボタン押下フラグをfalseに
			isBoostPressed = false;
		}
	}
}
```

試してみると、確かに色々扱いやすくなった。
スペース押したら斜方投射して、再度スペース押すと初期の位置に球が戻って再度斜方投射。
とてもよくできた、、、物理シミュレーション。。。

# おわりに
C#は初めて触ったしAPIをまともに調べていないから
ほとんど写経のような気持ちで作ってみたおかげもあるが
かなり楽に作れた。とてもやりやすい！

～おしまい～