エネミーのバレットにも、プレイヤーと同じように、BulletData に登録した情報を利用して、ダメージや速度、属性などを設定する制御を実装します。
<実装動画� .痢璽泪襦Ε┘優漾爾離丱譽奪函B粟�情報があるので、プレイヤーの選択しているバレットの種類によって属性間の相性が反映される>
動画ファイルへのリンク
<実装動画�◆.椒垢離丱譽奪函2菫�だけではなく、ダメージも固定値ではなく、BulletData の値を参照している>
動画ファイルへのリンク
以下の内容で順番に実装を進めていきます。
発展37 −エネミーのバレット処理の修正−
新しい学習内容は、以下の通りです。
BulletDataSO スクリプタブル・オブジェクトは、プレイヤーの扱えるバレットのデータだけではなく、
エネミーのバレットのデータも一緒に登録しておくことを前提に作成されています。
バレットのゲームオブジェクトのプレファブには、プレイヤーもエネミーも Bullet スクリプトを共有していることからも
BulletData も共有して利用することをふまえて設計されていることがうかがえます。
この手順ではエネミーの発射するバレットを復活させて、そのバレットの情報も、プレイヤーと同じように
バレットの固定値ではなく、エネミーごとに設定されているバレットの情報に紐づいて、それぞれの異なるバレットを発射できるように修正を加えます。
現在エネミーでバレットを発射する種類は2種類ですので、これらにそれぞれ専用のバレットのデータを用意して、
そのデータを利用できるようにロジックを作り変えます。
Datas フォルダ内にある BulletDataSO スクリプタブル・オブジェクトを選択し、
インスペクターの BulletDataList 変数の Size を 4 から 6 に変更してください。
Element4 と Element5 が追加されます。順番はどちらでも構いませんので、
ボス用のバレットと、ノーマルエネミー用のバレットのデータを登録しましょう。
下記のインスペクターを参考していただいても構いません。
エネミー本体の属性情報と異なる属性情報を設定しておくと、プレイヤーはより考える部分が多くなります。
そうすることにより、エネミー本体の属性だけではなく、バレットの属性にも目を向ける必要が生まれるためです。
なお、エネミーの発射するバレットの情報は、EnemyData 内にある BulletType によって決定します。
そのため、BulletData の BulletType と合わせておく必要があります。
BulletDataSO スクリプタブル・オブジェクト インスペクター画像

EnemyDataSO スクリプタブル・オブジェクト インスペクター画像(各 BulletType を確認する)

バレット画像例� .椒考僉ヾ�

バレット画像例�◆.┘優漾射僉)�子

以上で設定は完了です。
75.EnemyContorlller スクリプトと、 BulletSelectManager スクリプトと、EnemyGenerator スクリプトを修正し、エネミーの発射するバレットにも BulletData の情報を利用できるようにする
ロジックとして設計したい内容は次の通りです。
この処理を実行するためにはどのような設計を考えてロジックを組んでいけばよいか、自分で実装するまでの手順を考えてみてください。
<実装動画� .痢璽泪襦Ε┘優漾爾離丱譽奪函B粟�情報があるので、プレイヤーの選択しているバレットの種類によって属性間の相性が反映される>
動画ファイルへのリンク
<実装動画�◆.椒垢離丱譽奪函2菫�だけではなく、ダメージも固定値ではなく、BulletData の値を参照している>
動画ファイルへのリンク
以下の内容で順番に実装を進めていきます。
発展37 −エネミーのバレット処理の修正−
74.BulletDataSO スクリプタブル・オブジェクトを修正し、エネミー用のバレットのデータを追加する 75.EnemyContorlller スクリプトと、 BulletSelectManager スクリプトと、EnemyGenerator スクリプトを修正し、エネミーの発射するバレットにも BulletData の情報を利用できるようにする
新しい学習内容は、以下の通りです。
・BulletData の情報をどのようにすれば利用できるようになるか、ロジックを考える
BulletDataSO スクリプタブル・オブジェクトは、プレイヤーの扱えるバレットのデータだけではなく、
エネミーのバレットのデータも一緒に登録しておくことを前提に作成されています。
バレットのゲームオブジェクトのプレファブには、プレイヤーもエネミーも Bullet スクリプトを共有していることからも
BulletData も共有して利用することをふまえて設計されていることがうかがえます。
この手順ではエネミーの発射するバレットを復活させて、そのバレットの情報も、プレイヤーと同じように
バレットの固定値ではなく、エネミーごとに設定されているバレットの情報に紐づいて、それぞれの異なるバレットを発射できるように修正を加えます。
現在エネミーでバレットを発射する種類は2種類ですので、これらにそれぞれ専用のバレットのデータを用意して、
そのデータを利用できるようにロジックを作り変えます。
Datas フォルダ内にある BulletDataSO スクリプタブル・オブジェクトを選択し、
インスペクターの BulletDataList 変数の Size を 4 から 6 に変更してください。
Element4 と Element5 が追加されます。順番はどちらでも構いませんので、
ボス用のバレットと、ノーマルエネミー用のバレットのデータを登録しましょう。
下記のインスペクターを参考していただいても構いません。
エネミー本体の属性情報と異なる属性情報を設定しておくと、プレイヤーはより考える部分が多くなります。
そうすることにより、エネミー本体の属性だけではなく、バレットの属性にも目を向ける必要が生まれるためです。
なお、エネミーの発射するバレットの情報は、EnemyData 内にある BulletType によって決定します。
そのため、BulletData の BulletType と合わせておく必要があります。
BulletDataSO スクリプタブル・オブジェクト インスペクター画像

EnemyDataSO スクリプタブル・オブジェクト インスペクター画像(各 BulletType を確認する)

バレット画像例� .椒考僉ヾ�

バレット画像例�◆.┘優漾射僉)�子

以上で設定は完了です。
75.EnemyContorlller スクリプトと、 BulletSelectManager スクリプトと、EnemyGenerator スクリプトを修正し、エネミーの発射するバレットにも BulletData の情報を利用できるようにする 
ロジックとして設計したい内容は次の通りです。
・エネミーの発射するバレットの情報を固定値ではなく、BulletData に登録した情報を利用できるようにする
この処理を実行するためにはどのような設計を考えてロジックを組んでいけばよいか、自分で実装するまでの手順を考えてみてください。
ゲームを製作するにあたり、プログラムを書くのは一番最後です。
まずはどのような仕様にするかを設定し、その後、それを実装するための設計を考えて
必要なロジックを組み込んで制御を行っていきます。
どのスクリプトにどのような情報が必要なのか、今までに学習してきたことを復習しながらロジックを組んでみてください。
目的とする実装のゴール地点、スタート地点を設定してから、途中の処理を作っていきましょう。
<実装例>
設計に基づいて、処理の修正場所を特定して、処理を追加したり、削除したりしてください。
どんなロジックになっていればいいかを考えて、処理の流れをみながら実装を行うことを心がけてみてください。
分岐内に同じ処理を書く場合、すべての処理を書いてからでよいので、その重複している処理を1箇所にまとめて書くことはできないか、
ロジックの流れを見直してみてください。見直しをすることでロジックの組み立て力が養われます。
属性間の相性がよく、エネミーにとって弱点となる属性であった場合には、Debug.Log メソッドを利用して、
その情報を Console ビューに表示するようにしておいてください。相性の良しあしを判定した結果が Console ビューで判別できるようになります。
EnemyController.cs
スクリプトを修正したらセーブします。
このスクリプトには BulletDataSO スクリプタブル・オブジェクトの情報がありますので、
その情報を外部のスクリプトからでも取得できるように、ゲッターメソッドを用意しておきます。
あとは、その情報が必要なスクリプトがゲッターメソッドを実行することにより、
BulletDataSO スクリプタブル・オブジェクトの中を検索して情報を取得出来ます。
ゲッターメソッドである GetBulletData メソッド内の処理は、その上に記述されている GetElementTypeSprite メソッドと同じ処理で実装しています。
処理の実装に当たり、今回は Linq という機能を利用しています。if 文の部分が Where メソッドの部分になります。
Linq を利用せずに、GetElementTypeSprite メソッドと同じように、foreach 文内に if 文を用意しても問題ありません。
BulletSelectManager.cs
スクリプトを作成したらセーブします。
生成したエネミーにバレットの情報を渡すことによって、
EnemyContorlller スクリプトに BulletData を届けるような仕組みにしています。
EnemyGenerator.cs
スクリプトを修正したらセーブします。
まずはどのような仕様にするかを設定し、その後、それを実装するための設計を考えて
必要なロジックを組み込んで制御を行っていきます。
どのスクリプトにどのような情報が必要なのか、今までに学習してきたことを復習しながらロジックを組んでみてください。
目的とする実装のゴール地点、スタート地点を設定してから、途中の処理を作っていきましょう。
<実装例>
2−1.EnemyController スクリプトを修正する
設計に基づいて、処理の修正場所を特定して、処理を追加したり、削除したりしてください。
どんなロジックになっていればいいかを考えて、処理の流れをみながら実装を行うことを心がけてみてください。
分岐内に同じ処理を書く場合、すべての処理を書いてからでよいので、その重複している処理を1箇所にまとめて書くことはできないか、
ロジックの流れを見直してみてください。見直しをすることでロジックの組み立て力が養われます。
属性間の相性がよく、エネミーにとって弱点となる属性であった場合には、Debug.Log メソッドを利用して、
その情報を Console ビューに表示するようにしておいてください。相性の良しあしを判定した結果が Console ビューで判別できるようになります。
EnemyController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using UnityEngine.Events;
[RequireComponent(typeof(CapsuleCollider2D))]
public class EnemyController : MonoBehaviour
[Header("エネミーのデータ情報")]
public EnemyDataSO.EnemyData enemyData; // SetUpEnemy メソッドにて引数として受けとったエネミーのデータを代入する。これにより、振る舞いが変わる
[SerializeField]
private Image imgEnemy; // エネミーの画像の設定用
[SerializeField]
private Slider slider;
[SerializeField]
private GameObject bulletEffectPrefab; // ヒット演出用のエフェクトのプレファブをインスペクターよりアサインして登録する
[SerializeField]
private GameObject enemybulletPrefab; // EnemyBullet プレファブ・ゲームオブジェクトをインスペクターよりアサインして登録する
[SerializeField, Header("フロート表示を行う位置情報")]
private Transform floatingDamageTran;
[SerializeField]
private FloatingMessage floatingMessagePrefab; // FloatingMessageObj プレファブ・ゲームオブジェクトをインスペクターよりアサインして登録する
private int maxHp; // HPの最大値を代入する変数
private int hp; // エネミーのHP。EnemyData でHpの値が取得できるので、それを代入する。public にしてインスペクターで設定する必要なし
private EnemyGenerator enemyGenerator; // EnemyGenerator を利用するための変数
private UnityAction<Transform, float> moveEvent; // UnityAction<Transform, float> 型の moveEvent 変数と読む。ここにエネミーの移動方法に応じた移動用のメソッドを登録する
/// <summary>
/// エネミーの設定
/// </summary>
public void SetUpEnemy(EnemyDataSO.EnemyData enemyData) {
// 引数で届いた EnemyData を代入する
this.enemyData = enemyData;
// ボスではない場合
if (this.enemyData.enemyType != EnemyType.Boss) {
// エネミーの X 軸(左右)の位置を、ゲーム画面に収まる範囲でランダムな位置に変更
transform.localPosition = new Vector3(transform.localPosition.x + Random.Range(-650, 650), transform.localPosition.y, 0);
} else {
// ボスの位置を徐々に下方向に変更
transform.DOLocalMoveY(transform.localPosition.y - 500, 3.0f);
// ボスの場合、サイズを大きくする
transform.localScale = Vector3.one * 2.0f;
// Hpゲージの位置を高い位置にする
slider.transform.localPosition = new Vector3(0, 150, 0);
}
// 画像を EnemyData の画像にする => ここでエネミーごとの画像に変更する
imgEnemy.sprite = this.enemyData.enemySprite;
// EnemyData より Hp の値を最大値として代入
maxHp = this.enemyData.hp;
// hp を設定
hp = maxHp;
// Hpゲージの表示更新
DisplayHpGauge();
}
// 侵入判定
private void OnTriggerEnter2D(Collider2D col) {
// バレットが接触したら
if (col.gameObject.tag == "Bullet") {
// 侵入してきたコライダーのゲームオブジェクトに Bullet スクリプトがアタッチされていたら取得して bullet 変数に代入して、if 文の中の処理を行う
if (col.gameObject.TryGetComponent(out Bullet bullet)) {
// HPの更新処理とエネミーの破壊確認の処理を呼び出す
UpdateHp(bullet);
// バレットのヒット演出用エフェクトの生成
GenerateBulletEffect(col.gameObject.transform);
}
}
}
/// <summary>
/// バレットの破壊処理
/// </summary>
private void DestroyBullet(Collider2D col)
{
// バレットを破壊する
Destroy(col.gameObject);
}
/// <summary>
/// Hpの更新処理とエネミーの破壊確認処理
/// </summary>
private void UpdateHp(Bullet bullet)
{
// ダメージ確定用
int bulletPower = 0;
// 弱点判定用。弱点の場合には、true に切り替える
bool isWeakness = false;
// バレットの属性とエネミーの属性の情報を利用して相性を判定
if(ElementCompatibilityHelper.GetElementCompatibility(bullet.bulletData.elementType, enemyData.elementType) == true) {
Debug.Log("Element 相性 良");
// バレットの攻撃力をダメージ倍率分掛けた値にして、計算結果を切り上げて int 型にする
bulletPower = Mathf.FloorToInt(bullet.bulletData.bulletPower * GameData.instance.GetDamageRatio());
// 弱点として設定
isWeakness = true;
} else {
// バレットの攻撃力をそのまま利用する
bulletPower = bullet.bulletData.bulletPower;
}
// バレットの攻撃力値用のフロート表示の生成
CreateFloatingMessageToBulletPower(bulletPower, isWeakness);
// hpの減算処理
hp -= bulletPower;
// Hp の値の上限・下限を確認して範囲内に制限
hp = Mathf.Clamp(hp, 0, maxHp);
// HPゲージの表示更新
DisplayHpGauge();
if (hp <= 0) {
hp = 0;
// ボスの場合
if (enemyData.enemyType == EnemyType.Boss) {
// ボス討伐済みの状態にする
enemyGenerator.SwitchBossDestroyed(true);
}
// Exp を TotalExp に加算
GameData.instance.UpdateTotalExp(enemyData.exp);
// 最新の TotapExp を利用して表示更新
enemyGenerator.PreparateDisplayTotalExp(enemyData.exp);
// このゲームオブジェクトを破壊する
Destroy(gameObject);
}
// ノーマル弾の場合
if (bullet.bulletData.bulletType == BulletDataSO.BulletType.Player_Normal || bullet.bulletData.bulletType == BulletDataSO.BulletType.Player_5ways_Normal) {
// バレットを破壊
Destroy(bullet.gameObject);
}
}
/// <summary>
/// HPゲージの表示更新
/// </summary>
private void DisplayHpGauge() {
// HPゲージを現在値に合わせて制御
slider.DOValue((float)hp / maxHp, 0.25f);
}
/// <summary>
/// 被バレット時のヒット演出用のエフェクト生成
/// </summary>
/// <param name="bulletTran"></param>
private void GenerateBulletEffect(Transform bulletTran) {
// ヒット演出用のエフェクトを、バレットのぶつかった位置で生成
GameObject effect = Instantiate(bulletEffectPrefab, bulletTran, false);
// エフェクトをエネミーの子オブジェクトにする
effect.transform.SetParent(transform);
Destroy(effect, 3.0f);
}
////* ここから◇� ◆��◆◆���を修正 *////
/// <summary>
/// エネミーの追加設定
/// </summary>
/// <param name="enemyGenerator"></param>
/// <param name="bulletData"></param>
public void AdditionalSetUpEnemy(EnemyGenerator enemyGenerator, BulletDataSO.BulletData bulletData) { // <= ◇� ‖�2引数を追加します
// 引数で届いた情報を変数に代入してスクリプト内で利用できる状態にする
this.enemyGenerator = enemyGenerator;
// MoveEventSO スクリプタブル・オブジェクトの GetMoveEvent メソッドを実行し、戻り値で移動用のメソッドを受け取る。ここで移動方法を決定
moveEvent = this.enemyGenerator.moveEventSO.GetMoveEvent(enemyData.moveType);
// Invoke メソッドを実行すると、moveEvent 変数に登録されたメソッド(今回は移動用のメソッド)を実行する。UnityActon <Transform, float>型なので、実行にあたって、指定された型の情報を指定する
moveEvent.Invoke(transform, enemyData.moveDuration);
// バレットのデータがあり、かつ、バレットの種類が None ではない場合
if (bulletData != null && bulletData.bulletType != BulletDataSO.BulletType.None) { // <= ◇�◆‐魴鐚阿鯤儿垢靴泙�
// バレットの自動生成処理を実行する
StartCoroutine(EnemyShot(bulletData)); // <= ◇�� 引数を追加します
}
////* ここまで *////
Debug.Log("追加設定完了");
}
////* ここから☆� ◆��◆◆���を修正 *////
/// <summary>
/// バレットの自動生成
/// </summary>
/// <returns></returns>
private IEnumerator EnemyShot(BulletDataSO.BulletData bulletData) { // <= ☆� ^�数を追加します
// 条件に true を指定すると無制限のループ処理になる
while (true) {
// エネミーのバレットのクローンを生成し、戻り値を bulletObj 変数に代入
GameObject bulletObj = Instantiate(enemyBulletPrefab, transform);
// クローンのゲームオブジェクトから Bullet スクリプトを取得して、ShotBullet メソッドを実行する
bulletObj.GetComponent<Bullet>().ShotBullet(enemyGenerator.PreparateGetPlayerDirection(transform.position), bulletData); // <= ☆�◆‖�2引数を追加します
// ボスの場合
if (enemyData.moveType == MoveType.Boss_Horizontal) {
// バレットとエネミーの親子関係を解消し、バレットの親を TemporaryObjectContainer ゲームオブジェクトに変更する
bulletObj.transform.SetParent(TransformHelper.GetTemporaryObjectContainerTran());
}
// BulletData に設定されている待機時間分だけ処理を中断する(待機する)
yield return new WaitForSeconds(bulletData.loadingTime); // <= ☆�� 引数を固定値から、バレット固有の値に変更します
////* ここまで *////
}
}
/// <summary>
/// バレットの攻撃力値分のフロート表示の生成
/// </summary>
/// <param name="bulletPower"></param>
/// <param name="isWeakness"></param>
private void CreateFloatingMessageToBulletPower(int bulletPower, bool isWeakness) {
// フロート表示の生成。生成位置は EnemySet ゲームオブジェクト内の FloatingMessageTran ゲームオブジェクトの位置(子オブジェクト)
FloatingMessage floatingMessage = Instantiate(floatingMessagePrefab, floatingDamageTran, false);
// 生成したフロート表示の設定用メソッドを実行。引数として、バレットの攻撃力値とフロート表示の種類を指定して渡す
floatingMessage.DisplayFloatingMessage(bulletPower, FloatingMessage.FloatingMessageType.EnemyDamage, isWeakness);
}
}
スクリプトを修正したらセーブします。
2−2.BulletSelectManager スクリプトを修正する
このスクリプトには BulletDataSO スクリプタブル・オブジェクトの情報がありますので、
その情報を外部のスクリプトからでも取得できるように、ゲッターメソッドを用意しておきます。
あとは、その情報が必要なスクリプトがゲッターメソッドを実行することにより、
BulletDataSO スクリプタブル・オブジェクトの中を検索して情報を取得出来ます。
ゲッターメソッドである GetBulletData メソッド内の処理は、その上に記述されている GetElementTypeSprite メソッドと同じ処理で実装しています。
処理の実装に当たり、今回は Linq という機能を利用しています。if 文の部分が Where メソッドの部分になります。
Linq を利用せずに、GetElementTypeSprite メソッドと同じように、foreach 文内に if 文を用意しても問題ありません。
BulletSelectManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // <= 追加します。
public class BulletSelectManager : MonoBehaviour
{
[SerializeField]
private BulletSelectDetail bulletSelectDetailPrefab; // BulletSelectDetail スクリプトがアタッチされている、プレファブの btnBulletSelect ゲームオブジェクトをアサインする
[SerializeField]
private Transform bulletTran; // バレット選択ボタンの生成位置として利用するゲームオブジェクトをアサインする
private const int maxBulletBtnNum = 4; // バレット選択ボタンの最大数。定数として宣言する
public List<BulletSelectDetail> bulletSelectDetailList = new List<BulletSelectDetail>(); // 生成されたバレット選択ボタンの List
[SerializeField]
private BulletDataSO bulletDataSO; // BulletDataSO スクリプタブル・オブジェクトをアサインする
[SerializeField]
private ElementDataSO elementDataSO; // ElementDataSO スクリプタブル・オブジェクトをアサインする
private GameManager gameManager;
/// <summary>
/// バレット選択用ボタンの生成
/// </summary>
/// <param name="gameManager"></param>
/// <returns></returns>
public IEnumerator GenerateBulletSelectDetail(GameManager gameManager) {
this.gameManager = gameManager;
// BulletDataSO スクリプタブル・オブジェクトのデータから、利用者の種類が Player のバレットの情報だけを代入する List を用意する
List<BulletDataSO.BulletData> playerBulletDatas = new List<BulletDataSO.BulletData>();
// バレットの利用者の種類が Player のバレットの情報だけを抽出して List を作成する
playerBulletDatas = bulletDataSO.bulletDataList.Where(x => x.liberalType == BulletDataSO.LiberalType.Player).ToList();
for (int i = 0; i < maxBulletBtnNum; i++) {
// バレットボタン生成
BulletSelectDetail bulletSelectDetail = Instantiate(bulletSelectDetailPrefab, bulletTran, false);
// TODO バレットボタンの設定。また後で引数に変更があります
bulletSelectDetail.SetUpBulletSelectDetail(this, playerBulletDatas[i]);
// リストに追加
bulletSelectDetailList.Add(bulletSelectDetail);
// 0.25 秒だけ処理を中断(順番にボタンが生成されるようにする演出)
yield return new WaitForSeconds(0.25f);
}
// 使用するバレットの情報を初期設定
GameData.instance.SetBulletData(playerBulletDatas[0]);
}
/// <summary>
/// すべてのバレット選択ボタンに対して no で照合を行い、装填中と未装填の状態を変更
/// </summary>
/// <param name="bulletNo"></param>
public void ChangeLoadingBulletSettings(int bulletNo) {
// バレット選択ボタンの List の要素をすべて取り出し、1つずつ順番に処理する
for (int i = 0; i < bulletSelectDetailList.Count; i++) {
// この要素のバレット選択ボタンの no の値が bulletNo (選択されたバレット)と同じであるなら
if (bulletSelectDetailList[i].bulletData.no == bulletNo) {
// 装填中状態にする
bulletSelectDetailList[i].ChangeLoadingBullet(true);
Debug.Log("装填中のバレットの No " + bulletNo);
// それ以外の no の場合には
} else {
// 未装填状態にする
bulletSelectDetailList[i].ChangeLoadingBullet(false);
Debug.Log("未装填のバレットの No " + bulletNo);
}
}
}
/// <summary>
/// 初期バレットを装填中のバレットとして設定
/// </summary>
public void ActivateDefaultBullet() {
// バレット選択ボタンの List から要素を1つずつ取り出す
foreach (BulletSelectDetail bulletSelectDetail in bulletSelectDetailList) {
// 初期バレットの設定があるバレットのデータの場合
if (bulletSelectDetail.isDefaultBullet) {
// そのバレットを装填中の状態にする
bulletSelectDetail.OnClickBulletSelect();
Debug.Log("初期バレットを装填中のバレットとして設定");
return;
}
}
}
/// <summary>
/// 使用可能バレット選択ボタンの確認と更新
/// </summary>
public void JugdeOpenBullets() {
// 繰り返し利用する情報を変数に代入しておく
int totalExp = GameData.instance.GetTotalExp();
// バレットごとに使用可能なEXPを超えているか確認
foreach (BulletSelectDetail bulletData in bulletSelectDetailList) {
// ゲーム終了の状態になったら
if (gameManager.isGameUp) {
// バレット選択ボタンを非活性化の状態にしてタップできない状態にする
bulletData.SwitchActivateBulletBtn(false);
continue;
}
// コストを支払っているかどうか、戻り値を持つメソッドを利用して確認する
if (bulletData.GetStateBulletCostPayment()) {
// コストを支払い済みの場合、そのバレット選択ボタンはオンの状態にする
bulletData.SwitchActivateBulletBtn(true);
continue;
}
// バレットに設定されているコストの値よりも、Exp の現在値の方が大きいか同じであれば
if (bulletData.bulletData.openExp <= totalExp) {
// そのバレット選択ボタンを活性化の状態にしてタップできるようにする
bulletData.SwitchActivateBulletBtn(true);
} else {
// Exp の現在値が超えていないバレット選択ボタンを非活性化の状態にしてタップできないようにする
bulletData.SwitchActivateBulletBtn(false);
}
}
}
/// <summary>
/// 選択したバレットのコストの支払いとそれに関連する処理
/// </summary>
/// <param name="costExp"></param>
public void SelectedBulletCostPayment(int costExp) {
// TotalExp(Exp の現在値) より、選択したバレットのコストを減算
GameData.instance.UpdateTotalExp(-costExp);
// 画面の Exp 表示減算後の値に更新
gameManager.uiManager.DisplayTotalExp(GameData.instance.GetTotalExp());
// コストの値のフロート表示を生成
gameManager.uiManager.CreateFlotingMessageToExp(-costExp, FloatingMessage.FloatingMessageType.BulletCost);
// 使用可能バレットの確認と更新
JugdeOpenBullets();
}
/// <summary>
/// ElementTypeからSprite取得用
/// </summary>
/// <param name="elementType"></param>
/// <returns></returns>
public Sprite GetElementTypeSprite(ElementType elementType) {
// elementDataList 変数の要素を1つずつ順番いに取り出して、elementData 変数へ代入
foreach (ElementDataSO.ElementData elementData in elementDataSO.elementDataList) {
// 現在代入されている ElementData の ElementType と引数の elementType が同じであるであるか照合
if (elementData.elementType == elementType) {
// 照合の結果、合致した ElementData に登録されている画像情報を戻す
return elementData.elementSprite;
}
}
// どれも合致しない場合は null を戻す
return null;
}
////* ここからメソッドを1つ追加 *////
/// <summary>
/// BulletType より BulletData を検索して取得
/// </summary>
/// <param name="bulletType"></param>
/// <returns></returns>
public BulletDataSO.BulletData GetBulletData(BulletDataSO.BulletType bulletType) {
// 引数の bulletType と同じ BulletType が登録されている BulletData を探す
foreach (BulletDataSO.BulletData bulletData in bulletDataSO.bulletDataList.Where(x => x.bulletType == bulletType)) {
// 合致した BulletData を戻す
return bulletData;
}
// どれも合致しない場合は null を戻す
return null;
}
////* ここまで *////
}
スクリプトを作成したらセーブします。
2−3.EnemyGenerator スクリプトを修正する
生成したエネミーにバレットの情報を渡すことによって、
EnemyContorlller スクリプトに BulletData を届けるような仕組みにしています。
EnemyGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyGenerator : MonoBehaviour
{
[SerializeField, Header("エネミーのプレファブ")]
private EnemyController enemyObjPrefab;
[SerializeField, Header("エネミー生成までの準備時間")]
public float preparateTime;
private int generateCount; // 生成したエネミーの数をカウントするための変数
private float timer; // 準備時間の計測用の変数
private GameManager gameManager;
[Header("エネミーの最大生成数")]
public int maxGenerateCount;
[Header("エネミーの生成完了管理用")]
public bool isGenerateEnd;
[Header("ボス討伐管理用")]
public bool isBossDestroyed;
[Header("エネミーのスクリプタブル・オブジェクト")]
public EnemyDataSO enemyDataSO;
// Normal タイプのエネミーのデータだけ代入されている List
private List<EnemyDataSO.EnemyData> normalEnemyDatas = new List<EnemyDataSO.EnemyData>();
// Boss タイプのエネミーのデータだけ代入されている List
private List<EnemyDataSO.EnemyData> bossEnemyDatas = new List<EnemyDataSO.EnemyData>();
[Header("エネミー移動用のスクリプタブル・オブジェクト")]
public MoveEventSO moveEventSO;
[SerializeField, Header("生成したエネミーのリスト")]
private List<EnemyController> enemiesList = new List<EnemyController>();
/// <summary>
/// EnemyGenerator の設定
/// </summary>
/// <param name="gameManager"></param>
public void SetUpEnemyGenerator(GameManager gameManager) {
// EnemyGenerator スクリプトの gameManager 変数に、引数で届いた GameManager スクリプトの情報を代入
this.gameManager = gameManager;
// 引数で指定したエネミーのタイプのリストを作成
normalEnemyDatas = GetEnemyTypeList(EnemyType.Normal);
bossEnemyDatas = GetEnemyTypeList(EnemyType.Boss);
// GameData よりエネミーの最大生成数を取得
maxGenerateCount = GameData.instance.GetMaxGenerateCount();
}
/// <summary>
/// エネミー生成の準備
/// </summary>
private void PreparateGenerateEnemy() {
// 時間の計測
timer += Time.deltaTime;
// 準備時間を超えたら
if (timer >= preparateTime) {
// 次回の時間の計測を行うためにリセット
timer = 0;
// エネミーの生成
GenerateEnemy();
// 生成したエネミーの数をカウントアップ
generateCount++;
Debug.Log("生成したエネミーの数 : " + generateCount);
// 今までに生成したエネミーの数が、エネミーの最大生成数の値と同じか超えたら
if (generateCount >= maxGenerateCount) {
// 生成完了の状態にする = Update メソッドでこの値を利用して、生成完了状態になったら生成の準備処理が実行されないように制御する
isGenerateEnd = true;
Debug.Log("生成完了");
// ボスの生成
StartCoroutine(GenerateBoss());
}
}
}
/// <summary>
/// エネミーの生成
/// </summary>
private void GenerateEnemy(EnemyType enemyType = EnemyType.Normal) {
// ランダムな値を代入するための変数を宣言
int randomEnemyNo;
// EnemyData を代入するための変数を宣言
EnemyDataSO.EnemyData enemyData = null;
// EnemyType に合わせて生成するエネミーの種類を決定し、そのエネミーの種類ごとのリストからランダムな EnemyData を取得
switch (enemyType) {
case EnemyType.Normal:
randomEnemyNo = Random.Range(0, normalEnemyDatas.Count);
enemyData = normalEnemyDatas[randomEnemyNo];
break;
case EnemyType.Boss:
randomEnemyNo = Random.Range(0, bossEnemyDatas.Count);
enemyData = bossEnemyDatas[randomEnemyNo];
break;
}
// プレファブからエネミーのクローンを生成する。生成位置は EnemyGenerator の位置。戻り値の値は EnemyController 型になる
EnemyController enemyController = Instantiate(enemySetPrefab, transform, false); // <= ☆ 左辺に EnemyController 型の変数を用意して、インスタンスされたエネミーの情報を戻り値で受け取る
// EnemyController スクリプトの SetUpEnemy メソッドを実行する => Start メソッドの代わりになる処理
enemyController.SetUpEnemy(enemyData);
////* ここから処理を修正 *////
// Boss 以外でも追加設定を行う
enemyController.AdditionalSetUpEnemy(this, gameManager.bulletSelectManager.GetBulletData(enemyData.bulletType); // <= ☆ 第2引数を追加します
////* ここまで *////
// List に生成したエネミーの情報を追加
enemiesList.Add(enemyController);
}
void Update() {
// 生成完了の状態になったら生成処理を行わないようにする
if (isGenerateEnd) {
return;
}
// GameManager スクリプトの準備が完了していないうちは生成処理を行わないようにする
if (!gameManager.isSetUpEnd) {
return;
}
// ゲーム終了の状態ではないなら
if (!gameManager.isGameUp) {
// エネミー生成の準備
PreparateGenerateEnemy();
}
}
/// <summary>
/// ボスの生成
/// </summary>
private IEnumerator GenerateBoss() {
// ボス出現の警告演出
yield return StartCoroutine(gameManager.uiManager.PlayBossAlert());
// ボス生成
GenerateEnemy(EnemyType.Boss);
}
/// <summary>
/// ボス討伐状態の切り替え
/// </summary>
/// <param name="isSwitch"></param>
public void SwitchBossDestroyed(bool isSwitch) {
// ボスの討伐状態を切り替え
isBossDestroyed = isSwitch;
Debug.Log("ボス討伐");
// ボス討伐に合わせて、ゲーム終了の状態に切り替える
gameManager.SwitchGameUp(isBossDestroyed);
// ゲームクリアの準備
gameManager.PreparateGameClear();
}
/// <summary>
/// 引数で指定されたエネミーの種類のListを作成し、作成した値を戻す
/// </summary>
/// <param name="enemyType"></param>
/// <returns></returns>
private List<EnemyDataSO.EnemyData> GetEnemyTypeList(EnemyType enemyType) {
List<EnemyDataSO.EnemyData> enemyDatas = new List<EnemyDataSO.EnemyData>();
// 引数のタイプのエネミーのデータだけを抽出して enemyDatas リストに EnemyData を追加して、List を作成していく
for (int i = 0; i < enemyDataSO.enemyDataList.Count; i++) {
if (enemyDataSO.enemyDataList[i].enemyType == enemyType) {
enemyDatas.Add(enemyDataSO.enemyDataList[i]);
}
}
// 抽出処理の結果を戻す
return enemyDatas;
}
/// <summary>
/// TotalExp の表示更新準備
/// </summary>
/// <param name="exp"></param>
public void PreparateDisplayTotalExp(int exp) {
// GameManager スクリプトから UIManager スクリプトの UpdateDisplayTotalExp メソッドを実行する
gameManager.uiManager.UpdateDisplayTotalExp(exp);
// GameManager スクリプトから UIManager スクリプトの CreateFlotingMessageToExp メソッドを実行する。引数として exp とフロート表示の種類を渡す
gameManager.uiManager.CreateFlotingMessageToExp(exp, FloatingMessage.FloatingMessageType.GetExp);
// 使用可能バレット選択ボタンの確認と更新
gameManager.bulletSelectManager.JugdeOpenBullets();
}
/// <summary>
/// プレイヤーとエネミーとの位置から方向を判定する準備
/// </summary>
/// <returns></returns>
public Vector3 PreparateGetPlayerDirection(Vector3 enemyPos) {
return gameManager.GetPlayerDirection(enemyPos);
}
/// <summary>
/// enemiesList に登録されているエネミーのうち、リストに残っているエネミーのゲームオブジェクトを破壊し、enemiesList をクリアする
/// </summary>
public void ClearEnemiesList() {
// enemiesList の要素(中身)を1つずつ順番に、要素の最大値になるまで判定していく
for (int i = 0; i < enemiesList.Count; i++) {
// 要素が空ではない(プレイヤーに破壊されずにゲーム画面に残っている)なら
if (enemiesList[i] != null) {
// そのエネミーのゲームオブジェクトを破壊する
Destroy(enemiesList[i].gameObject);
}
}
// リストをクリア(要素が何もない状態)にする
enemiesList.Clear();
}
/// <summary>
/// 一時オブジェクト(バレット、エフェクトなど)をすべて破棄(利用状況に応じて、�,��△里い困譴�の処理を実装する)
/// </summary>
public void DestroyTemporaryObjectContainer() {
// �� TemporaryObjectContainer ゲームオブジェクトを破壊する(プロパティを利用している場合)
Destroy(TransformHelper.TemporaryObjectContainerTran.gameObject);
// �� TemporaryObjectContainer ゲームオブジェクトを破壊する(プロパティを利用していない場合)
Destroy(TransformHelper.GetTemporaryObjectContainerTran().gameObject);
}
}
スクリプトを修正したらセーブします。
ロジックの流れを確認して、ゲームを実行し、処理の制御を確認してください。
バレットに属性の情報が反映されていれば、プレイヤーのバレットの種類との相性判定により、弱点となる制御が実装されます。
<実装動画� .痢璽泪襦Ε┘優漾爾離丱譽奪函B粟�情報があるので、プレイヤーの選択しているバレットの種類によって属性間の相性が反映される>
動画ファイルへのリンク
<実装動画�◆.椒垢離丱譽奪函2菫�だけではなく、ダメージも固定値ではなく、BulletData の値を参照している>
動画ファイルへのリンク
以上でこの手順は終了です。
次は 発展38 −BGMの準備− です。
バレットに属性の情報が反映されていれば、プレイヤーのバレットの種類との相性判定により、弱点となる制御が実装されます。
<実装動画� .痢璽泪襦Ε┘優漾爾離丱譽奪函B粟�情報があるので、プレイヤーの選択しているバレットの種類によって属性間の相性が反映される>
動画ファイルへのリンク
<実装動画�◆.椒垢離丱譽奪函2菫�だけではなく、ダメージも固定値ではなく、BulletData の値を参照している>
動画ファイルへのリンク
以上でこの手順は終了です。
次は 発展38 −BGMの準備− です。




コメントをかく