【Unity】2Dシューティングを作ってみる話 #18
記事をご覧いただき、誠にありがとうございます。
後はそれぞれを相応しい・処理に合致する名前にしていきます。
次にContinueを非アクティブにします。
投稿主の無能です。
前回は、ゲームの中断を実装しました。
今回は前回の続きで、コンティニューの実装をしたいと思います。
コンティニューの実装
ではコンティニューの実装ですが、ゲームの中断と同様にUIから作成します。
えー、またUIを作るの…?と思われた方、安心してください。
既に前回のゲームの中断で基礎が出来ています。
難しいUIでなければ、複製して文言を変えるだけで大丈夫です。
何故かと言うと、要点としては
- GameOver
- コンティニューしますか?
- はいボタン
- いいえボタン
だけです。
前回のゲームの中断と似ていますね。
要はゲームオーバーの表示と質問→質問に応答するボタンという感じです。
なので簡単にUIを実装できます。
では早速やってみましょう。
ゲームの中断UIを複製する
ゲームの中断のUIの頭(親オブジェクト)はPauseになっています。
そのPauseを複製してやれば大丈夫です。
Pauseを複製
GameOverで問われることと言えば、まずはGameOverの表示、そしてコンティニューするかでですね。
GameOverは前回作成したPauseを変更すればいいので楽勝ですし、違う文言はテキストのPauseの部分だけになります。
GameOverという表示の下にYes・Noボタンがあるという構造も同じなので、名前を変えて整理してみましょう。
名前を変えて複製したオブジェクトを整理してみる
これでオブジェクトの名前の整理が出来ました。
次に各オブジェクトで表示させる文言を変更します。
Text_GameOverはGameOverを伝える文言になります。
一度Continueをアクティブにしてから作業してみます。
Text_GameOverの文言を変更する
Gameビュー
これだけでコンティニューのUIが出来ました。
もし違うコンティニュー用のUIが必要であれば作成しても問題ありません。
コンティニュー処理を実装
ではスクリプトでコンティニューの処理を実装します。
Noボタンの場合は、ゲームの中断と変わらずアプリを終了するので変更はありません。
ということは、Yesボタンの処理だけ作れば大丈夫です。
このようになります。
GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
[SerializeField] GameObject ShotButton; // 画面上の発射ボタン
[SerializeField] GameObject UI_Pause; // ゲーム中断のUI
[SerializeField] GameObject UI_Continue; // コンティニューのUI
int pause = 0, pauseRelease = 1; // ゲーム中断時のタイムスケール
void Start()
{
#if PLATFORM_STANDALONE_WIN
ShotButton.SetActive(false);
#endif
}
void Update()
{
// Escキーが押された場合
if (Input.GetKey(KeyCode.Escape))
{
// ゲーム中断のUIをアクティブ
UI_Pause.SetActive(true);
// ゲーム中断処理
GamePause();
}
}
// ゲーム中断
void GamePause()
{
// タイムスケールを0にする
Time.timeScale = pause;
}
// ゲーム中断解除
public void GamePauseRelease()
{
// タイムスケールを1にする
Time.timeScale = pauseRelease;
// ゲーム中断のUIを非アクティブ
UI_Pause.SetActive(false);
}
// ゲーム終了
public void GameExit()
{
// アプリケーションを終了する
Application.Quit();
}
// コンティニューのUIを表示
public void VisibleUI_Continue()
{
// ゲームを中断
GamePause();
// コンティニューのUIをアクティブ
UI_Continue.SetActive(true);
}
// コンティニュー
public void StageContinue()
{
// 中断を解除
Time.timeScale = pauseRelease;
// 現在のシーンを再読み込み
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
では見ていきましょう。
メンバ変数にUI_Pause同様にコンティニューのUIを追加しています。
ゲームオーバーの際にアクティブにして表示させるためですね。
VisibleUI_Continue関数でコンティニューのUIをアクティブにするのですが、一度ゲームの中断と同様にゲームの流れを止めます。
何故ならゲームオーバーとなっても、いなくなった(破棄された)のはプレイヤーだけなので、ゲームの世界では絶えず敵が生成され続けてしまうからです。
ゲームの流れを止めた後にUIを表示します。
UIを表示した後はYes・Noボタンだけが操作可能なので、Yesボタンが押された場合にコンティニューの処理が走るようにしてあげます。
コンティニューの処理ですが、usingの部分にUnityEngine.SceneManagementが追加されています。
これはSceneManagerを使う時に必要になるものです。
StageContinue関数で、現在のシーンを再読み込みしています。
括弧内はどうなっているかと言うと、現在のアクティブになっているシーン番号を取得しています。
シーン番号と言うのは、ビルド設定画面で確認できます。
シーンの右側の数字がシーン番号
これはシーンを追加・削除・並び替えをすると自動的に割り振られるようになっています。
ですのでシーン一つひとつが必ずユニークな番号を持っています。
括弧内ではこの番号を取得していたんですね。
一つ注意点として、タイムスケールを戻さないとシーンを再読み込みしても止まったままになります。
ですのでStageContinue関数ではシーン再読み込み前にタイムスケールを戻しておきます。
結構忘れがちな処理なので覚えておきましょう。
ではYesボタンのOnClick設定を変更しましょう。
YesボタンのOnClick設定を変更する
Continueを非アクティブにする
次にGameManagerに追加されたUI_Continueを設定します。
GameManagerに追加されたUI_Continueを設定する
これでコンティニューのUIの準備が出来ました。
あとはこのUIをアクティブにするには、PlayerControllerからGameManagerにアクセスして、VisibleUI_Continue関数を呼ぶようにすれば良いので、PlayerControllerにその処理を追記します。
このようになります。
PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour
{
[Header("移動関連")]
[SerializeField] float moveSpeed = 1f; // プレイヤーの移動速度
[SerializeField] float marginX = 1f, marginY = 1f; // 余白x・y
[Header("ショット関連")]
[SerializeField] GameObject playerBullet; // 弾のプレハブ
[SerializeField] float bulletSpeed = 10f; // 弾の速度
[SerializeField] Transform playerFirePos; // 発射ポイント
[SerializeField] float bulletDelay = 1f; // 発射の閾値
[Header("バーチャルスティック")][SerializeField] VariableJoystick joyStick;
[Header("爆発エフェクト")][SerializeField] GameObject playerExplosion;
[Header("ゲームマネージャー")][SerializeField] GameManager gameManager;
Rigidbody2D playerRB; // プレイヤーのRigidbody
Vector2 moveDirection, min, max; // プレイヤーのベクトル,画面サイズの最小ベクトル/最大ベクトル
Vector2 playerPos; // プレイヤーの移動制限用
float bulletInterval; // 弾の発射間隔管理用
bool isShotPressed = false; // 発射ボタン管理用
void Start()
{
// プレイヤーのRigidbodyを取得
playerRB = GetComponent<Rigidbody2D>();
}
void Update()
{
// 入力受付
InputProcess();
// バーチャルスティック処理 ---
// バーチャルスティックに何らかの入力が合った場合
// (joyStickが0では無い場合)
if (joyStick.Direction != Vector2.zero)
{
// moveDirectionをバーチャルスティックの入力にする
moveDirection = joyStick.Direction;
}
// --- ここまで
// 弾の発射 ---
// deltaTimeを加算
bulletInterval += Time.deltaTime;
// 発射間隔が閾値未満の場合
if (bulletInterval < bulletDelay)
{
// 処理を中断
return;
}
// スペースキーが押された場合/isShotPressedがtrueの場合
if (Input.GetKey(KeyCode.Space) || isShotPressed)
{
// 弾の発射関数を呼ぶ
PlayerShot(playerFirePos);
}
// 発射間隔をリセット
bulletInterval = 0;
// --- ここまで
}
void FixedUpdate()
{
// プレイヤーを動かす
PlayerMove();
}
// 入力受付
void InputProcess()
{
// 入力を正規化して受け取る
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
// 入力を正規化する
moveDirection = new Vector2(x, y).normalized;
}
// プレイヤーを動かす
void PlayerMove()
{
// 移動制限
MoveClamp();
// プレイヤーを動かす
playerRB.velocity = moveDirection * moveSpeed;
}
// プレイヤーの移動制限
void MoveClamp()
{
// 最後の移動地点を取得
playerPos = transform.position;
// ビューポート座標からワールド座標を取得
min = Camera.main.ViewportToWorldPoint(Vector2.zero);
max = Camera.main.ViewportToWorldPoint(Vector2.one);
// 移動を制限する
playerPos.x = Mathf.Clamp(playerPos.x, min.x + marginX, max.x - marginX);
playerPos.y = Mathf.Clamp(playerPos.y, min.y + marginY, max.y - marginY);
// プレイヤーの位置を取得した最後の地点にする
transform.position = playerPos;
}
// 弾の発射
void PlayerShot(Transform firePos)
{
// 弾を生成
GameObject bulletClone = Instantiate(playerBullet, firePos.position, Quaternion.identity);
// 弾のRigidbodyに速度ベクトルをつける
bulletClone.GetComponent<Rigidbody2D>().velocity = new Vector2(bulletSpeed, 0);
}
// 接触判定
private void OnTriggerEnter2D(Collider2D collision)
{
// 敵の場合
if (collision.gameObject.CompareTag("Enemy"))
{
// 爆発演出
PlayerExplosion(collision);
}
// 敵の弾の場合
else if (collision.gameObject.CompareTag("EnemyBullet"))
{
// 爆発演出
PlayerExplosion(collision);
}
// プレイヤーの弾の場合
else if (collision.gameObject.CompareTag("PlayerBullet"))
{
// 処理を中断
return;
}
// コンティニュー
Continue();
}
// 爆発演出
void PlayerExplosion(Collider2D collision)
{
// 接触した方を破棄
Destroy(collision.gameObject);
// 自身を破棄
Destroy(gameObject);
// プレイヤーの位置に爆発オブジェクトを作成
Instantiate(playerExplosion, playerPos, Quaternion.identity);
}
// 発射ボタンが押された時のイベント
public void OnPointerDown()
{
// 発射ボタンのフラグを変更
isShotPressed = true;
}
// 発射ボタンが元に戻った時のイベント
public void OnPointerUp()
{
// 発射ボタンのフラグを変更
isShotPressed = false;
}
// コンティニュー
void Continue()
{
// ゲームマネージャーのコンティニューUIを表示
gameManager.VisibleUI_Continue();
}
}
では見ていきましょう。
メンバ変数にGameManagerを追加しています。
これはGameManagerの中にあるVisibleUI_Continue関数にアクセスするためです。
次に接触判定のOnTriggerEnter2D関数内のif文に、PlayerBulletに接触した場合のelse if文を追加しています。
これは何故かと言うと、プレイヤーが弾を撃った瞬間も接触判定がなされてしまうので、PlayerBulletの場合は処理を中断します。
そしてOnTriggerEnter2D関数の最後にContinue関数を呼んでいます。
Continue関数では、GameManagerのVisibleUI_Continue関数を呼びます。
これでコンティニューのUIが表示されるので、コンティニューが可能になります。
ではPlayerで追加されたgameManagerを設定します。
HierarchyビューにあるGameManagerオブジェクトをドラッグして設定できます。
Playerに追加されたgameManagerを設定する
コンティニュー出来るようになったか、ゲームを実行して確認します。
確認することは
- プレイヤーが弾を発射してもコンティニューの処理にならない(つまり何も起きない事)を確認
- プレイヤーが敵の弾を受けた、若しくは敵と接触したらコンティニューのUIが表示されることを確認
- コンティニューのUIでYesを押したらシーンが再読み込みされるかを確認
の三点になります。
プレイヤーが弾を撃っても問題無し
コンティニューのUIが表示された
Yesボタンでシーンが再読み込みされた
これでゲームの中断とコンティニューの処理が実装出来ました。
まとめ
今回は
コンティニューの処理を実装した
という事をやりました。
次回は、ゲームの中断とコンティニューの処理が正しく出来るかを確認するため、各デバイス向けにビルドしていきます。
では、また次回!












コメント
コメントを投稿