【Unity】2Dシューティングを作ってみる話 #25
記事をご覧いただき、誠にありがとうございます。
Item_Healプレハブを複製し、「Item_Score」という名前にします。
投稿主の無能です。
前回は、アイテムがランダムで定期的に生成される処理を実装しました。
またその際に発生したエラーを解決しました。
今回はその他のアイテムを実装したいと思います。
その他のアイテムを実装する
HP回復以外のアイテムはどんなものが良いか考えてみます。
スコアを加算する、1up(残機が増える)、スピードアップ等々…挙げればキリがないですね。
ではまず、スコアを加算するアイテムを追加してみましょう。
スコアを加算するアイテムを作る
スコア加算アイテム用の画像を用意してUnityにインポートします。
スコア加算アイテムは黒にしました。
画像をインポート
Item_Healを複製し名前を「Item_Score」にする
後は画像の大きさを調整して、Sprite Rendererで画像を設定してやれば、土台の完成です。
画像の大きさを調整
Item_ScoreのSprite Rendererで画像を設定
後はスクリプトでスコア加算を実装してやります。
スクリプトでスコアを加算する
スコアの加算の処理は、敵を倒したらの時にGameManagerに実装しました。
ですのでスコア加算アイテムを取得したらItemControllerからGameManagerのScoreAdded関数を呼んでやれば良いという事になります。
このようになります。
ItemController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class ItemController : MonoBehaviour
{
#region Variables
#region var-CapsuleBase
[Header("アイテムの基本パラメーター")]
[SerializeField] float rotateSpeed = 10.0f; // アイテムの回転速度
[SerializeField] float dropSpeed = 1.0f; // アイテムの進行速度
[SerializeField] int itemType = 0; // アイテムの種類
#endregion
#region var-CapsuleIndividual
[Header("アイテム個別のパラメーター")]
[SerializeField] int healPoint = 5; // 回復アイテムの回復量
[SerializeField] int itemScore = 5000; // スコアアイテムのスコア増加量
#endregion
#region var-Controller
[Header("コントローラー関連")]
[SerializeField] PlayerController player; // プレイヤーコントローラー
[SerializeField] GameManager gameManager; // ゲームマネージャー
Rigidbody2D capsuleRB; // アイテムのリジッドボディ
#endregion
#region others
Collider2D itemCollider; // アイテムのコライダー
#endregion
#region Internal
enum itemEffectType { heal, addScore }; // 内部処理用(アイテムの効果の種類)
#endregion
#endregion
#region Methods
#region Start
void Start()
{
// カプセルのリジッドボディを取得
capsuleRB = GetComponent<Rigidbody2D>();
// ゲームマネージャーを取得
gameManager = GameObject.FindWithTag("GameManager").GetComponent<GameManager>();
// プレイヤーコントローラーを取得
player = GameObject.FindWithTag("Player").GetComponent<PlayerController>();
// アイテムのコライダーを取得
itemCollider = GetComponent<Collider2D>();
}
#endregion
#region Update
void Update()
{
// アイテムをプレイヤーの方向へ動かす
capsuleRB.velocity = new Vector2(-dropSpeed, 0);
// アイテムを回転させる
capsuleRB.transform.Rotate(0, 0, rotateSpeed);
}
#endregion
#region OnTriggerEnter2D
// 接触判定
private void OnTriggerEnter2D(Collider2D collision)
{
// 接触したオブジェクトのタグがPlayerだった場合
if (collision.gameObject.CompareTag("Player"))
{
// アイテムの効果を呼び出す
ItemEffect(itemType, itemCollider);
}
}
#endregion
#region ItemEffect
// アイテムの効果
void ItemEffect(int iType, Collider2D collision)
{
// 引数iTypeで分岐
switch (iType)
{
// 回復効果の場合
case (int)itemEffectType.heal:
// プレイヤーのHP表示を更新
player.PlayerHPChanged(iType, healPoint, collision);
// switch文を抜ける
break;
// スコア加算の場合
case (int)itemEffectType.addScore:
// スコアを加算する
gameManager.ScoreAdded(itemScore);
// 自身を破棄
Destroy(gameObject);
// switch文を抜ける
break;
// 該当なしの場合
default:
// switch文を抜ける
break;
}
}
#endregion
#endregion
}では見ていきましょう。
メンバ変数にitemScoreという敵と同じように加算するスコアを追加しています。
そしてenumのitemEffectTypeにaddScoreを追加しました。
後はItemEffect関数のswitch文で、スコア加算の分岐を増やしています。
GameManagerのScoreAdded関数を呼んでitemScoreに設定してあるものを加算します。
呼んだ後はアイテム自身を破棄します。
では追加された項目を設定します。
Item_Scoreのアイテムの種類と加算する値を設定
この時のアイテムの種類の設定ですが、enumのitemEffectTypeで振り分けられた連番と同じ値にしてください。
そうでないとswitch文でdefaultになってしまうか、違うアイテムの効果になってしまいます。
ItemGeneratorの項目も追加します。
ItemGeneratorのItemsに追加する
これで追加の項目も設定できたので、ゲームを実行して確認します。
スコアが加算された
スコアが加算されましたね。
きちんとHP回復ともアイテムの効果が違っています。
では次に1upのアイテムを実装してみます。
残機を増やすアイテムを作る
これまでの手順と同様に1upのアイテムを作ります。
1upは黄色にしました。
画像を用意してインポート
画像の大きさを設定
アイテムを複製
画像を適用
これで外側は出来たので、あとはスクリプトでの実装になります。
このようになります。
ItemController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class ItemController : MonoBehaviour
{
#region Variables
#region var-CapsuleBase
[Header("アイテムの基本パラメーター")]
[SerializeField] float rotateSpeed = 10.0f; // アイテムの回転速度
[SerializeField] float dropSpeed = 1.0f; // アイテムの進行速度
[SerializeField] int itemType = 0; // アイテムの種類
#endregion
#region var-CapsuleIndividual
[Header("アイテム個別のパラメーター")]
[SerializeField] int healPoint = 5; // 回復アイテムの回復量
[SerializeField] int itemScore = 5000; // スコアアイテムのスコア増加量
[SerializeField] int remainingAdd = 1; // 残機の増加量
#endregion
#region var-Controller
[Header("コントローラー関連")]
[SerializeField] PlayerController player; // プレイヤーコントローラー
[SerializeField] GameManager gameManager; // ゲームマネージャー
Rigidbody2D capsuleRB; // アイテムのリジッドボディ
#endregion
#region others
Collider2D itemCollider; // アイテムのコライダー
#endregion
#region Internal
enum itemEffectType { heal, addScore, oneUp }; // 内部処理用(アイテムの効果の種類)
#endregion
#endregion
#region Methods
#region Start
void Start()
{
// カプセルのリジッドボディを取得
capsuleRB = GetComponent<Rigidbody2D>();
// ゲームマネージャーを取得
gameManager = GameObject.FindWithTag("GameManager").GetComponent<GameManager>();
// プレイヤーコントローラーを取得
player = GameObject.FindWithTag("Player").GetComponent<PlayerController>();
// アイテムのコライダーを取得
itemCollider = GetComponent<Collider2D>();
}
#endregion
#region Update
void Update()
{
// アイテムをプレイヤーの方向へ動かす
capsuleRB.velocity = new Vector2(-dropSpeed, 0);
// アイテムを回転させる
capsuleRB.transform.Rotate(0, 0, rotateSpeed);
}
#endregion
#region OnTriggerEnter2D
// 接触判定
private void OnTriggerEnter2D(Collider2D collision)
{
// 接触したオブジェクトのタグがPlayerだった場合
if (collision.gameObject.CompareTag("Player"))
{
// アイテムの効果を呼び出す
ItemEffect(itemType, itemCollider);
}
}
#endregion
#region ItemEffect
// アイテムの効果
void ItemEffect(int iType, Collider2D collision)
{
// 引数iTypeで分岐
switch (iType)
{
// 回復効果の場合
case (int)itemEffectType.heal:
// プレイヤーのHP表示を更新
player.PlayerHPChanged(iType, healPoint, collision);
// switch文を抜ける
break;
// スコア加算の場合
case (int)itemEffectType.addScore:
// スコアを加算する
gameManager.ScoreAdded(itemScore);
// 自身を破棄
Destroy(gameObject);
// switch文を抜ける
break;
// 1upの場合
case (int)itemEffectType.oneUp:
// 残機を増やす
player.RemainingCounter(remainingAdd);
// 自身を破棄
Destroy(gameObject);
// switch文を抜ける
break;
// 該当なしの場合
default:
// switch文を抜ける
break;
}
}
#endregion
#endregion
}
では見ていきましょう。
メンバ変数はプレイヤーの残機を増やすremainingAddを追加し、itemEffectTypeにoneUpを追加しました。
ItemEffect関数にはoneUpの処理の分岐を追加しています。
PlayerControllerのRemainingCounter関数を少々改造する必要がありますが、RemainingCounter関数を呼ぶ際に引数に増やす残機を渡してあげれば大丈夫です。
そしてアイテム自身を破棄してswitch文を抜けます。
ではPlayerControllerの方も改造します。
このようになります。
PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour
{
#region Player-Variables
#region var-Move
[Header("移動関連")]
[SerializeField] float moveSpeed = 1f; // プレイヤーの移動速度
[SerializeField] float marginX = 1f, marginY = 1f; // 余白x・y
#endregion
#region var-Shot
[Header("ショット関連")]
[SerializeField] GameObject playerBullet; // 弾のプレハブ
[SerializeField] float bulletSpeed = 10f; // 弾の速度
[SerializeField] Transform playerFirePos; // 発射ポイント
[SerializeField] float bulletDelay = 1f; // 発射の閾値
#endregion
#region var-HP
[Header("プレイヤーのHP")]
[SerializeField] int playerHPMax = 10; // プレイヤーのHP(初期値:10)
[SerializeField] int playerHPCurrent; // プレイヤーの現在のHP
[SerializeField] TMP_Text playerHPText; // プレイヤーのHP表示のテキスト
[SerializeField] Slider playerHPGauge; // プレイヤーのHP表示のゲージ
#endregion
#region var-Remaining
[Header("プレイヤーの残機")]
[SerializeField] TMP_Text remainingCount_Text; // プレイヤーの残機表示のテキスト
[SerializeField] GameObject[] remainingCount_Icon; // プレイヤーの残機表示のアイコン
[SerializeField] int remainingCountMax = 5; // プレイヤーの残機の最大数
[SerializeField] int remainingCountCurrent = 3; // プレイヤーの残機の現在値
[SerializeField] Transform respawnPos; // プレイヤーの復活地点
#endregion
#region var-Controllers
[Header("バーチャルスティック")][SerializeField] VariableJoystick joyStick;
[Header("爆発エフェクト")][SerializeField] GameObject playerExplosion;
[Header("ゲームマネージャー")][SerializeField] GameManager gameManager;
[Header("エネミーコントローラー")][SerializeField] EnemyController enemyController;
#endregion
#region base-Variables
Rigidbody2D playerRB; // プレイヤーのRigidbody
Vector2 moveDirection, min, max; // プレイヤーのベクトル,画面サイズの最小ベクトル/最大ベクトル
Vector2 playerPos; // プレイヤーの移動制限用
float bulletInterval; // 弾の発射間隔管理用
bool isShotPressed = false; // 発射ボタン管理用
#endregion
#region Internal-Variables
int playerHPMin = 0; // HPの最小値
enum calcType { heal, damage, display } // HP表示・計算のタイプ
int remainingCountMin = 0; // プレイヤーの残機の最小値
int changeRemaining = -1, remainingCountDisp = 0; // プレイヤーの残機減少用,表示用
#endregion
#endregion
#region Method
#region Start
void Start()
{
// プレイヤーのRigidbodyを取得
playerRB = GetComponent<Rigidbody2D>();
// プレイヤーのHPを初期化
playerHPCurrent = playerHPMax;
// プレイヤーのHPを表示
PlayerHPDisplay();
// プレイヤーの残機を表示
RemainingCounter(remainingCountDisp);
}
#endregion
#region Update
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;
// --- ここまで
}
#endregion
#region FixedUpdate
void FixedUpdate()
{
// プレイヤーを動かす
PlayerMove();
}
#endregion
#region InputProcess
// 入力受付
void InputProcess()
{
// 入力を正規化して受け取る
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
// 入力を正規化する
moveDirection = new Vector2(x, y).normalized;
}
#endregion
#region PlayerMove
// プレイヤーを動かす
void PlayerMove()
{
// 移動制限
MoveClamp();
// プレイヤーを動かす
playerRB.velocity = moveDirection * moveSpeed;
}
#endregion
#region MoveClamp
// プレイヤーの移動制限
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;
}
#endregion
#region PlayerShot
// 弾の発射
void PlayerShot(Transform firePos)
{
// 弾を生成
GameObject bulletClone = Instantiate(playerBullet, firePos.position, Quaternion.identity);
// 弾のRigidbodyに速度ベクトルをつける
bulletClone.GetComponent<Rigidbody2D>().velocity = new Vector2(bulletSpeed, 0);
}
#endregion
#region OnTriggerEnter2D
// 接触判定
private void OnTriggerEnter2D(Collider2D collision)
{
// 敵の場合
if (collision.gameObject.CompareTag("Enemy"))
{
// ダメージの算出
PlayerHPChanged((int)calcType.damage, EnemyController.bodyDamage, collision);
}
// 敵の弾の場合
else if (collision.gameObject.CompareTag("EnemyBullet"))
{
// ダメージの算出
PlayerHPChanged((int)calcType.damage, EnemyController.shotDamage, collision);
}
// プレイヤーの弾の場合
else if (collision.gameObject.CompareTag("PlayerBullet"))
{
// 処理を中断
return;
}
}
#endregion
#region PlayerExplosion
// 爆発演出
void PlayerExplosion(Collider2D collision)
{
// 接触した方を破棄
Destroy(collision.gameObject);
// 自身を非アクティブ
gameObject.SetActive(false);
// プレイヤーの位置に爆発オブジェクトを作成
Instantiate(playerExplosion, playerPos, Quaternion.identity);
}
#endregion
#region OnPointerDown
// 発射ボタンが押された時のイベント
public void OnPointerDown()
{
// 発射ボタンのフラグを変更
isShotPressed = true;
}
#endregion
#region OnPointerUp
// 発射ボタンが元に戻った時のイベント
public void OnPointerUp()
{
// 発射ボタンのフラグを変更
isShotPressed = false;
}
#endregion
#region Continue
// コンティニュー
void Continue()
{
// ゲームマネージャーのコンティニューUIを表示
gameManager.VisibleUI_Continue();
}
#endregion
#region PlayerHPDisplay
// プレイヤーのHP表示・更新
void PlayerHPDisplay()
{
// HPのテキストを表示・更新
playerHPText.SetText("PlayerHP : " + playerHPCurrent + " / " + playerHPMax);
// 現在のHPをゲージに反映
playerHPGauge.value = playerHPCurrent;
}
#endregion
#region PlayerHPChanged
// プレイヤーのダメージ算出
public void PlayerHPChanged(int cType, int damage, Collider2D collision)
{
// switchで条件分岐:対象→cType
switch (cType)
{
// cTypeが回復だった場合
case (int)calcType.heal:
// ダメージ分を加算する
playerHPCurrent += damage;
// 接触されたオブジェクトを破棄
Destroy(collision.gameObject);
// プレイヤーのHPが最大値以上の場合
if (playerHPCurrent >= playerHPMax)
{
// プレイヤーのHPを最大値にする
playerHPCurrent = playerHPMax;
}
// switch文を抜ける
break;
// cTypeがダメージだった場合
case (int)calcType.damage:
// ダメージ分を減算する
playerHPCurrent -= damage;
// 接触したオブジェクトを消す
Destroy(collision.gameObject);
// プレイヤーのHPが0以下(最小値以下)だった場合
if (playerHPCurrent <= playerHPMin)
{
// 爆発演出
PlayerExplosion(collision);
// プレイヤーのHPを0(最小値)にする
playerHPCurrent = playerHPMin;
// 残機を更新
RemainingCounter(changeRemaining);
}
// それ以外の場合
else
{
// switch文を抜ける
break;
}
// switch文を抜ける
break;
// cTypeが表示だった場合
case (int)calcType.display:
// switch文を抜ける
break;
// 該当なしの場合
default:
// HP表示の分岐へジャンプ
goto case (int)calcType.display;
}
// プレイヤーのHPを表示
PlayerHPDisplay();
}
#endregion
#region RemainingCountDisplay
// 残機表示・更新
void RemainingCountDisplay()
{
// 現在の残機が最大値を超えている場合
if (remainingCountCurrent > remainingCountMax)
{
// 残機を最大値にする
remainingCountCurrent = remainingCountMax;
}
// プレイヤーの残機表示のテキストを更新
remainingCount_Text.SetText("Remaining Count:" + remainingCountCurrent + "/" + remainingCountMax);
// 残機の最大数までループ
for (int i = 0; i < remainingCountMax; i++)
{
// 現在の残機数がi以下の場合
if (remainingCountCurrent <= i)
{
// 残機アイコンを非アクティブ
remainingCount_Icon[i].SetActive(false);
}
// それ以外の場合
else
{
// 残機アイコンをアクティブ
remainingCount_Icon[i].SetActive(true);
}
}
}
#endregion
#region RemainingCounter
// 残機カウントを更新
public void RemainingCounter(int addRemaining)
{
// 残機に変更がない場合(addRemainingが0の場合)
if (addRemaining == remainingCountDisp)
{
// 残機を表示する
RemainingCountDisplay();
}
// 残機を増やす場合(addRemainingがプラスの場合)
else if (addRemaining > remainingCountDisp)
{
// 残機を増やす
remainingCountCurrent += addRemaining;
// 残機が最大値を超える場合
if (remainingCountCurrent > remainingCountMax)
{
// 現在の残機を最大値にする
remainingCountCurrent = remainingCountMax;
}
}
// 残機を減らす場合(addRemainingがマイナスの場合)
else if (addRemaining < remainingCountDisp)
{
// 残機を減らす
remainingCountCurrent += addRemaining;
// 残機が最小値(0)以下の場合
if (remainingCountCurrent < remainingCountMin)
{
// 残機を最小値にする
remainingCountCurrent = remainingCountMin;
// コンティニュー処理
Continue();
}
// それ以外の場合
else
{
// プレイヤーを復活地点に移動
PlayerRespawn(respawnPos);
}
}
// 残機表示を更新
RemainingCountDisplay();
}
#endregion
#region PlayerRespawn
// プレイヤーの復活
void PlayerRespawn(Transform rePos)
{
// プレイヤーをアクティブ
gameObject.SetActive(true);
// プレイヤーを復活地点に移動
gameObject.transform.position = rePos.position;
// プレイヤーのHPを戻す
playerHPCurrent = playerHPMax;
// プレイヤーのHPを更新
PlayerHPDisplay();
}
#endregion
#endregion
}
では見ていきましょう。
メンバ変数にプレイヤーの残機を減らすchangeRemainingと表示用のremainingCountDispというint型の変数を追加しました。
Start関数で残機の表示をしているのですが、引数無しのRemainingCounter関数に引数を追加したのでremainingCountDispを渡します。
同様にPlayerHPChanged関数でもRemainingCounter関数に引数を渡します。
RemainingCountDisplay関数では、これまで残機を減らすものばかりだったので、残機が最大値を超えた場合、最大値を超えないようにする処理を追加しています。
そしてRemainingCounter関数は、外部アクセスが可能になるよう新たにpublicにしています。
引数を渡されるようになったので、引数を条件として分岐させています。
0の場合は残機の表示を更新するのみにしました。
0を超える場合、つまり残機がプラスされる場合は、最大値を超えないようにプラスしています。
0未満の場合、つまり残機がマイナスされる場合は、最小値を超えないようにマイナスしています。
ここで-=と減算してしまいそうですが、changeRemainingがマイナスなので正と負の足し算になるので問題ありません。
あとは変わらず残機が残っているならプレイヤーが復活地点で復活、残っていなければコンティニュー処理となります。
では追加された項目を設定します。
追加された項目を設定
ではゲームを実行して確認します。
今回確認することは、1upのアイテムを取得したら
- 残機が増える
- 残機が最大値を超えない
を確認します。
アイテムを取得して残機が増える:OK
最大値を超えない:OK
これで残機を増やすアイテムを実装できました。
まとめ
今回は
- その他のアイテムを実装した
という事をやりました。
次回は、アイテムやプレイヤーのHPを実装したので、弾が当たった時やアイテム取得時の効果音を追加したいと思います。
では、また次回!














コメント
コメントを投稿