この講座はハクスラローグライク×デッキ構築型カードバトルRPG「呪術迷宮」の作り方講座の第8回です。
前回の記事ではカード効果のUI反映、そしてシングルトンを用いたカードデータ管理のためのマネージャーシステムと日英ローカライズ機能を実装しました。
前回の記事:

今回は前回作成したシングルトンによるデータ管理のシステムを拡張してプレイヤーのデッキデータを管理する機能を作成します。
記事の後半ではこれから戦闘処理を実装していくにあたって必要になる敵キャラクターAIパラメータや行動データも設定していきます。
デッキ管理スクリプトを作成
Dataクラスを直接拡張してデッキ管理の機能を付けても良いですが、機能を分割する為に新しくPlayerDeckDataクラスを作成します。(Scripts/Commonフォルダ以下がおすすめです)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// プレイヤ側デッキ・所持カード管理クラス /// </summary> public class PlayerDeckData : MonoBehaviour { // 各種設定データ(Inspectorからセット) public List<CardDataSO> allPlayerCardsList; // プレイヤー側カード全部のリスト public List<CardDataSO> playerInitialDeck; // プレイヤー側初期デッキカードリスト // プレイヤー側全カードデータと通し番号を紐づけたDictionary public static Dictionary<int, CardDataSO> CardDatasBySerialNum; // 現在のプレイヤーデッキデータ(通し番号で管理) public static List<int> deckCardList; // 初期化処理 public void Init () { // プレイヤー側全カードデータと通し番号を紐づける CardDatasBySerialNum = new Dictionary<int, CardDataSO> (); foreach (var item in allPlayerCardsList) { CardDatasBySerialNum.Add (item.serialNum, item); } } /// <summary> /// ゲーム初回起動時の初期デッキを設定する /// </summary> public void DataInitialize () { // プレイヤーの現在デッキデータに初期デッキ設定を反映 deckCardList = new List<int> (); foreach (var cardData in playerInitialDeck) { AddCardToDeck (cardData.serialNum); } } /// <summary> /// デッキにカードを1枚追加する /// </summary> /// <param name="cardSerialNum">カードの通し番号</param> public static void AddCardToDeck (int cardSerialNum) { deckCardList.Add (cardSerialNum); deckCardList.Sort (); } } |
プレイヤーの初期デッキ、および現在設定中のデッキデータを管理します。Dataクラス同様にDetaManagerオブジェクトにアタッチして使用します。
まずはInspectorから初期デッキを表すplayerInitialDeckパラメータに対してカード設定ファイルをドラッグ&ドロップします。これにより初期データ(ゲーム起動時)でのプレイヤーデッキを作成します。何でも良いので適当なカードを合計10枚以上指定しておきます。
(上記の登録カードは一例です。6章で同じカードを作ってないですが気にしないでください)
今回の設計ではデッキに組み込んだカードのデータをint型のIDで管理しています。このIDは各カードの設定ファイル(CardDataSO)に入力した通し番号(serialNum)と同一です。
複数のデータを持っているCardDataSOファイルそのものでなくIDで管理する事によって今後の拡張の際に取り扱いやすくなります。
ただしIDだけで管理する場合は当然そのIDから該当のカードデータを取得できる機能を用意する必要があります。そのため、allPlayerCardsListパラメータを追加し、ここにプレイヤーが使用する全てのカードをまとめて登録します。IDとカードデータの紐づけをDictionary型であるCardDatasBySerialNumで記憶しておきます。
また、ここでプレイヤーが保有している全てのカードをallPlayerCardsListに登録します。
Inspectorからの操作でallPlayerCardsListに全プレイヤーカードを登録しますが、ファイルの1つ1つを順番にドラッグ&ドロップしているととても時間がかかってしまいます。まとめて操作する方法も学んでおきましょう。
Projectビューでの複数ファイルの同時選択自体はShiftキーを押しながらクリックまたは矢印キーの入力で可能です。又はCtrl+Aキーを押す事で開いているフォルダ内の全部のファイルが選択状態になります。
しかし複数のファイルを同時選択するとInspectorビューでの表示が切り替わってしまい先ほどのPlayerDeckDataコンポーネントに対してドラッグ&ドロップ操作が出来ません。
この切り替わりを防ぐ方法としてInspectorビューのロック機能があります。事前にDataManagerオブジェクトを選択しておき、Inspectorビュー右上の鍵マークのアイコンをクリックするとロック状態になり、他のファイルやオブジェクトなどを選択してもInspectorビューが切り替わらなくなります。
この状態でPlayerCardsフォルダ内のカードデータファイル全てをallPlayerCardsListにドラッグ&ドロップすれば完了です。
操作後は忘れずにInspectorのロックを解除しておきましょう。気づかずにロックしたままだと誤操作の原因になりやすいです。
なお、「敵が使用するカード全部のリスト」は用意しなくても大丈夫です。敵のデータを設定する際には使用するカードを直接それぞれの敵に対して指定するのでIDで管理する必要はありません。
PlayerDeckDataクラスは今後デッキ編集画面を作成した時に拡張を行う予定です。
Dataクラスからの参照をセット
他のクラスからPlayerDeckDataクラスにアクセスできるようにDataクラス内で参照を用意しておきます。
Data.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// (DataManagerオブジェクトにアタッチ) /// データマネージャー /// ゲーム起動中常に同じインスタンス(オブジェクト)が1つ存在している /// </summary> public class Data : MonoBehaviour { #region シングルトン維持用処理(変更不要) // シングルトン維持用 public static Data instance; // Awake(Startより前に1度だけ実行) private void Awake () { // シングルトン用処理 if (instance != null) { Destroy (gameObject); return; } instance = this; DontDestroyOnLoad (gameObject); // ゲーム起動時処理 InitialProcess (); } #endregion // 各種コンポーネント public PlayerDeckData playerDeckData; // デッキ管理クラス // シーン間保存データ public static SystemLanguage nowLanguage; // 現在の設定言語 /// <summary> /// ゲーム開始時(インスタンス生成時)に一度だけ実行される処理 /// </summary> private void InitialProcess () { // 乱数シード値初期化 Random.InitState (System.DateTime.Now.Millisecond); // 実行環境の言語設定を取得する nowLanguage = GetLanguageData (); //nowLanguage = SystemLanguage.English; // (テスト用)英語設定に変更する // プレイヤーデッキデータ初期化処理 playerDeckData.Init (); // プレイヤー所持カードデータ初期化(セーブ機能実装後は別のタイミングで呼び出し) playerDeckData.DataInitialize (); } /// <summary> /// 実行環境の言語設定を取得して返す /// </summary> /// <returns>言語データ</returns> private SystemLanguage GetLanguageData () { // 実行環境の言語設定を取得 var language = Application.systemLanguage; // 日本語以外の言語だった場合全て英語で対応する if (language != SystemLanguage.Japanese) language = SystemLanguage.English; return language; } } |
DataManagerオブジェクトのDataコンポーネントにDataManagerオブジェクト自身をアタッチしておきます。
FieldManagerクラスでの現在デッキ管理
FieldManager内にて戦闘中に使用するデッキ(山札)の状態を管理し、ドロー操作によってデッキの中からカードが引かれていく処理を追加します。
PlayerDeckDataクラス内ではプレイヤーの現在のデッキ設定をdeckCardList変数内に(通し番号で)格納していますがこれは「戦闘開始時の初期デッキ」として扱います。
戦闘中にデッキからカードを引く時はFieldManager内に用意する「現在デッキデータ」から引いていきます。このゲームが戦闘ごとに手札や山札の状態がリセットされるルールになっている為です。
初期デッキの枚数を変更して色々なパターンでの表示を確かめておくと良いでしょう。
敵キャラクターデータを定義 ステータスパラメータと行動AIの設定
そろそろ戦闘システムの本格的な実装が近づいてきたので次回に向けて敵キャラクターの設定を行えるようにしておきましょう。
カード設定の時と同様に敵キャラクターも沢山の種類を用意するのでScriptableObjectで設定できるようにしておきます。Scripts/Define以下にEnemyStatusSOを作成します。
EnemyStatusSOでデータを管理する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 敵のステータス定義クラス(ScriptableObject) /// </summary> [CreateAssetMenu (fileName = "EnemyStatusSO", menuName = " ScriptableObjects/EnemyStatusSO", order = 2)] public class EnemyStatusSO : ScriptableObject { [Header ("名前(日本語)")] public string enemyName_JP; [Header ("名前(英語)")] public string enemyName_EN; [Header ("敵画像")] public Sprite charaSprite; [Header ("最大HP(初期HP)")] public int maxHP; [Header ("各ターンに使用するカードと設置先のリスト")] public List<EnemyUseCardData> useCardDatas; [Header ("撃破ボーナス:選択肢の個数")] public int bonusOptions; [Header ("撃破ボーナス:獲得できる個数")] public int bonusPoint; [Header ("撃破ボーナス:選択肢に出現するプレイヤーカード")] public List<CardDataSO> bonusCardList; /// <summary> /// 1ターンに使用するカードと設置先のデータクラス /// (Inspectorで編集すると配列の長さを固定出来ない為、変数を必要分宣言) /// </summary> [System.Serializable] public class EnemyUseCardData { public CardDataSO placeCardData_0; // 0番目のカードデータ public CardDataSO placeCardData_1; // 1番目のカードデータ public CardDataSO placeCardData_2; // 2番目のカードデータ public CardDataSO placeCardData_3; // 3番目のカードデータ public CardDataSO placeCardData_4; // 4番目のカードデータ } } |
各パラメータの解説は次項で行います。
EnemyStatusSOクラス内に新しいクラスEnemyUseCardDataを宣言していますが、[System.Serializable]属性を付与する事でそのクラスもInspectorでの設定が可能になります。
敵キャラクター設定ファイルの作成
ProjectビューからScriptableObjectフォルダを開き、Enemysフォルダを作成してその中に敵キャラクター設定ファイルを追加していきます。後々複数のステージを実装するのでステージごとにフォルダ分けするのもOKです。
カードデータ作成の時と同様にProjectビューの何もない所を右クリック→Create→ScriptableObjects→EnemyStatusSOを選択で敵キャラクター設定ファイルを作成できます。
日本語と英語でのキャラクター名、画像の設定などはカード設定の時と同様です。画像の大きさは次章で表示させた時に1つ1つ調整していきます。最大HPは適当な数値を入れておきます。
敵キャラクターはターン開始時にプレイボード上に複数のカードを配置します。useCardDatasパラメータでは各ターンで配置するカードの位置や種類を指定します。
useCardDatasリストに要素を追加すると、スクリプト後半で定義しているEnemyUseCardData型の設定項目が追加されます。placeCardData_0~4の5つのパラメータがそれぞれプレイボードの左端~右端に対応し、ここに敵側カードをドラッグ&ドロップする事で使用カードを追加できます。
useCardDatasリストの0番目の設定が1ターン目に適用され、1番目の要素が2ターン目に適用されるという流れになります。全ての行動を終えたら最初の行動に戻るというループを実装するので最低でも1種類の行動さえ設定しておけばOKです。
先ほどのHP設定と同様、細かい設定はバランス調整時に行えば大丈夫です。まずは動作確認として適当な設定にしておきましょう。特にbonusから始まる変数名の撃破ボーナス設定については当分触れないので気にしなくてOKです。
サンプルゲームのエネミーデータについて
本講座のサンプルプロジェクトファイルを参考にしていただければ、どんな形で敵を作ればいいかはわかります。呪術迷宮には数多くの敵キャラクターが存在しますが、一度に全ての敵データを作成する必要はありません。
今後の動作確認用に1~3体程度のデータを用意しておけばOKです。
プロジェクトファイルのデータでも同様に一部の敵キャラクターのみ実装されています。あとはあなたの好きな敵を実装するもよし。呪術迷宮のゲームをプレイして同じ敵の実装に挑戦するもよしです。
まとめ
Dataクラスを拡張してデッキ管理の機能を追加しました。今後もセーブ&ロードが絡んでくる機能や全シーンで共通になる機能はDataクラスを拡張して実装していきます。
そしてプレイヤーのデッキを参照して戦闘開始時に山札を用意し、そこからカードをドローするという最初の流れを実装しました。Random.Rangeを使えば任意の範囲内の数値をランダムに得ることができます。
次回はさらに戦闘画面を充実させるための開発に踏み込んでいきます。
今回はその準備として敵キャラクターのデータ準備までを行いました。ひとまず1体分のデータだけ作っておけば動作確認可能です。
次の記事:

コメント