0から2Dアクションバトルゲームを作ろう!②プレイヤーキャラを動かそう

0からアクションゲームを作ろう講座その2です。
今回からスクリプトの解説が始まります。プレイヤーを自由に動かせる所までやってみましょう!

この章でやること

前回でプレイヤーとボスのオブジェクトを配置する所まで出来ましたが、何の処理も与えていなかったためその場で止まっているだけでした。(前回の内容が不安の方はこちらから復習しておきましょう)

今回はコンポーネントとスクリプトの基礎知識を学び、プレイヤーに「移動する処理」を与えましょう!

目標とする動作

この講座のサンプルゲームを遊んでいただいた方はプレイヤーの動きをイメージできるかと思います。

ゲームではプレイヤーは「マウスカーソルに追随して移動」していましたね。今回はあの動作をスクリプト1つで実現させてみましょう。

コンポーネントを知ろう

前章でも少し触れましたが、Unityでゲームを開発していくにあたって「コンポーネント」の基礎知識は必須となります。

しかし難しく考える必要はなく、実態はいたってシンプルです。

コンポーネントとは?

コンポーネントとはゲームオブジェクトに付加するものであり、ゲームオブジェクトはコンポーネントによって機能を獲得していきます。

コンポーネントには沢山の種類があり、それぞれが固有の機能を持っています。ですので、各オブジェクトはその機能に応じて適切なコンポーネントが付けられねばなりません。

では、前回作成したPlayerオブジェクトを選択して今のコンポーネントを確認してみましょう。


画像で水色に囲った部分がコンポーネントです。
「Transform」と「Sprite Renderer」の2つのコンポーネントが既に取り付けられている事がわかります。
まず、この2つコンポーネントの機能を見ていきます。

  • Transformコンポーネント
    オブジェクトの位置や角度、スケール(拡大率)といった基本的な情報が定義されています。
    例えば、このコンポーネントの持つPosition値(位置パラメータ)を別の値に変更するとオブジェクトがその位置へ移動します。
    全てのオブジェクトは生成される際に必ずこのコンポーネントが取り付けられています。
  • Sprite Rendererコンポーネント
    2Dオブジェクトの描画に関する機能を持つコンポーネントです。
    「Sprite」にテクスチャをセットするとそのテクスチャを描画するようになります。(前回行ったのはこの作業です。)
    「Color」パラメータは色を管理しており、これを変更するとオブジェクトの色が変わります。
    それ以外のパラメータは今回はいじる必要はありません。
    Spriteオブジェクト生成の操作で生成すると自動的にこのコンポーネントも付加されます。

 

この2つのコンポーネントがあるからこそ、現在決まった位置にプレイヤー・ボスの画像を表示する事が出来ているのです。
そしてこれからやるべき事はプレイヤーの移動処理の実現ですね。

ですので、「オブジェクトの移動処理を行うコンポーネント」を作成し、プレイヤーオブジェクトに取り付けるという事をこれから行っていきます。

コンポーネントの追加と削除

オブジェクトにコンポーネントを追加するには、主に二通りの方法があります。

1つ目は、前項の画像の赤色で囲った「Add Component」というボタンからコンポーネントの追加を行う方法です。これはUnityに標準機能として存在しているコンポーネントを取り付ける場合に使われます。
例えば「Sprite Renderer」コンポーネントは今回オブジェクトに元から付けられていましたが、これを後から追加したいとなった場合、このボタンを押してSprite Rendererを探せば同じように取り付けることが可能です。

そして2つ目は、プロジェクトウィンドウからのドラッグアンドドロップであり、これは主にユーザー作成のスクリプトをコンポーネントとして取り付ける場合にとる方法です。
1つ目のAdd Componentからでもスクリプトの取り付けは可能ですが、スクリプトはプロジェクトウィンドウ内に存在するものなのでここから直接取り付けた方が分かりやすいです。

コンポーネントの削除はヒエラルキーウィンドウ上で行います。

削除したいコンポーネントの名前の部分を右クリックか、右上端の歯車マークをクリックでメニューが表示されます。
そこから「Remove Component」を選択で削除が完了します。

ちなみにメニューの内容を見て分かるとおり、コンポーネントのコピーペースト等の操作もここから行う事ができます。

スクリプトの扱い方

スクリプトを作成してからオブジェクトのコンポーネントにすることを「アタッチ(する)」と言います。スクリプトは基本的に作成しただけでは読み込まれず、オブジェクトにアタッチするという操作が必要なので注意が必要です。

また、最初はどんな機能をもったプログラミングをどのオブジェクトのスクリプトに加えていけばいいか戸惑うかもしれません。しかし、サンプルをいくつか作る中で感覚は徐々に掴めていくかと思います。

 

 

プレイヤーキャラを動かしてみよう

いよいよプログラミングに突入します!

まずは単純な動作を行うスクリプトを作成し、感覚を慣らしてみましょう。

スクリプトの作成

最初にする事はスクリプトの新規作成です。操作方法は後述しますが、スクリプト1つ1つはファイルとして保存されるので追加や削除といった管理操作はプロジェクトウィンドウ上で行います。
ファイルですので最初にファイル名を指定する事になるのですが、最初に決めた名前を後から変更しようとすると、「ファイル名とクラス名が一致しない」という状態が発生します。

クラスに関する知識がないと意味が分からないかと思いますが、これから行うプレイヤーの移動処理などの実装は全て「クラスの機能」として作る事になります。ですので、最初の段階ではクラス名=コンポーネント名だと解釈しても問題ないでしょう。

そのクラス名が新規作成するスクリプトの中に自動的にファイル名と同一で書き込まれてしまうので、後からファイル名だけを変えるとそのような事態が起こるのです。
なお、クラス名とファイル名は違っているだけでエラーが出てしまいます

 

難しい話が続きましたが、現段階では理解できなくても構いませんので、実際に次のように作成してみましょう。

プロジェクトウィンドウの何もない所で右クリックをし、[Create]から[C# Script]を選択するとスクリプトの原型を作成できます…が、すぐにファイル名の指定に入るのでまだ確定をしないでください。
ファイル名(クラス名)はここでは「PlayerController」とします。入力が出来たら確定をしてください。



スクリプトに限らず、ファイルの削除はそのファイルを選択して右クリックから[Delete]の選択で行えます。クラス名の入力を間違えた時などはこの方法でやり直すと良いでしょう。

ちなみにスクリプトの新規作成はメニューから[Assets]→[Create]→[C# Script]でも可能です。

スクリプトエディタの起動

それでは、続いていよいよプログラミングを開始していきます!
作成した「PlayerController」ファイルをダブルクリック(または右クリックから[Open])しましょう。
何らかのスクリプトエディタが立ち上がるはずです。(基本的にはMonoDevelopだと思います。)

ここではスクリプトエディタの使い方の説明は割愛しますが、大まかな所はメモ帳のようなテキストエディタと大差はありません。
エディタはAssetsフォルダ内のスクリプトファイルを書き換えるだけですので、極端な話メモ帳でプログラミングしてもOKです。

ただしスクリプトエディタなら自動補完機能やエラーチェックが付いているので、スクリプトエディタを使わない理由はないでしょう。

初期スクリプトの確認

前述した通り、スクリプトは作成時に土台となるコードが書き込まれています。
それを確認し、少しだけ内容を把握してみましょう。

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

public class PlayerController : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

以下簡易的な解説を行います。少々難しいかもしれませんが、今全てを覚える必要はなく、今後回数を重ねる上で少しずつ慣れていきましょう。

まず1~3行目のusingから始まる文ですが、これは「他の〇〇というソースコード(名前空間)を利用しますよ」という文です。
特に「UnityEngine」を利用する事で、Unityで使いたい様々な機能をC#のプログラミングで使用する事が可能になっているのです。
この状態で必要最小限のusing文は揃っているので、現在はここを触る必要はありません。

そして「public class PlayerController : MonoBehaviour」という文があり、ここからファイル終端までが波かっこ{}内に含まれるようになっています。
まず文の意味ですが、これが先ほど出てきたクラスという概念を記述するコードです。クラスとコンポーネントは似たような意味だとお話しましたね。
この後{}内に記述するのが全てこのクラス固有の機能という事になります。

また、行の中で「//」という記号2文字が登場した場合、その行は終わりまでがコメント部になります。
コメントとなった文は全てC#プログラムとしては無視されるので、何を書いてもエラーになりません。コメントを利用することでスクリプト内にメモを残すことができます。
例えば7行目の「// Use this for initialization」はコメント行であり、Startメソッドの簡単な説明が書いてあります。

そして「void Start ()」と「void Update ()」の2つのメソッドが書かれていますが、続く{}内は空白となっており、現段階では2つとも何の仕事もしないメソッドとなっています。
メソッドという概念についての解説はやや長くなってしまうので、以下の解説記事を読んでいただけると幸いです。

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

2018.09.25

このStart()とUpdate()というメソッドは特別な立ち位置にある存在であり、それぞれ決まったタイミングで自動的に内部の処理が実行されます。それぞれの機能については次の通りです。

Start()メソッド
スクリプトが読み込まれるタイミングで、最初に1度だけ呼び出される(=内部の処理を開始する)メソッド。

Update()メソッド
スクリプトが読み込まれると、毎フレーム呼び出されるメソッド。

 

メソッドはユーザーが自由に作成して呼び出す事が出来ますが、この2つはゲーム中に自動的に呼び出される特殊なメソッドであり、あらゆる処理の起点として機能していく事になります。

これから具体的な使用例を見てみましょう。

動作確認用のスクリプトを用意する

ではこの空白のスクリプトに何か処理を書き込んでみましょう。

まずは簡単なものを実装して動作確認としてみます。「(プレイヤー)キャラクターが右に移動する」だけですが、次のようなスクリプトを作成しましょう。

スクリプトの右上からコピーが可能ですが、最初のうちは手打ちでプログラミングに慣れていくことを強くお勧めします。

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

public class PlayerController : MonoBehaviour
{

    // float型メンバ変数をprivateで宣言
    private float speed; // 移動速度設定用

    // 起動時に1回だけ呼び出されるメソッド
    void Start()
    {
        speed = 0.03f; // 移動速度0.03fをspeed変数にセット
    }

    // 毎フレーム呼び出されるメソッド
    void Update()
    {
        // Vector3型ローカル変数を宣言
        // (Vector3はx値とy値とz値を保持する構造体)
        Vector3 pos; // プレイヤー座標値計算用

        // Transformコンポーネントからposition(座標)パラメータを取得
        pos = GetComponent<Transform> ().position;

        // 右方向への移動
        pos.x = pos.x + speed; // 変数posのx値にspeed文を加算して代入

        // Transformコンポーネントのpositionに計算した座標をセット
        GetComponent<Transform> ().position = pos;
    }
}

(ここからスクリプトの解説をしますが、動作結果を見たい方は「プレイヤーキャラにアタッチ」までスクロールしてください。)

上のスクリプトでは、プログラミングで必須となる「変数」という概念が登場しました。
変数はスクリプト内で自由に値を保持できる箱のような存在で、使用する前に宣言が必要になります。宣言の方法は「変数型 変数名」です。
クラスの機能になるので、宣言もクラス内で行います。

メソッドの中で変数宣言するとその変数は「ローカル変数」となり、そのメソッド内でのみ使用できます。
逆にメソッド外で宣言すると「メンバ変数」となります。メンバ変数は更にpublic変数とprivate変数に分けられますが、この違いは後述します。

変数は計算式に組み込む事で、変数に値を代入(セット)したり四則演算等が出来ます。
その他、変数にまつわる解説は別記事にもありますので、変数の基礎内容をまずは覚えてください。

箱をイメージ?Unityにおける変数・定数の違いについて知ろう!

2018.09.02

また、変数と共に登場したのがGetComponent<>()というメソッドです。これはゲームオブジェクトに備わっている機能であり、<>内に指定したコンポーネントをこのスクリプトが付いているゲームオブジェクトの中から探し、実体を返してくれるメソッドです。
この場合ではTransformコンポーネントをスクリプト内に取得し、利用できるようにしています。

Transformコンポーネントのメンバ変数positionは座標を管理する変数でしたね。(クラス内のメンバ変数などは実体に.(ドット)を付ける事で呼び出せます。)
この変数を別の値に変更すればオブジェクトが動くので、まずposition変数と同じ型であるVector3型の変数を用意してそこに数値をコピーしています。
Vector3型はx,y,zの3要素で構成される構造体です。変数posの場合、pos.xと記述する事でpos内のx成分を指定できます。
今回はpos.xに0.03を加算して代入し、更にこれをTransformのposition変数に代入しました。

Update()は毎フレーム呼び出されるメソッドなので、これで毎フレームx座標が+0.03する処理が追加されたという事になります。

プレイヤーキャラにアタッチ

それではUnityに戻り、このスクリプトをコンポーネントとして使用してみましょう。
取り付けるのはPlayerですので、まずはPlayerオブジェクトを選択します。

インスペクターウィンドウ上に先ほどのスクリプトをドラッグアンドドロップすると取り付けが完了します。(上の動画では見やすいようにUnityエディターの表示を変更しています)

こうなっていればOKです。

いざ実行!

あとは動いている状態を確認するだけです。

シーン・ゲームビューの真上にある右向き▲のボタンをクリックするとゲームのテストプレイが開始します!

このように、プレイヤーが右方向にスーっと動いていればOKです。

少しだけ書き換えてみよう

プログラミングに慣れるため、先ほどのソースコードほぼ同じ処理で別の書き方に変えてみます。
実際に試さなくても良いのですが、一度読んで確認してみましょう。

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

public class PlayerController : MonoBehaviour {

	// float型メンバ変数をpublicで宣言
	// publicにする事で、インスペクターウィンドウから変数の値を変更可能になる。
	public float speed = 0.03f; // 移動速度設定用

	// 起動時に1回だけ呼び出されるメソッド
	void Start () {
	}
	
	// 毎フレーム呼び出されるメソッド
	void Update () {
		// Vector3型ローカル変数を宣言
		Vector3 pos; // プレイヤー座標値計算用

		// Transformコンポーネントからposition(座標)パラメータを取得
		// transformとGetComponent<Transform>()は同義
		pos = transform.position;

		// 右方向への移動
		pos.x = pos.x + speed; // 変数posのx値にspeed文を加算して代入

		// Transformコンポーネントのpositionに計算した座標をセット
		transform.position = pos;
	}
}

speed変数がpublicで宣言された事により、インスペクターウィンドウからこの変数の値を変更できるようになります。

具体的には上の画像のSpeedの値を変更することで、移動速度を変更できます。

移動のスクリプトを完成させよう

ここまでで真横に移動する動作は実現しました。
Unityでのプログラミングの大まかな流れは分かったでしょうか?
これからは仕様の実装に入るので、もう少し複雑な処理を書いてみましょう。

これから行う事

今回実装すべきプレイヤーの動作は「マウスカーソルに追随する」というものです。
追随という表現をしていますが、ゲーム画面上のマウスカーソルの位置にぴったり張り付くようになっていれば良いです。

もちろんここまでの知識だけでは実装できず、新しい機能をいくつか利用する必要があります。
更に難易度が上がりますが、ここからはやや限定的な知識も含まれていきます。

特定の場面でしか使わないような機能・構文は暗記する必要はなく、必要になったタイミングで都度探したり考えたりすれば良いのです。
ただし、基本的な流れである「情報を取得し、計算し、適切な場所で使用する(セットする)」というのは多くの処理で共通します。
ですので何が起こっているかの理解だけは出来るようになると、この先大きなアドバンテージとなります。

マウスカーソルの位置を検出

いったん、マウスカーソルの座標がどのような形で入力されるのか確かめてみましょう。

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

public class PlayerController : MonoBehaviour {
	
	// 起動時に1回だけ呼び出されるメソッド
	void Start () {
	}
	
	// 毎フレーム呼び出されるメソッド
	void Update () {
		// マウスカーソルの座標をVector2型変数cursorPosに取得
		// Vector2型とVector3型は互換性がある(z値は無視される)
		Vector2 cursorPos = Input.mousePosition;

		// cursorPosの値をコンソール画面に出力する
		Debug.Log (cursorPos);
	}
}

これの実行時に確認したいのが、プロジェクトウィンドウの上にあるConsoleタブです。
このタブをクリックするとコンソール画面に切り替わり、Debug.Log()によって出力されるログがここに表示されるようになります。

Debug.Logについて

Debug.Log()メソッドの()に変数を渡す(入れる)とコンソール画面にログとしてその変数の中身を出力してくれます。
ちなみにメソッドに渡した変数はそのメソッドの引数(ひきすう)となります。

今後プログラミングを行う上で、変数の中身や処理の順番などを確認できるこのDebug.Log()メソッドは非常に多用する事になるでしょう。
とても便利なのでぜひ覚えておいてください。

先ほどのスクリプトを実行すると上のようにマウスの位置がConsoleタブに表示されます。(画面の構成は見やすいように変更しています)

プレイヤーキャラをカーソルに追随させる

マウスカーソルの座標の取得が完了しました。後はこれをTransformのpositionに代入するだけで良いです。
…が、少しだけここに必要な処理が挟まります。

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

public class PlayerController : MonoBehaviour {
	
	// 起動時に1回だけ呼び出されるメソッド
	void Start () {
	}
	
	// 毎フレーム呼び出されるメソッド
	void Update () {
		// マウスカーソルの座標をVector2型変数cursorPosに取得
		Vector2 cursorPos = Input.mousePosition;

		// cursorPos内の座標データをスクリーン座標からワールド座標に変換する
		cursorPos = Camera.main.ScreenToWorldPoint (cursorPos);

		// Transformコンポーネントのpositionに計算したcursorPosを代入
		transform.position = cursorPos;
	}
}

取得したマウスカーソル座標はそのままの形では利用するに適さず、ワールド座標に変換する必要があります。
ですが、この処理に関しては覚えなくとも良いです。「何か一手間が要るんだな」くらいに思っておいてOKです。

このスクリプトを用意できたら再び実行確認をしてみてください。
マウスカーソル上にプレイヤーが移動するようになっていれば完了です!

 

うまく動かない場合

まずはエラーの発生を疑いましょう。
エラー発生中はコンソールウィンドウ上に赤いマークと共に文章が表示されています。
スクリプトに原因がある場合、この文章をダブルクリックでスクリプトの該当部分を表示してくれます。

エラーの原因で多いのは変数やメソッド、クラス名等の記述ミスです。
または行末の;(セミコロン)の打ち忘れ、{}(波かっこ)の記述忘れや場所の間違い等も考えられます。
スクリプトエディタ内に表示されるエラー文も読んでみましょう。英語なので少々難しいですが…。

エラーが出ない場合は厄介です。スクリプト内の怪しい箇所にDebug.Log()を打ち込み、コンソールで確認をしながら「想定したタイミングで変数に想定した値が入っている」事を見てください。
またpublicでメンバ変数を宣言する場合、インスペクターウィンドウ上で設定した値も確認しましょう。(今回は不要です)

ここまでのおさらい

プレイヤーオブジェクトがマウスカーソルに追随する動きが完成しました。
その過程でコンポーネント及びスクリプトの基礎という、Unityでゲームを作る上でとても大切な要素を学びました。

コンポーネントはゲームオブジェクトに取り付けられ、オブジェクトに様々な機能を付与する存在です。ユーザーが作成するスクリプトもまたコンポーネントになります。
「移動する処理を行う」スクリプトを作成し、コンポーネントとして取り付ければ、そのゲームオブジェクトは移動処理を行います。

スクリプトはStart()やUpdate()などの自動的に呼び出されるメソッドを起点として色々な処理を組んでいきます。
Start()はスクリプト動作開始時に実行、Update()は毎フレーム実行です。

変数は値を保持できる入れ物であり、計算に使ったり他の変数に代入が可能です。

GetComponent<>()メソッドは同じゲームオブジェクト中の<>で指定したコンポーネントを取得できます。
Transformコンポーネントのposition変数を変更するとオブジェクトの位置が変わります。

ここまでで理解しにくい部分があれば、ぜひ他の記事なども参考にしていただいて、疑問を解消してから次のステップに進む事をオススメします。

次章で学ぶこと

まだプレイヤーが移動するのみで戦闘は実現していません。
なので弾のオブジェクトを作成し、それをクリックで発射する所までをやってみましょう。
その過程でプレハブという概念を学ぶ必要があるので、その解説も行います。

<前回>    <=「①ゲーム画面の作成

<次回>    =>「③弾を発射してみよう

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