シューティングゲームの自機の作り方

前回の作業で「メテオバスター」の制作に必要な下準備ができたので、ここから本腰を入れてゲームを作っていきます。

手始めにシューティングゲーム用のプレイヤーキャラクター(=自機)を作ってみましょう。

スポンサーリンク

今回のシューティングゲームの自機の仕様

まず、はじめに今回のシューティングゲームで「どういう動きの自機を作るか?」という仕様部分をまとめておきます。今回の自機の仕様は次の通りです。

  • 自機はマウスの方向を向くように回転する。
  • W,Sもしくは上下矢印キーで前進・後退する。

いたってシンプルですね。今回は簡単なので書き出すまでもないかもしれませんが、例えばアクションゲーム等ではプレイヤーの動きが複雑になりがちなので紙に仕様を書いて整理するのはとても有効です。仕様があやふやなままだと後で変更したくなって二度手間になる、といったことがよくあるので事前にできる限り固めておくようにしましょう。

自機のゲームオブジェクトの作り方

では仕様を固めたところで早速プレイヤーを作っていきます。まずは必要なコンポーネントをアタッチして設定を行いましょう。

まず下準備でインポートしてもらった素材のうち「Player」スプライトをシーンにドラッグ&ドロップしたら、次の画像のようにコンポーネントをアタッチして赤線の部分の設定を行ってください。

プレイヤーのコンポーネントの設定例

あと、画像では見切れてしまっていますがAudio Sourceコンポーネントもアタッチしておいてください。

自機を動かすためのC#スクリプト

そうしたらプレイヤーを動かすためのC#スクリプトを書いていきましょう。適当なフォルダに新しいC#スクリプトを作り、名前を「PlayerController」に変更したら次のコードを書いてください。

using System.Collections;
using UnityEngine;

public class PlayerController : MonoBehaviour
{

  public bool isActive = true;

  [Header("必要なコンポーネントを登録")]
  [SerializeField]
  Rigidbody2D rigidBody = null;
  [SerializeField]
  Transform bulletSpawn = null;
  [SerializeField]
  AudioSource audioSource = null;

  [Header("移動設定")]
  [SerializeField]
  float powerToMove = 10;

  [Header("射撃設定")]
  [SerializeField]
  GameObject bulletPrefab = null;
  [SerializeField, Min(0)]
  float fireInterval = 0.5f;
  [SerializeField]
  AudioClip fireSe = null;

  [Header("入力設定")]
  [SerializeField]
  string verticalButtonName = "Vertical";
  [SerializeField]
  string fireButtonName = "Fire1";

  bool fire = false;
  bool firing = false;
  float forwardInput;
  Vector2 mousePos;
  WaitForSeconds fireIntervalWait;
  Camera mainCamera;
  Transform thisTransform;
  Transform mainCameraTransform;

  void Start()
  {
    // transformでトランスフォームを参照すると少しだけ重いので、キャッシュしておく
    thisTransform = transform;
    mainCamera = Camera.main;
    mainCameraTransform = mainCamera.transform;

    // コルーチンの停止処理をキャッシュしておく
    // こうするとメモリにゴミが発生しづらくなり高速化できる
    fireIntervalWait = new WaitForSeconds(fireInterval);
  }

  void OnDisable()
  {
    StopCoroutine(nameof(Fire));
    firing = false;
  }

  void Update()
  {
    if (!isActive)
    {
      return;
    }

    GetInput();

    if (fire && !firing)
    {
      StartCoroutine(nameof(Fire));
    }
  }

  void FixedUpdate()
  {
    if (!isActive)
    {
      return;
    }

    MovePlayer();
  }

  void GetInput()
  {
    // 移動
    forwardInput = Input.GetAxis(verticalButtonName);

    // 方向
    // マウス座標(スクリーン座標)を取得し、ワールド座標に変換する
    Vector3 screenMousePos = Input.mousePosition;
    screenMousePos.z = mainCameraTransform.position.z;
    mousePos = mainCamera.ScreenToWorldPoint(screenMousePos);

    // 射撃
    fire = Input.GetButton(fireButtonName);
  }

  void MovePlayer()
  {
    // なめらかにマウスの方向を向く
    thisTransform.rotation = Quaternion.Lerp(thisTransform.rotation, Quaternion.LookRotation(Vector3.forward, (Vector3)mousePos - thisTransform.position), 0.1f);

    // 移動
    rigidBody.AddForce(thisTransform.up * forwardInput * powerToMove * rigidBody.mass, ForceMode2D.Force);
  }

  IEnumerator Fire()
  {
    firing = true;

    // 弾のゲームオブジェクトを生成
    Instantiate(bulletPrefab, bulletSpawn.position, thisTransform.rotation);

    yield return fireIntervalWait;

    firing = false;
  }

}

書けたらスクリプトをプレイヤーオブジェクトにアタッチして、PlayerControllerコンポーネントの空欄にリジッドボディなど必要なコンポーネントを設定してください。

なおスクリプトが少し長くなってしまいましたが、特に難しいことはやっていないので上から順番に読んでいただければ内容を理解できると思います。それでもいちおう簡単に解説をしておきますね。

C#スクリプトの解説

Start関数の処理

ソースコードのコメントの通りで、Start関数内では必要なものを変数にキャッシュしておきます。まあ今回のゲームはシンプルで軽いので別にやらなくても重くはなりませんが、一手間かけることで少し高速化できるということは覚えて頂いても損はないと思います。

Update関数の処理

Update関数では主に入力を取得する処理を行います。実際の入力受付処理はGetInput関数にある通りです。

また、もし射撃ボタン(デフォルトではマウス左クリック)が押されていて、かつFireコルーチンが処理中でなければFireコルーチンを開始して射撃を行います。

FixedUpdate関数の処理

FixedUpdate関数ではプレイヤーの移動処理を行います。実際の移動処理はMovePlayer関数の通りです。

MovePlayer関数でやっていることは

  1. 滑らかにマウスの方向を向く
  2. 入力に応じてプレイヤーのリジッドボディに力を加える

という2種類の処理です。これらは説明抜きだと少しわかりづらいので、もう少し詳しく説明しておきましょう。

移動処理1:滑らかにマウスの方向を向く

まずはプレイヤーの回転処理です。Quaternion.LookRotationを使ってマウスの方向を向かせます。もし、LookRotationを使うのが初めてという方はUnityのリファレンスを一読していただければと思います。

Quaternion-LookRotation - Unity スクリプトリファレンス
指定された forward と upwards 方向に回転します。

簡単に言うと引数の方向を向くような回転を取得するための関数です。ここでは

  • 第一引数(前方向)にワールド座標のZ方向
  • 第二引数(上方向)にマウス座標とプレイヤーの座標の差分

を入れています。これでXY平面上のプレイヤーがマウスの方向を向くように回転します。

ただし、そのままだと例えばマウスがプレイヤーの反対側に動いても一瞬でその方向を向くようになり不自然なので、Quaternion.Lerpを使って滑らかな回転になるように補間を行います。Lerp関数が初耳だという方はこちらも公式リファレンスを参照していただければ良いでしょう。

Quaternion-Lerp - Unity スクリプトリファレンス
a と b の間を t で補間し、その後、その結果を正規化します。パラメーター t は、 の範囲です。

ここでは第三引数(パラメーターt)は0.1としました(※パラメーターの値はお好みで大丈夫ですが、1に近いほど瞬間的に回転するようになります)。このあと実際にゲームを実行してみると滑らかに回転するようになるでしょう。

移動処理2:入力に応じてプレイヤーのリジッドボディに力を加える

次はプレイヤーの移動処理です。こちらは簡単で、Rigidbody2D.AddForce

  • 自機自身の向きに
  • プレイヤーの入力に応じた力を
  • 継続的に加える

といった処理になっています。

ちなみに今回は宇宙空間をイメージしたゲームなので、慣性があるほうが良いかなと思ってAddForceを使いました。他にはvelocityを直接操作する方法もあり、そちらは(物理的には正しくないが)パキッとした動きになるのでゲームによって使い分けると良いでしょう。

テストプレイして自機の動きを確認しよう

では最後にテストプレイして自機の動きを確認してみましょう。次のGIFのような動きになれば成功です。

自機の動きの例

(クリックで再生)

ちなみに…

  • まだ発射する弾のゲームオブジェクトを作っていないので射撃をするとエラーになると思います。弾は次のページで作りましょう。
  • 自機が画面外に出てしまいますが、これは後で直します。

次のページ→シューティングゲームの弾の作り方