【Unity 入門】《第1回》大砲で弾を撃つ|シューティングゲームを作ろう!
Unity入門のゲーム作成チュートリアル第4弾の「城防衛シューティングゲーム」です!
襲来してくる敵を大砲で迎撃し、城を守りきることができれば勝利というゲームを作成していきます。
※そのほかのゲーム作成記事に関する情報は以下記事をご参考ください。
参考記事)Unity入門オリジナルチュートリアル集
第1回の今回は「大砲で弾を撃つ」方法をご紹介します。
作成する大砲は1.カメラと連動して動き(一人称視点)、2.特定キーを押すことで弾を発射するという仕組みです。
準備(フォルダ構成)
フォルダ構成
まずはじめにファイルを整理するためにフォルダを準備します。
後々フォルダを追加する可能性もありますが、準備段階では基本的な要素を保存しておくためのフォルダを作成します。
Assets下に以下のフォルダを作成してください。
- Assets
- Fonts・・・フォント
- Images・・・画像
- Items・・・アセットストアで取得したアセット
- Materials・・・マテリアル
- Prefabs・・・プレハブ
- Scenes・・・シーン
- Scripts・・・C#スクリプト
このようにそれぞれのファイルを種類別に保存しておくことで開発環境を整えることができます。
また、保存にはゲーム要素ごとにフォルダを作成することもできます。
例えば今回の例を用いると、大砲関連のファイルを保存しておくための「Cannon」フォルダ、敵関連のファイルを保存するための「Enemy」フォルダ、フィールド情報を保存するための「Field」フォルダ・・・などのようにゲーム要素ごとにファイルを整理する方法もあります。
今回は種類別に保存する方法を採用しますが、ゲーム要素ごとの保存方法を利用していただいても全く問題ありません。
カメラと連動した動きを実現する
いよいよここからゲーム作成に入ります!
まずは大砲とカメラの動きを連動させます。
以下のような流れでこの機能を実装していきます。
- カメラを矢印キー入力で360度向きが変わるようにする
- カメラの向きと大砲の動きを連動させる(親子オブジェクト)
準備(大砲をアセットストアからダウンロードする)
この章はアセットをダウンロードしてくるだけですので手短に説明します。
まずはアセットストアで「Victorian PBR Cannon」と検索して以下のアセットをダウンロード・インポートしてください。
このように無料でありながらクオリティの高いアセットがストアには多数あります。自作する前に一度アセットストアを探してみるのも良いかもしれません。
参考サイト)Unity Asset Store
インポートしたフォルダはItemsフォルダに移動しておきましょう。
カメラを矢印キー入力で360度向きが変わるようにする
準備
まずはじめにカメラのポジションを全て0(ゼロ)に変更します。
次にカメラが回転していることを把握するために一時的にオブジェクトを配置します。キューブなどの適当なオブジェクトをカメラの周りに配置してください。(わかりやすくするためのものですのでどんなオブジェクトを置いていただいても構いません)
ここで配置したオブジェクトを任意の空オブジェクトの子オブジェクトにしておくと後で削除するときにわかりやすいです。
(ここでは空オブジェクト「Debug」の子オブジェクトにしておきます)
カメラを回転させるスクリプトを作成する
カメラを回転させるスクリプトを作成していきます。
カメラを回転させるときの条件として、あらかじめ回転に制限(上下左右どこまで回転可能か)を設けます。
このように設定することで後ほどの開発がスムーズに進みます。
それではScriptsフォルダに「CameraController」スクリプトを作成して以下のように編集してください。
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraController : MonoBehaviour { //回転速度 [Range(0f,1f)] public float rotationSpeed = 1f; //縦方向の角度(上側) [Range(0f, 90f)] public float max_rotation_x = 80f; //縦方向の角度(下側) [Range(0f, 90f)] public float min_rotation_x = 80f; //左右方向の最大角度(左右対称のため最大のみ) [Range(0f, 180f)] public float max_rotation_y = 180f; //現在の回転角度 private float rotation_x = 0f; private float rotation_y = 0f; // Update is called once per frame void Update() { //左矢印キーが押されたとき if (Input.GetKey(KeyCode.LeftArrow)) { //カメラの横左方向の角度の範囲を指定 if (rotation_y < -max_rotation_y) { //範囲外のときreturnして処理させない return; } //現在の回転角度を変更 rotation_y -= rotationSpeed; //y軸を軸に左回りにrotationSpeed度回転 transform.rotation = Quaternion.Euler(rotation_x, rotation_y, 0); } //右矢印キーが押されたとき else if (Input.GetKey(KeyCode.RightArrow)) { //カメラの横右方向の角度の範囲を指定 if (rotation_y > max_rotation_y) { //範囲外のときreturnして処理させない return; } //現在の回転角度を変更 rotation_y += rotationSpeed; //y軸を軸に左回りにrotationSpeed度回転 transform.rotation = Quaternion.Euler(rotation_x, rotation_y, 0); } //上矢印キーが押されたとき if (Input.GetKey(KeyCode.UpArrow)) { //カメラの縦方向の角度の範囲を指定 if (rotation_x < -max_rotation_x) { //範囲外のときreturnして処理させない return; } //現在の回転角度を変更 rotation_x -= rotationSpeed; //x軸を軸に上方向に回転 transform.rotation = Quaternion.Euler(rotation_x, rotation_y, 0); } //下矢印キーが押されたとき else if (Input.GetKey(KeyCode.DownArrow)) { //カメラの縦方向の角度の範囲を指定 if (rotation_x > min_rotation_x) { //範囲外のときreturnして処理させない return; } //現在の回転角度を変更 rotation_x += rotationSpeed; //x軸を軸に上方向に回転 transform.rotation = Quaternion.Euler(rotation_x, rotation_y, 0); } } } |
注目してほしい部分をピックアップして説明します。
7行目〜14行目を確認してください。
1 2 3 4 5 6 7 8 |
//回転速度 [Range(0f,1f)] public float rotationSpeed = 1f; //縦方向の角度(上側) [Range(0f, 90f)] public float max_rotation_x = 80f; //縦方向の角度(下側) [Range(0f, 90f)] public float min_rotation_x = 80f; //左右方向の最大角度(左右対称のため最大のみ) [Range(0f, 180f)] public float max_rotation_y = 180f; |
ここではエディターから値を指定する変数を宣言しています。
「[Range(float,float)]」を変数宣言の前につけることで値の範囲を制限することができます。その範囲外の値を入力することができなくなるためとても便利です。このように範囲を指定することでユーザーが予期しない数値を入力することを防ぎます。
Rangeで値を指定するとエディターでは以下のように値を指定できるようになります。メインカメラにスクリプトをアタッチして確認してみてください。
それぞれの値は速度や回転範囲を制限するための変数です。
22行目〜35行目を確認してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//左矢印キーが押されたとき if (Input.GetKey(KeyCode.LeftArrow)) { //カメラの横左方向の角度の範囲を指定 if (rotation_y < -max_rotation_y) { //範囲外のときreturnして処理させない return; } //現在の回転角度を変更 rotation_y -= rotationSpeed; //y軸を軸に左回りにrotationSpeed度回転 transform.rotation = Quaternion.Euler(rotation_x, rotation_y, 0); } |
左矢印キーが押されたときの処理を記述しています。
「if ( Input.GetKey( KeyCode.LeftArrow ) )」で左矢印キーが押されていることを確認します。押されている間中はこの中の処理を実行します。
中では以下のような処理をしています。
- カメラの左方向の角度が制限範囲を超えていないか判定
- 超えている場合、以降の処理を実行しない
- 超えていない場合、以降の処理を実行
- 現在の回転角度に指定したスピード分だけ加算する
- 縦方向に回転するためにX軸を軸として回転する
ここではQuaternionに関する詳しい説明は省略しますが、詳しく知りたいという方は以下記事をご参考ください。
参考記事)Quaternionでオブジェクトを回転させる方法
この処理を上下左右全てで実装することでカメラの回転を実現します。
このスクリプトをメインカメラにアタッチして実行してみましょう。
矢印キーで回転することが確認できました。
カメラの向きと大砲の動きを連動させる
次にカメラの向きと大砲の動きを連動させます。
まずは大砲を呼び出しましょう。
Items→Victorian PBR Cannon→Prefab→Cannonをシーンビューにドラッグ&ドロップしてください。
ヒエラルキーウィンドウで新しく作成されたCannonを右クリックしてUnpack Prefabを選択してプレハブ化を解除しておきましょう。
新しく作成したCannonを丁度良い位置に移動します。
ポジションの値を( X, Y, Z ) = ( 0, -1.5, 1 )に変更してください。
これだけではカメラだけが回転してしまいますのでCannonをメインカメラの子オブジェクトに変更します。
これによってCannonが親オブジェクトであるメインカメラの座標を基準にするので動きが連動します。
実際に確認してみましょう。
シーンビューで見ると台座ごと動いてしまっているため違和感がありますが、きちんとカメラと大砲の動きを連動させることができました。
弾を発射する
ここから球を発射する方法をご紹介します。
以下のような流れでこの機能を実装していきます。
- 弾のプレハブを作成する
- 作成したプレハブを特定キー入力で生成する
- 弾に勢いをつけて発射する
弾のプレハブを作成する
まずはじめに弾のプレハブを作成します。
ヒエラルキーウィンドウのCreateからSphereを作成して、名前を「Bullet」としてください。
作成したBulletのスケールの値を全て0.3に変更してください。(ポジションの値は後ほどスクリプトで変更しますので任意です)
次に色を変更します。
プロジェクトウィンドウのCreateからMaterialsフォルダにMaterial「BulletColor」を作成してください。
作成したらBulletColorを選択して色を変更します。色の変更はColorウィンドウで変更可能です。
マテリアルの色を変更したら、BulletColorをBulletにドラッグ&ドロップすると色を変更できます。
次に弾に重力が発生するようにします。
Bulletを選択してインスペクターウィンドウ最下部のAddComponent→Physics→Rigidbodyを選択してアタッチしてください。
これでBulletに対して重力が発生するようになります。
最後にBulletをプレハブ化してヒエラルキーウィンドウから削除すれば大砲の弾のプレハブは完成です。
プレハブにしたいオブジェクトをプロジェクトウィンドウの任意のフォルダにドラッグ&ドロップすることでプレハブ化することができます。プレハブ化したらヒエラルキーウィンドウのBulletは削除しておきましょう。
作成したプレハブを特定キー入力で生成する
先ほど作成した弾のプレハブをスペースキー入力で大砲の先端に生成します。
まずは大砲の先端がどこにあるかをわかりやすくするためにポジションだけに意味があるオブジェクト「BulletPos」を作成します。
ヒエラルキーウィンドウのCreateからCubeを選択して名前を「BulletPos」としてください。
作成したら、BulletPosをCannonの子オブジェクトにします。
Cannonの子オブジェクトに変更したのち、BulletPosのTransformを以下のように変更してください。
これで大砲の先端をマーキングすることができました。
この位置にあればゲームビューで見えないのであまり気にする必要はないかもしれませんが、このオブジェクトはあくまでPositionの値のみを利用するため描画する必要がありません。
MeshRendererをオフにすることで描画をキャンセルできます。
これによってBulletPosが描画されなくなりました。
これで下準備は完了です。
ここから実際にBulletを生成するスクリプトを記述していきます。
Scriptsフォルダに新しいスクリプト「CannonController」を作成し、以下のように編集してください。
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CannonController : MonoBehaviour { //bulletプレハブ public GameObject bullet; //弾が生成されるポジションを保有するゲームオブジェクト public GameObject bulletPos; //弾丸のスピード public float speed = 1500f; // Update is called once per frame void Update() { //スペースが押されたとき if (Input.GetKeyDown(KeyCode.Space)) { //ballをインスタンス化して発射 GameObject createdBullet = Instantiate(bullet) as GameObject; createdBullet.transform.position = bulletPos.transform.position; } } } |
注目してほしい部分をピックアップして説明します。
7行目〜12行目を確認してください。
1 2 3 4 5 6 |
//bulletプレハブ public GameObject bullet; //弾が生成されるポジションを保有するゲームオブジェクト public GameObject bulletPos; //弾のスピード public float speed = 3f; |
ここでは先ほど作成したプレハブを格納するための変数と弾が生成される場所を保存する変数、弾のスピードを保存する変数の3つを宣言しています。現段階ではspeedは利用しません。
17行目〜23行目を確認してください。
1 2 3 4 5 6 7 |
//スペースが押されたとき if (Input.GetKeyDown(KeyCode.Space)) { //bulletをインスタンス化して発射 GameObject createdBullet = Instantiate(bullet) as GameObject; createdBullet.transform.position = bulletPos.transform.position; } |
スペースが押されたときに中身を実行します。
中身は「Instantiate(bullet) as GameObject」によってプレハブを生成し、GameObject型の変数「createdBullet」に格納します。
次にcreatedBulletのポジションをあらかじめ決定しておいたbulletPosのポジションに変更して完了です。
これでスペースが押されるたびに大砲の先端にbulletが生成されます。
作成したCannonControllerをアタッチするための空のオブジェクト「CannonController」を作成してスクリプトをアタッチ、BulletプレハブとBulletPosをスクリプトにアタッチしてください。(一連の流れは以下GIFをご参考ください)
実際に実行して確認してみましょう。
スペースキーを押すことでBulletが生成されることが確認できました。
弾に勢いをつけて発射する
次にこの生成した弾に勢いをつけて発射します。
先ほど作成したCannonControllerスクリプトを以下のように編集してください。(追加した部分をマーク)
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CannonController : MonoBehaviour { //bulletプレハブ public GameObject bullet; //弾が生成されるポジションを保有するゲームオブジェクト public GameObject bulletPos; //弾のスピード public float speed = 1500f; // Update is called once per frame void Update() { //スペースが押されたとき if (Input.GetKeyDown(KeyCode.Space)) { //ballをインスタンス化して発射 GameObject createdBullet = Instantiate(bullet) as GameObject; createdBullet.transform.position = bulletPos.transform.position; //発射ベクトル Vector3 force; //発射の向きと速度を決定 force = bulletPos.transform.forward * speed; // Rigidbodyに力を加えて発射 createdBullet.GetComponent<Rigidbody>().AddForce(force); } } } |
付け加えた部分によって弾の向きと力を決定してBulletに加えることで弾を発射します。
実際に実行して確認してみましょう。
弾を発射できていることが確認できましたが、これだと非常に見づらいです。そこで弾の発射角度を変更します。
方法は簡単で、先ほど作成したBulletPosの向きを少しだけ上向きにすることで改善することができます。
理由は今回弾の向きをBulletPosの向きに設定しているため、このオブジェクトの向きを変えると弾の発射角度も変わります。
今回はBulletPosのRotationのXを-15に変更してください。
それでは改めて実行してみましょう。
弾が綺麗に発射されるようになりました。
まとめ
いかがでしたでしょうか。
弾の発射は簡単なスクリプトを作成するだけで実装できることがお分りいただけたでしょうか。
次回は敵を作成して大砲で敵を倒す方法をご紹介します。
敵と弾との当たり判定やエフェクトの作成など、よりゲームらしい要素の作成を進めていきます。
※そのほかのゲーム作成記事については以下記事をご参考ください。
参考記事)Unity入門オリジナルチュートリアル集
この記事はいかがでしたか?
もし「参考になった」「面白かった」という場合は、応援シェアお願いします!