【Unityゲーム制作】自作ゲームを教材に制作講座風にリメイクする#3 (Unity1week202102)
自作ゲームを教材にゲーム制作の流れを眺める企画の、第3回目。
今回は最後に余談として、制作時の失敗について触れています。
こんばんは、M橋です。
今回は、次の「3」と「4」に取り組む内容です。
- ステージを作成する
- ステージを回転させる処理を実装する
- プレイヤーの操作を受け付ける処理を実装する
- プレイヤーの操作と、ステージの回転を紐づける
- 球体を作成する
- 球体が落下する処理を実装する
- ステージとの衝突処理を実装する
- ゴールを作成する
- 球体がゴールに到達したことを判定する処理を実装する
- 障害物を作成する
- 球体との衝突処理を実装する
- 時間を計測するタイマーを作成する
- 時間を表示するUIを作成する
本記事の制作環境は以下の通りです。
- OS : Windows 10 64bit
- Unity : 2020.1.3f1 Personal
目標
前回の記事で、「オブジェクト(ステージ)を回転させる処理を実装する」ところまで行いました。
ですが、動きを眺めているだけではゲームになりません。
今回は、ゲームに必須の「プレイヤーの操作をゲーム内処理に紐づける」ことが目標です。
手順
ざっくりと以下のようになります。
- プレイヤーの操作を受け付ける処理を実装する
- 操作方法を決める
- プレイヤーの操作をスクリプトで受け取る処理を実装する
- プレイヤーの操作と、ステージの回転を紐づける
- プレイヤーの操作を、ゲーム内の意味のある値に変換する
- 操作から受け取った値をオブジェクトの回転処理に組み込む
順番に見ていきましょう。
操作方法を決める
一口に操作方法と言っても、最近では様々な選択肢があります。
最も多いと思われるのは、スマホゲーム等のタッチ・スワイプ等の操作。
PCゲームであればマウス・キーボード入力やゲームパッドも
操作方法の選択肢になります。
Unityではこれらの操作方法に合った処理が用意されているので
プレイヤーにどんな操作をさせたいか
というところから、ゲームでの実装を考えていきます。
今回はマウス一つで操作できることで進めていきます。
プレイヤーの操作をスクリプトで受け取る処理を実装する
プレイヤーのマウス操作を受け付けるために、次の関数があります。*1
public static bool GetMouseButton(int button); // (1) public static bool GetMouseButtonDown(int button); // (2) public static bool GetMouseButtonUp(int button); // (3)
それぞれ、次の用途に使えます。
- マウスが押されている間に、何か処理をしたい時
- マウスが押された瞬間に、何か処理をしたい時
- マウスが押された後に離された瞬間に、何か処理をしたい時
例えば、次のスクリプトは、それぞれの処理が行われたフレームを表示させます。
void Update() { if (Input.GetMouseButtonDown(0)) { Debug.Log($"Mouse Pressed at {Time.frameCount}"); } if (Input.GetMouseButton(0)) { Debug.Log($"Mouse Held at {Time.frameCount}"); } if (Input.GetMouseButtonUp(0)) { Debug.Log($"Mouse Released at {Time.frameCount}"); } }
このスクリプトを実行すると、マウス操作によって下のようなログが表示されます。
これらの関数を使うことで、マウス操作に応じた何らかの処理をすることが出来ます。
プレイヤーの操作を、ゲーム内の意味のある値に変換する
この辺りからゲーム開発っぽくなります。*2
というのも、ここまではお決まりの作業のようなものだからです。
オブジェクトの回転も、マウス操作の受付も、
既にある物・手順をなぞっているに過ぎません。
しかし、ここで意味のある値を考え始めたところから、
このゲームをどう構築してやろうかという
開発者の意思が試されることになります。
と、大げさな話は脇に置きまして、
ここで「プレイヤーの操作」とは何かをおさらいしましょう。
今回のゲームでは「重力の向きを変えることがプレイヤーの操作」としています。
具体的には「ステージを回転させることがプレイヤーの操作」です。
では、ステージを回転とは何でしょうか。
前回、ステージを回転させる処理を実装しました。
実装したコードを抜粋すると
transform.Rotate(new Vector3(0f, 0f, rotationSpeed) * Time.deltaTime);
transform.Rotateという関数を使っていますね。
transform.Rotateは、「オブジェクトを、現在の状態から指定された値だけ回転させる」機能を持った関数です。
そのため引数に設定する値は、「現在の状態から回転させたい量」になります。
前回の実装では、rotationSpeedという固定の値を使って、
毎フレーム回転させたい量を計算して引数にしていたのです。*3
では、この回転させたい量をプレイヤー操作から得るにはどうすれば良いでしょうか。
結論としては、次の流れで取得することができます。
- 前フレームのマウス位置と現フレームのマウス位置の差分を計算する
- マウス位置の差分をステージの回転量に変換する
- 次フレームの計算用に現フレームのマウス位置を保存する
1.で、プレイヤーの操作によって変化する値を取得します。
2.で、その値を今回の目的である回転させたい量に変換します。
3.は、今回の実装が前フレームとの差分を基準であるために必要な内部処理です。*4
実装では次のようなスクリプトになります。
Unity上でマウス位置というと、Vector3という3次元の型で扱われますが、
今回は3次元も情報は不要です。
マウスを左右に移動させればステージが回転するという法則を作り、
X座標のみを扱うようにしています。*5
using UnityEngine; namespace chapter2 { /// <summary> /// ステージの動きを制御するクラス /// </summary> public class StageController : MonoBehaviour { public float sensitivity = 40f; // マウス感度 private float rotationSpeed = 0f; // 回転速度 private float previousMousePositionX; // 前フレームでのマウスカーソル位置(X座標) // Update is called once per frame void Update() { if (Input.GetMouseButtonDown(0)) { // マウスクリックの最初の位置を保存する previousMousePositionX = Input.mousePosition.x; // (0) } if (Input.GetMouseButton(0)) { // マウスポインタの移動量を計算する float move = Input.mousePosition.x - previousMousePositionX; // (1) // マウスポインタの移動量を回転速度に変換する rotationSpeed = move * sensitivity; // (2) // マウスクリックの位置を保存する previousMousePositionX = Input.mousePosition.x; // (3) } // 回転処理 transform.Rotate(new Vector3(0f, 0f, rotationSpeed) * Time.deltaTime); } } }
Update関数の中身が一気に増えましたが、処理が4行追加されているだけです。
0. マウスクリックの最初の位置を保存する
if (Input.GetMouseButtonDown(0)) { previousMousePositionX = Input.mousePosition.x; // (0) }
最初にクリックした瞬間だけ、使う関数が異なるので条件式で分けています。
本質的には(3)と変わりませんので、ここでは説明を省略します。
1. マウスポインタの移動量を計算する
2. マウスポインタの移動量を回転速度に変換する
3. マウスクリックの位置を保存する
if (Input.GetMouseButton(0)) { float move = Input.mousePosition.x - previousMousePositionX; // (1) rotationSpeed = move * sensitivity; // (2) previousMousePositionX = Input.mousePosition.x; // (3) }
- (1) マウスポインタの移動量を計算する
現フレームのマウス位置はInput.mousePosition.xで取得できます。
そして前フレームでのマウス位置は、previousMousePositionXに保存しています。
この2つの差分を取ることで、1フレーム分のマウス移動量が計算できます。
- (2) マウスポインタの移動量を回転速度に変換する
moveは、(1)で計算したマウスの移動量でした。
それをそのままtransform.Rotateの引数に使っても良いのですが、
プレイヤーの操作に直接関わる箇所なので、調整をしたくなることが多々あります。
sensitivityは、その調整用に用意した変数です。
例えば、マウスを左に移動させたときに「ステージが左回転する or 右回転する」を
変化させたい時などに使います。
- (3) マウスクリックの位置を保存する
これは(1)で計算するために現在の値を保存しています。
(0)で書いた処理も、同じ理由ですね。
まとめ
今回は、プレイヤーの操作をゲームに反映させる部分として
マウス操作でステージを回転させることについて書きました。
次回はプレイヤーの分身となるキャラクターを実装していきます。
余談
さて、冒頭に挙げたUnity1Week参加中に失敗した話について、ここに書きます。
ここからは余談ですので、興味のある方以外はスルーしてしまってください。
記事中ではステージを回転させる処理について、唐突にtransform.Rotate関数を引っ張り出してきました。
ただ、実際の開発では、やりたいことを実現する手段は複数あります。
その中で、どれが今の開発に最適かは自分で判断する必要があります。*6
今回の場合は、プレイヤー操作を受け付ける手段と
ステージを回転させる手段の2つが該当します。
と言ってもこの2つは対になっているので、どちらかを決めるともう一方も殆ど決定されます。
実は私の場合、開発時点ではプレイヤー操作を受け付ける手段を先に考えていました。
その時の流れは、
- クリックされた時のマウス位置を保存する
- クリックされた時のステージの回転値を保存する
- 現フレームのマウス位置とクリック位置の差分をステージの回転量に変換する
となります。
記事中の流れと同じく3段階ですが、こちらはステージを回転させる手段の考えが少し複雑になっていたのです。
そして、そもそも上の流れで実装する必要性を考えるまでに
時間がかかってしまいました。いわゆる「手段が目的化する」状態です。
結果的に、ステージを回転させる手段から実現を考えた結果、
この記事と同じ流れに至っています。
なお、上の流れを実装したものを載せておきます。
(記事が長くなってしまったので、解説については省略させていただきます)
using UnityEngine; namespace chapter2 { /// <summary> /// ステージの動きを制御するクラス /// </summary> public class StageController2 : MonoBehaviour { public float sensitivity = 0.05f; // マウス感度 private float rotationValue = 0f; // 回転量 private Quaternion baseRotation; // クリック時の回転値 private float clickPositionX; // クリック位置(X座標) // Update is called once per frame void Update() { if (Input.GetMouseButtonDown(0)) { // マウスクリックの最初の位置を保存する clickPositionX = Input.mousePosition.x; // マウスクリック時点の回転の値を基準値として保存する baseRotation = transform.rotation; } if (Input.GetMouseButton(0)) { // マウスポインタの移動量を計算する float move = Input.mousePosition.x - clickPositionX; // マウスポインタの移動量を回転量に変換する rotationValue = move * sensitivity; } // 回転処理。基準値から回転量だけ回転させる transform.rotation = baseRotation * Quaternion.Euler(new Vector3(0f, 0f, rotationValue)); } } }
どのように実現するかが自由な分、なぜそれが必要なのかを
良く考える癖をつけておかないといけない事例でした。