0から2Dアクションバトルゲームを作ろう!③弾を発射させてみよう

0からアクションゲームを作ろう講座その3です。
今回は弾を発射してみます。前章と同じく重要な基礎知識が沢山あるので頑張っていきましょう!

この章でやること

プレイヤーの移動処理が実装できたので、次は攻撃を出来るようにしてみましょう。(前回の内容が不安な方はこちらから復習しておきましょう)

攻撃の手段として「弾の発射」を実装します。
発射される弾は勿論ゲームオブジェクト(以下、オブジェクト)なのですが、その際役に立つのが「プレハブ(Prefab)」という機能です。

これから、プレハブの概念も学んでいきましょう。

目標とする動作

新しい機能を実装する際は、その仕様を簡単にまとめておくと分かりやすくなります。今回実装したい機能は、次の通りになります。

  • マウス左クリックでプレイヤーが弾を発射。
  • 弾の初期位置はプレイヤーの位置と同じ。
  • 弾は発射後、右に直進し続ける。
  • 弾は発射されてから数秒後消滅する。

 

1つ1つ順番に実装していきましょう。

プレハブを知ろう

ゲーム中のオブジェクトを扱う際に役に立つのがプレハブ(Prefab)という機能です。
ここで基本的な機能を学びましょう。

プレハブとは?

プレハブは既に作成したオブジェクトを元として作成できる、金型のようなものです。

プレハブ自体はオブジェクトではありませんが、スクリプト等と同じようにファイル(Asset)として保存されプロジェクトウィンドウで管理できます。

プレハブはオブジェクトの設計図のようなもので、スクリプトからあるいはUnityの編集画面から実体(インスタンス)をオブジェクトとして生成できます。

生成したばかりのインスタンスはプレハブと全く同じパラメータを持ちます。

プレハブの使い時

プレハブは前述の通り、「ゲーム中にオブジェクトを(スクリプトから)生成する必要がある」場合に有用です。
ですがそれ以外にも使い時はあります。

例えばゲーム中ではなく、編集中にシーン(画面)にオブジェクトを配置する場合です。
一種類のオブジェクトを大量に配置する時などは特にプレハブを使うと便利です。

元のプレハブの設定(コンポーネントのパラメータ等)を変えると、プレハブから作られたオブジェクトも全て設定の変更が適用されます。(既にその設定をオブジェクト側が変更していた場合を除きます。)

プレハブの登録や更新

プレハブは、シーン中のオブジェクトを対象にした「プレハブ化」という操作で登録する事が出来ます。設計図としてUnity上に登録する操作と考えればイメージしやすいでしょう。
それでは具体的な操作を見ていきましょう。この操作は、とても簡単です。

まずはヒエラルキーウィンドウからプレハブ化したいオブジェクトを選択します。
それをプロジェクトウィンドウにドラッグアンドドロップするとプレハブが作成されます。

プレハブを作成した後ヒエラルキーウィンドウを見ると、元になったオブジェクトは上の画像のように青色で表示されるようになります。
この青色が「インスタンスである」というサインです。

また今は利用しない機能なのですが、インスタンスであるオブジェクトはインスペクターウィンドウ上に「Select」「Revert」「Apply」というボタンが出現します。
これはプレハブの更新に関する機能であり、「似たようなオブジェクトを大量に配置する」場合においてよく役立ちます。

覚えておくと便利でしょう。

  • Selectボタン
    そのオブジェクトの元となったプレハブをプロジェクトウィンドウから選択します。
  • Revertボタン
    そのオブジェクトのパラメータをプレハブのパラメータに戻します。(座標と回転値は除く)
  • Applyボタン
    そのオブジェクトのパラメータをプレハブにセットします。

 

弾のプレハブを作ろう

それでは発射される弾のオブジェクトを作成し、それをプレハブに変化させてみましょう。

オブジェクトの作成

これから「弾のプレハブ」を作成します。なので、「元となる弾のオブジェクト」をまずは作成していきます。

オブジェクトの作成は今までとほぼ同じですので、思い出しながらやってみましょう。
メニューバーの[GameObject]から[2D Object]→[Sprite]を選択です。
作成したオブジェクトには「BulletPrefab」等分かりやすい名前を付けましょう。
(オブジェクト名がプレハブの名前にもなります。後から変更可)

オブジェクトが作成できたら、テクスチャもセットしてあげましょう。
今回はサンプル素材として以下に弾画像を公開します。(11/14 弾画像をアップしたので、右クリックから保存でこちらをお使いください)

(ちなみに、オブジェクトの大きさを変更したい場合にはShittキーを押しながら操作すると、現在の縦横の縮尺を維持したまま大きさを変更することができます。)

流れも前回と同じで、プロジェクトウィンドウへのドラッグアンドドロップでインポート、
それを弾オブジェクトのSpriteにセットで完了です。

違う画像を使ってももちろん大丈夫ですが、次章の当たり判定で設定するパラメータが少し変わるのでお気をつけください。


ひとまずこの状態を目指してみましょう。(クリックで拡大できます)

スクリプトの作成とアタッチ

オブジェクトの作成が出来たのでこのままプレハブ化をしても問題はないのですが、
後から弾のスクリプトを組んでいくので、あらかじめスクリプトをコンポーネントとして用意しておきましょう。

スクリプトの作成も復習です。
プロジェクトウィンドウ上で右クリック→[Create]→[C# Script]でスクリプトの作成、そしてすぐ名前の入力が始まります。
スクリプト名は「Bullet」にしましょう。(スクリプトの中身は後で編集します。)

これを「BulletPrefab」オブジェクトのヒエラルキーウィンドウにドラッグアンドドロップで準備完了です。

オブジェクトをプレハブに

それではこの弾のオブジェクトをプレハブ化してみましょう。

「プレハブの登録や更新」で説明した通り、まずはヒエラルキーウィンドウから弾オブジェクトを選択します。
それをそのままプロジェクトウィンドウまでドラッグアンドドロップするだけです。

これでプレハブのデータが出来上がりました。
ヒエラルキーウィンドウを確認すると、プレハブ化された「BulletPrefab」オブジェクトはインスタンス扱いとなり、青文字で表記されています。

そして、弾オブジェクトはプレイヤーかボスが発射した時のみ画面に現れるので、ゲーム開始時には不要な存在です。後ほどスクリプトで出現させるようにします。

なのでヒエラルキーウィンドウ上の元となったオブジェクトはここで削除しておきましょう。
(削除もヒエラルキーウィンドウから可能で、右クリック→[Delete]でOKです。)

弾を発射してみよう

弾のプレハブが作成できましたね。まだ無処理のスクリプトが1つ付いているのみで動きはありません。まずはスクリプトからインスタンスを生成する所から始め、その後、弾の動作処理を書いていきましょう。

プレイヤーのスクリプトと紐づける

まずはプレイヤーが弾を発射する処理を作るので、PlayerControllerスクリプトを開きます。
そしてクラスのメンバ変数となる場所に、以下の文を書き込んでpublic変数宣言をします。(メンバ変数の内容が不安という方は前回の記事を参考にしてみてください)

public GameObject bulletPrefab;
(場所が不安な方は次項にスクリプト全文がありますので参考にしてください。)

これでプレハブを呼び出すスクリプト側の用意は終わりました。
次に、スクリプトがアタッチされているPlayerオブジェクト側の準備を行います。

Unity編集画面に戻り、Playerオブジェクトを選択状態にしてください。
そしてインスペクターウィンドウを見てコンポーネントを確認すると、スクリプトのパラメータが1項目増えているのが分かります。

スクリプトでpublicメンバ変数を宣言すると、インスペクターウィンドウ上で初期値を設定する事が出来ます。
今回GameObject型の変数をpublicで宣言したので、ここでオブジェクトかプレハブを初期値として指定できるようになりました。
上の画像を参考に、Playerオブジェクトに先ほど用意したプレハブをセットしましょう。

これでオブジェクト側の準備も完了です。
プレハブをスクリプトで利用する場合、public宣言からのセットという流れを覚えておきましょう。

インスタンスの生成

では「クリックした時に弾を発射」する処理の実装に入ります。
編集するのはもちろんPlayerControllerスクリプトです。
前回組んだ移動処理は残しつつ、以下のように追加してみましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {

	// メンバ変数宣言
	public GameObject bulletPrefab; // 弾のプレハブ

	// 起動時に1回だけ呼び出されるメソッド
	void Start () {
	}
	
	// 毎フレーム呼び出されるメソッド
	void Update () {
		// -----移動処理-----
		// マウスカーソルの座標をVector2型変数cursorPosに取得
		Vector2 cursorPos = Input.mousePosition;
		// cursorPos内の座標データをスクリーン座標からワールド座標に変換する
		cursorPos = Camera.main.ScreenToWorldPoint (cursorPos);
		// Transformコンポーネントのpositionに計算したcursorPosを代入
		transform.position = cursorPos;

		// -----弾発射処理-----
		if (Input.GetMouseButtonDown (0))
		{ // 左クリックを押された瞬間
			// GameObject型ローカル変数を宣言 (生成したインスタンスを格納する)
			GameObject obj;
			// 弾プレハブのインスタンスを生成し、変数objに格納
			obj = Instantiate (bulletPrefab);
			// 弾インスタンスの座標にプレイヤーの座標をセット
			obj.transform.position = transform.position;
		}
	}
}

まず注目したいのがInput.GetMouseButtonDown()メソッドです。
これはマウスクリックがされた瞬間のみtrue(真)を、それ以外はfalse(偽)を戻り値として返します。
戻り値とはメソッドから呼び出し元に対して渡される値です。引数の逆という感覚で覚えると良いでしょう。

メソッドの内容については、こちらの記事で解説しています。

上手に使えてる?メソッドの基本について学ぼう!

2018.09.25

参考として、if文部分をより分かりやすく書き換えたものを以下に用意しました。

		// -----弾発射処理-----
		// bool型のローカル変数を宣言(マウスクリック状態格納用)
		bool clickCheck = Input.GetMouseButtonDown (0);
		if (clickCheck)
		{ // 左クリックを押された瞬間

マウスクリック状態がtrueならif文の中に入ります。

そしてオブジェクトを生成する処理ですが、生成だけならInstantiate()メソッド1つで完了です。
引数として先ほど用意したプレハブの変数bulletPrefabを渡せばそれのインスタンスが出来上がります。

ですがそれだけだと位置が未指定です。実際はプレイヤーと同じ位置に出現して欲しいですね。
なのでインストールのposition変数に自分のposition変数をセットしてやります。
Instantiate()メソッドは戻り値に生成されたオブジェクト変数を返すので、それを取得して編集しています。


まだ弾が動かないので地味ですが、クリックした時のプレイヤー位置にオブジェクトが出来ているはずです。

if文のおさらい

if文はスクリプト中で条件分岐に用いる文です。
if (A==B) {…} と書けば、
「AがBと等しければ波かっこ{}内の処理を実行」という流れになります。

かっこ()内に書くのは条件式です。条件式は成立するとtrueになります。
最終的にtrueが入っていれば処理に入れるので、条件式を複数書いても良いですし、先ほどのようにbool変数を入れても良いです。

条件式で使う演算子のうち、代表的なものをいくつか紹介します。

A > B AがBより大きければ成立
A >= B AがB以上なら成立
A < B AがBより小さければ成立
A <= B AがB以下なら成立
A == B AがBと等しければ成立(書き間違いに注意!)
A != B  AがBと等しくなければ成立

if文はとても大事になる制御文なので、何度も試したりして慣れておくと吉です。
不安な場合はこちらで復習しておきましょう。

制御文の基本とif文の扱い方を知ろう!

2018.09.01

 

クリックの検知

先ほど説明した通り、Input.GetMouseButtonDown()メソッドを使うとクリックされた瞬間を取得できます。
これに関連するメソッドとして、Input.GetMouseButton()はクリックをされた瞬間ではなくされている間ずっとtrueが戻り値で返ってきます。
Input.GetMouseButtonUp()はクリックを離された瞬間のみtrueが入ります。

引数として渡すのはマウスのボタン番号です。
左クリックなら0、右クリックなら1を指定します。ちなみに2はホイールクリックです。

弾の処理を実装

まだ弾自体の処理が未実装であり、発射してもその場にとどまるだけです。
ここで章の序盤に挙げた仕様を思い出し、弾の処理をプログラミングしていきましょう。

今から編集するのはBulletスクリプトです。他のスクリプトと間違えないように注意しましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour {

	// 変数宣言
	private float speed = 3.0f; // スピード
	private float time = 0.0f;  // 経過時間

	// 起動時に1回だけ呼び出されるメソッド
	void Start () {
	}

	// 毎フレーム呼び出されるメソッド
	void Update ()
	{
		// -----移動処理-----
		// Transformコンポーネントからposition(座標)パラメータを取得
		Vector3 pos = transform.position;
		// 右に指定した速度で直進する
		pos.x += speed * Time.deltaTime;
		// Transformコンポーネントのpositionに変数posをセット
		transform.position = pos;

		// -----寿命処理-----
		// 前回のUpdate実行から経過した時間をtimeに加算
		time += Time.deltaTime;
		// 消滅処理
		if (time > 5.0f)
		{ // 弾の経過時間が5秒より大きければ
			Destroy (gameObject);
		}
	}
}

それでは中身を解説していきます。

基本的な流れは今まで書いてきたものと似ていますが、新たにTime.deltaTimeという文言が確認できます。
これはfloat型変数であり、「前回のUpdate()実行からの経過時間(秒)」が入っています。
これに例えば0.02が入っていた場合、経過時間は0.02秒です。
pos.x += speed * Time.deltaTime;とすれば、1秒経過時点でちょうどspeed値分だけx座標が増える、という事になります。

またDestroy()メソッドが登場しています。これは引数で渡したオブジェクトやコンポーネントを消去するというものです。
今回は時間経過による消滅処理での使用ですので、gameObjectを指定します。こうするとこのスクリプトがアタッチされているオブジェクトが消去されます。

ちなみに今回は時間経過という条件にしましたが、何らかの条件でこのような処理を入れないとオブジェクトが消去されずにどんどん増えていってしまいます。
それだと動作が重くなったり、最悪固まってしまう場合もあるので、このような消滅処理は状況によっては必須です。

ここまで実装した処理を試してみましょう。下の画像のように、クリックした瞬間右側に向かって弾が発射される様子が確認できれば完了です!(弾のテクスチャは設定した画像によって異なります)

ヒエラルキーウィンドウ上では、Destroy()メソッドによってオブジェクトが消滅処理されている様子が確認できます。

余談ですが、エディタの色が違うことに違和感を感じた方もいるかもしれません。実は簡単に変更できますので、もし気になった方はこちらを参照してみてください。

ここまでのおさらい

スクリプトを使ってゲーム動作中にオブジェクトを追加する事を学びました。

その為に使ったプレハブという機能は、オブジェクトをプレハブ化する事で作成できるAssetの一種です。
Instantiate()メソッドを利用する事でスクリプトからインスタンス化ができます。

if文は今後プログラミングを行う上で重要度の高い制御文です。
if(A>B){…}と書いた場合、AがBよりも大きければ波かっこ{}内の処理を実行します。

メソッドやif文が不安だという方は、こちらでより詳しく解説しています。

 

次章で学ぶこと

現在は弾が撃てるようになっただけで、ボスには当たっていないですね。
次章では当たり判定を実装し、ボスと弾の接触処理を組んでみましょう。
少しずつゲームらしくなっていきますよ!

<前回>    <= 「②プレイヤーキャラを動かそう
< 次回>  => 「④当たり判定を実装しよう

オススメアプリ!渋谷で気軽に友達を作れるアプリが登場!