今回はカメラのレンズの中心からマウスクリックした位置に弾を飛ばす機能を作成します。
以前作成した

では銃口の先に弾を飛ばすという処理をやりましたが、それを少し変更する感じですね。
↑のような機能を作成していきます。
カメラのレンズが始まる位置から弾を飛ばすようにしています。
↑のサンプルでは重力を働かせていませんが、重力を働かせた場合のサンプルも作成していきます。
飛ばす弾の作成
まずは飛ばす弾のプレハブを作成していきます。
ヒエラルキー上で右クリック→3D Object→Sphereを選択し、名前をBulletとします。
↑のようにScaleの調整をし、Rigidbodyコンポーネントを取り付けてUse Gravityのチェックを外し重力を働かせないようにします。
BulletTestスクリプトを作成し取り付けます。
BulletTestスクリプトはこの後作成します。
弾に力を加えるBulletTestスクリプト
BulletTestは弾が存在している間はRigidbodyに力を加えて飛ばす処理を記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | using UnityEngine; using System.Collections; public class BulletTest : MonoBehaviour { [SerializeField] private float power = 50f; [SerializeField] private float deleteTime = 10f; private Rigidbody rigid; private Ray ray; void Awake() { // Rigidbodyを取得し速度を0に初期化 rigid = GetComponent <Rigidbody> (); } // 弾がアクティブになった時 void OnEnable() { // カメラからクリックした位置にレイを飛ばす ray = Camera.main.ScreenPointToRay (Input.mousePosition); // 弾を発射してから指定した時間が経過したら自動で削除 Destroy (this.gameObject, deleteTime); } void OnCollisionEnter(Collision col) { // Enemyタグがついた敵に衝突したら自身と敵を削除 if (col.gameObject.CompareTag ("Enemy")) { Destroy (this.gameObject); Destroy (col.gameObject); } } // 弾が存在していればレイの方向に力を加える void FixedUpdate() { rigid.AddForce (ray.direction * power, ForceMode.Force); } } |
Awakeで自身に設定されているRigidbodyコンポーネントを取得します
OnEnableメソッドはゲームオブジェクトがアクティブになった時に呼ばれるメソッドです。
Camera.main.ScreenPointToRay(Input.mousePosition)でカメラからマウスの位置までレイを飛ばしそれをrayに保持しておきます。
弾のゲームオブジェクトがアクティブになってからdeleteTimeで指定した時間が経ったらDestroyで弾のゲームオブジェクトを削除します。
OnCollisionEnterは弾に設定しているコライダが他のゲームオブジェクトと衝突した時に呼ばれるので、その中でEnemyタグを設定したゲームオブジェクトと衝突した時は、
弾自身のゲームオブジェクトと当たった相手のゲームオブジェクトを削除しています。
FixedUpdateメソッドは固定フレームレートで呼ばれるメソッドで剛体(Rigidbody等の物理的な挙動)を使う時はこの中で処理をするといいみたいです。
そんなわけでray.directionでカメラの位置からマウスの位置の方向を取得し、そこに力を加えています。
FixedUpdateが呼ばれる度にAddForceで弾に力を加えているので、RigidbodyのUse Gravityにチェックを入れても効果の程が解り辛いです。
そんな時はFixedUpdateで毎回力を加えるのではなく最初に強い力を加えてFixedUpdate処理をしないようにします。
そこら辺の処理は最後にやりたいと思います。
これで弾が出来たのでAssetsフォルダにドラッグ&ドロップしてプレハブにします。
敵のゲームオブジェクトの作成
次に敵のゲームオブジェクトを作成します。
今回は弾が物理的に当たるようにし、OnCollisionEnterで判定しているので、衝突対象も物理的に当たるようにします。
ヒエラルキー上で右クリック→3D Object→Cubeを選択し名前をEnemyとします。
インスペクタのTagに新しくEnemyタグを作成し設定します。
上のTagの部分をEnemyにします。
出来たらCtrl+Dキーでコピーして位置を変えたりしていくつか設置しておきます。
MainCameraに弾を発射するスクリプトを取り付ける
次にカメラから弾のプレハブをインスタンス化して弾を発射させるので、新しくShotTestスクリプトを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using UnityEngine; using System.Collections; public class ShotTest : MonoBehaviour { // 弾のプレハブ [SerializeField] private GameObject bullet; // レンズからのオフセット値 [SerializeField] private float offset; // Update is called once per frame void Update () { if (Input.GetButton ("Fire1")) { // カメラのレンズの中心を求める var centerOfLens = Camera.main.ViewportToWorldPoint (new Vector3(0.5f, 0.5f, Camera.main.nearClipPlane + offset)); // カメラのレンズの中心から弾を飛ばす var bulletObj = Instantiate (bullet, centerOfLens, Quaternion.identity) as GameObject; } } } |
インスペクタでbulletに弾のプレハブを設定します。
offsetはカメラレンズからのオフセット値で、カメラレンズと同じ位置から弾を飛ばすと弾のゲームオブジェクトの中身が表示されてしまうので、それを回避する為に設定します。
今回はZ軸の値のみ変更する為、floatにしてますが、Vector3型にして上下左右のオフセット値を指定する事も出来ます。
Input.GetButton(“Fire1”)でマウスの左ボタンを押している間はメインカメラのビューポート(カメラの表示領域の横と縦を0~1の割合で現したもの)の中心+オフセット値を取得し、
Instantiateで弾の位置をこのレンズの中心+オフセット値を指定します。
ShotTestは
↑のように設定しました。
弾の衝突設定を変更する
ShotTestスクリプトでInput.GetButtonで処理をする為、ボタンを押している間ずっと弾をインスタンス化しています。
その為、弾同士がぶつかって跳ね返ってしまいます。
そこで、弾同士は衝突判定をしないように設定します。
UnityメニューのEdit→Project Settings→Physicsを選択します。
↑のようにPhysicsのLayer Collision MatrixでBullet同士のチェックを外し、お互いが衝突しないようにします。
弾を撃ってからのフリーズ時間を作る
現時点では弾はUpdateの度に実行されていますが、あまりに弾が連続して発射されても困るので1回弾を発射してから次の弾を発射するまでに一定時間が必要となるようにします。
ShotTestに処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 弾を飛ばす間隔時間 [SerializeField] private float waitTime = 0.1f; // 経過時間 private float elapsedTime = 0f; void Update () { elapsedTime += Time.deltaTime; if (elapsedTime < waitTime) { return; } if (Input.GetButton ("Fire1")) { // カメラのレンズの中心を求める var centerOfLens = Camera.main.ViewportToWorldPoint (new Vector3(0.5f, 0.5f, Camera.main.nearClipPlane + offset)); // カメラのレンズの中心から弾を飛ばす var bulletObj = Instantiate (bullet, centerOfLens, Quaternion.identity) as GameObject; elapsedTime = 0f; } } |
弾を飛ばしてからの時間(elapsedTime)が待ち時間(waitTime)を超えていなければその後の処理をしないようにします。
こうすることで弾を飛ばす間隔が広くなります。
弾に重力を働かせる
弾に取り付けたRigidbodyのUse Gravityにチェックを入れ弾に重力を働かせるようにして確認してみます。
しかしBulletTestスクリプトのFixedUpdateメソッドで毎回力を加えているので重力の効果が解り辛いです。
そこで力を加えるのは最初に1回だけにする事にしましょう。
BulletTestスクリプトを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | using UnityEngine; using System.Collections; public class BulletTest : MonoBehaviour { [SerializeField] private float power = 50f; [SerializeField] private float deleteTime = 10f; private Rigidbody rigid; private Ray ray; void Awake() { // Rigidbodyを取得し速度を0に初期化 rigid = GetComponent <Rigidbody> (); } // 弾がアクティブになった時 void OnEnable() { // カメラからクリックした位置にレイを飛ばす ray = Camera.main.ScreenPointToRay (Input.mousePosition); rigid.AddForce (ray.direction * power, ForceMode.Force); // 弾を発射してから指定した時間が経過したら自動で削除 Destroy (this.gameObject, deleteTime); } void OnCollisionEnter(Collision col) { // Enemyタグがついた敵に衝突したら自身と敵を削除 if (col.gameObject.CompareTag ("Enemy")) { Destroy (this.gameObject); Destroy (col.gameObject); } } } |
FixedUpdateメソッドを削除し、OnEnableメソッド内で力を1回加えます。
力を加えるのが1回だけになったので、BulletTestで設定するpowerを上げます。
それでは確認してみましょう。
↑のようになりました。
弾に与える力の調整をする事で、放物線を描いて飛ぶ弾にすればちょっとした的当てゲームの作成も出来そうですね。(^^)/
終わりに
今回の処理では弾を飛ばす度に弾のプレハブをインスタンス化していますが、弾をあらかじめインスタンス化しておき、それを再利用(プール)する方法もあります。
毎回弾をインスタンス化したり削除したりといった事をしないので、メモリの読み書きの節約やガベージコレクションの発生が抑えられます(らしいです)。
それは次回以降やってみたいと思います。