【Unity】RPG制作 その11- アイテム使用処理(探索中)
RPG制作その4とその5でアイテムのUIと所持数の処理は実装したのですが、実際に使う処理をまだ実装していないので、今回はアイテム使用処理を実装していきます。
今回かなり煩雑な内容になっています。参考程度に御覧ください。
現在のゲームの場面に応じて処理が分けられるように、GameManagerでGAMEPHASEという名前で列挙型定数を宣言します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //シングルトン(すべてのシーンで共有する)) public static GameManager instance; private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(this.gameObject); } else { Destroy(this.gameObject); } } //--シングルトン終わり-- public int gold; [System.NonSerialized] public int partyMemberCount = 1; //パーティの人数 New //現在のゲームフェーズの列挙型 public enum GAMEPHASE //New { TOWN, QUEST, BATTLE, }; [System.NonSerialized] public GAMEPHASE gamePhase = GAMEPHASE.TOWN; }
そして、探索の開始時(街から探索に出た時)、戦闘に突入した時、戦闘が終わって探索に戻る時、探索が終わって街に戻る時にgamePhaseを変更します。
クリックで展開
GameManager.instance.gamePhase = GameManager.GAMEPHASE.QUEST; // ゲームフェーズをQUESTに New stageUI.UpdateUI(currentStage); commandUI.gameObject.SetActive(false); DialogTextManager.instance.SetScenarios(new string[] { "洞窟についた。"}); playerUIManager.SetupUI(PlayerManager.instance);
クリックで展開
void EncountEnemy() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.BATTLE; // ゲームフェーズをBATTLEに New stageUI.HideButtons(); commandUI.gameObject.SetActive(true); battleManager.SetUpBattle(); //EnemyGroupの中からランダムに敵グループを生成する //敵のプレハブを配列からランダムに生成する int randomEnemyGroupID = Random.Range(0, enemyGroupList.enemyGroup.Length); enemyGroup = enemyGroupList.enemyGroup[randomEnemyGroupID]; enemyObjs.Clear(); EnemyManager enemy; for (int i = 0; i < enemyGroup.enemys.Length; i++) { enemyObjs.Add(Instantiate(enemyGroup.enemys[i])); enemy = enemyObjs[i].GetComponent<EnemyManager>(); battleManager.SetEnemy(i,enemy); float posX = enemyObjs[i].GetComponent<EnemyUIManager>().SetXPosition(enemyGroup.enemys.Length, i); enemyObjs[i].transform.position = new Vector3(posX, 0.0f, 0.0f); enemyObjs[i].GetComponent<EnemyUIManager>().SetupUI(enemy);//敵のプレハブが保持しているUIのセット New } }
クリックで展開
public void EndBattle() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.QUEST; // ゲームフェーズをQUESTに New commandUI.gameObject.SetActive(false); stageUI.ShowButtons(); }
クリックで展開
public IEnumerator GotoTown() //New { GameManager.instance.gamePhase = GameManager.GAMEPHASE.TOWN; // ゲームフェーズをTOWNに New yield return new WaitForSeconds(0.1f); sceneTransisitonManager.LoadTo("Town"); }
それからアイテムの個別の効果を設定するItemDataスクリプトのenum STATUSCONDITIONとPlayerManagerのenum STATUSCONDITIONを共用したいので、どちらもGeneralEnum型に宣言し直します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GeneralEnum { public enum STATUS //New { HP, MP, ATK, DEF, AGI, LUK, SENCE, MAGPOW, MAGDEF, LEARNABI, } public enum STATUSCONDITION { DEAD, POISON, //毒 (毎ターンダメージ。戦闘が終わっても治らない。) BLIND, //盲目(通常攻撃の命中率大幅ダウン(3分の1)) PARALYSIS, //麻痺(数ターン身動きができない。魔法の詠唱は出来る。) SLEEP, //睡眠(数ターン行動できない。攻撃を受けると高確率で起きる。) CONFUSION, //混乱 (数ターン敵味方判別せずに通常攻撃する。敵から攻撃を受けると解除する時がある。) BURN, //燃焼 FROZEN, //凍結 (数ターン動けない) ATKUP, //攻撃力UP DEFUP, //防御力UP AGIUP, //速さUP LUKUP, //運の良さUP HITRATEUP, //命中率UP EVASIONRATEUP, //回避率UP CRITICALRATEUP, //クリティカル率UP MAGPOWUP, //魔力UP MAGDEFUP, //魔法防御力UP ATKDOWN, //攻撃力DOWN DEFDOWN, //防御力DOWN AGIDOWN, //速さDOWN LUKDOWN, //運の良さDOWN HITRATEDOWN, //命中率DOWN EVASIONRATEDOWN, //回避率DOWN CRITICALRATEDOWN, //クリティカル率DOWN MAGPOWDOWN, //魔力DOWN MAGDEFDOWN, //魔法防御力DOWN } }
PlayerManagerとItemDataのもともとSTATUSCONDITIONだった部分の前にGeneralEnum.をつけて置き換えます。
これでSTATUSCONDITIONをスクリプト間で共有できるようになりました。
それでは今からアイテムの使用処理の実装をしていきます。今回は探索中に回復アイテムが使用できるようにしていきます。
ItemUIのアイテムの項目をタップした時に、使用するターゲットを選択する為のパネルと、アイテム使用時にテキストを表示するためのパネルを作成します。ItemUICanvasの階層下にPanelオブジェクトを2つ作成し、それぞれ名前をItemUsePanel,UseItemMessageとします。そしてスクリプト、”ItemUsePanel”を新規作成し、ItemUsePanelオブジェクトに貼り付けます。
ItemUsePanel.csを開き、以下のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ItemUsePanel : MonoBehaviour { public PlayerUIManager playerUIManager; public GameObject itemCanvas; public GameObject itemUI; public GameObject cancelButton; public bool canTapItemUsePanel = true; public void SetHealItemUsePanel(GameObject useItemMessagePanel,ItemData itemData,ItemUsePanel itemUsePanel) { ResetItemUsePanel(); for (int i = 0; i < GameManager.instance.partyMemberCount; i++) { GameObject targetPartyMember = new GameObject("PartyMember" + (i + 1).ToString()); targetPartyMember.transform.parent = this.transform; //ItemUsePanelオブジェクトを親にする var Rect = targetPartyMember.AddComponent<RectTransform>(); Rect.transform.localPosition = new Vector3(0 ,0 ,0 ); Rect.transform.localScale = new Vector3(1, 1, 1); Rect.sizeDelta = new Vector3(0,100); targetPartyMember.AddComponent<CanvasRenderer>(); targetPartyMember.AddComponent<Image>(); targetPartyMember.AddComponent<LayoutElement>().preferredHeight = 100; var buttonState = targetPartyMember.AddComponent<Button>(); //背景の色を変えて見やすく var colors = buttonState.colors; if (this.transform.childCount % 2 == 0) { colors.normalColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.highlightedColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.pressedColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.disabledColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); } else { colors.normalColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.highlightedColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.pressedColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.disabledColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); } buttonState.colors = colors; GameObject text = new GameObject("Item" + (i + 1).ToString()); text.transform.parent = targetPartyMember.transform; var rect = text.AddComponent<RectTransform>(); rect.transform.localPosition = new Vector3(0, 0, 0); rect.transform.localScale = new Vector3(1,1,1); rect.sizeDelta = new Vector2(900, 90); text.AddComponent<CanvasRenderer>(); var textChild = text.AddComponent<Text>(); //----------------プレイヤー名のテキスト生成----------- textChild.text = PlayerManager.instance.pname; textChild.color = new Color(255f / 255f, 255f / 255f, 255f / 255f, 255f / 255f); textChild.fontSize = 30; textChild.alignment = TextAnchor.MiddleCenter; textChild.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font; //-----------ボタンを押したときの処理------ var itemEvent = targetPartyMember.AddComponent<itemEvent>(); itemEvent.useItemMessaagePanel = useItemMessagePanel; itemEvent.itemData = itemData; itemEvent.itemUsePanel = itemUsePanel; itemEvent.playerUIManager = this.playerUIManager; itemEvent.cancelButton = cancelButton; itemEvent.itemCanvas = this.itemCanvas; buttonState.onClick.AddListener(() => itemEvent.UseHealItem(PlayerManager.instance)); ; } } public void SetAttackItemUsePanel() { } public void ResetItemUsePanel() { if(this.gameObject.transform.childCount != 0) { foreach(Transform child in this.gameObject.transform) { //パネルの要素を全て削除する Destroy(child.gameObject); } } } }
リストの作り方はItemUIと同じで、項目の生成個数をGameManagerのint型変数のPartyMemberCountの数にしています。現在はPlayer一人だけなので個数を1にしています。パネルの下に生成したゲームオブジェクトにItemEventクラス(今回新規作成するクラス)をAddComponentし、ItemEventクラスの関数をonclickに登録しています。
新規作成したitemEvent.csの中身はこんな感じです。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //アイテムUIでButtonコンポーネント付きのオブジェクトにアタッチするスクリプト //各アイテムの処理を行う public class itemEvent : MonoBehaviour { ItemManager itemManager; public ItemData itemData; public GameObject itemCanvas; public ItemUsePanel itemUsePanel; public ItemUI itemUI; public GameObject itemUIView; public GameObject useItemMessaagePanel; public Text useItemText; TermManager termManagerF; public PlayerUIManager playerUIManager; public GameObject cancelButton; void Start() { itemManager = GameObject.Find("ItemManager").GetComponent<ItemManager>();//階層上の"ItemManager"を取得 itemUI = GameObject.Find("ItemUISystem").GetComponent<ItemUI>(); itemUIView = GameObject.Find("ItemUI_Scroll View"); termManagerF = new TermManager(); } // Update is called once per frame void Update() { } public void SetItemTargetPanel() { Debug.Log(itemUI); if (itemUI.GetComponent<ItemUI>().canTapItemUI) { itemUI.GetComponent<ItemUI>().canTapItemUI = false; useItemText = useItemMessaagePanel.transform.GetChild(0).GetComponent<Text>(); if (GameManager.instance.gamePhase == GameManager.GAMEPHASE.QUEST) //ゲームフェーズがQUESTの場合 { if (itemData.canUseScene == ItemData.AVAILABLESCENES.BATTLEONLY)//戦闘時のみ使用可の場合は使えない { SoundManager.instance.PlaySE(11); return; } if (itemData.itemType == ItemData.ITEMTYPE.HEAL) //回復アイテムの場合の処理 { // とりあえず使用対象がプレイヤーに対してと仮定してスクリプトを組む itemUsePanel.gameObject.SetActive(true); //itemUI.SetActive(false); itemUsePanel.SetHealItemUsePanel(useItemMessaagePanel, itemData, itemUsePanel); } else if (itemData.itemType == ItemData.ITEMTYPE.BOOKOFMAGIC) //魔導書アイテムの場合 { itemUsePanel.gameObject.SetActive(true); //itemUI.SetActive(false); } } else if (GameManager.instance.gamePhase == GameManager.GAMEPHASE.BATTLE) { if (itemData.canUseScene == ItemData.AVAILABLESCENES.QUESTONLY) { SoundManager.instance.PlaySE(11); return; } if (itemData.itemType == ItemData.ITEMTYPE.HEAL) //回復アイテムの場合の処理 { // とりあえず使用対象がプレイヤーに対してと仮定してスクリプトを組む itemUsePanel.gameObject.SetActive(true); //itemUI.SetActive(false); itemUsePanel.SetHealItemUsePanel(useItemMessaagePanel, itemData, itemUsePanel); } else if (itemData.itemType == ItemData.ITEMTYPE.ATTACK) { itemUsePanel.gameObject.SetActive(true); //itemUI.SetActive(false); itemUsePanel.SetAttackItemUsePanel(); } } } } public void UseHealItem(PlayerManager player) { if (itemUsePanel.canTapItemUsePanel) { itemUsePanel.canTapItemUsePanel = false; StartCoroutine(IEUseHealItem(player)); } } //回復アイテムを使用したときの処理(状態異常治療も含む) public IEnumerator IEUseHealItem(PlayerManager player) { useItemMessaagePanel.SetActive(true); useItemText = useItemMessaagePanel.transform.GetChild(0).GetComponent<Text>(); Debug.Log(useItemText); useItemText.text = player.pname + "は" + itemData.itemName + "を使った!"; yield return new WaitForSeconds(0.5f); SoundManager.instance.PlaySE(itemData.UseSEID);//アイテム使用SE yield return new WaitForSeconds(0.7f); useItemText.text = ""; yield return new WaitForSeconds(0.5f); //回復アイテムの回復量を計算する(最大HP * 回復割合 + 固定回復量) int hpPoint = (int)(player.maxHp * itemData.HPHealRateValue + itemData.healHPPoint); int mpPoint = (int)(player.maxMp * itemData.MPHealRateValue + itemData.healMPPoint); int tpPoint = (int)(player.maxTp * itemData.TPHealRateValue + itemData.healTPPoint); bool wasItemEffective = false; player.hp += hpPoint; player.mp += mpPoint; player.tp += tpPoint; if(player.hp > player.maxHp) { hpPoint -= player.hp - player.maxHp; //回復量をテキストに出力するために計算 player.hp = player.maxHp; } if(hpPoint > 0) { useItemText.text = player.pname + "のHPが" + hpPoint + "回復した!"; SoundManager.instance.PlaySE(12);//回復SE yield return StartCoroutine(MessageWait1()); wasItemEffective = true; } if (player.mp > player.maxMp) { mpPoint -= player.mp - player.maxMp; player.mp = player.maxMp; } if(mpPoint > 0) { useItemText.text = player.pname + "のMPが" + mpPoint + "回復した!"; SoundManager.instance.PlaySE(12); yield return StartCoroutine(MessageWait1()); wasItemEffective = true; } if (player.tp > player.maxTp) { tpPoint -= player.tp - player.maxTp; player.tp = player.maxTp; } if(tpPoint > 0) { useItemText.text = player.pname + "のTPが" + tpPoint + "回復した!"; SoundManager.instance.PlaySE(12); yield return StartCoroutine(MessageWait1()); wasItemEffective = true; } //探索用の特殊効果実装部分(未実装) if(itemData.tempQuestBuff1 == ItemData.QUESTBUFF.AVOIDENEMIES) { } if (itemData.tempQuestBuff2 == ItemData.QUESTBUFF.AVOIDENEMIES) { } if (itemData.tempQuestBuff3 == ItemData.QUESTBUFF.AVOIDENEMIES) { } //アイテムの個数を1減らす itemManager.itemCountDictionary[itemData.itemID] -= 1; if (!wasItemEffective) { useItemText.text = "しかし何もおこらなかった"; yield return new WaitForSeconds(1.2f); } playerUIManager.UpdateUI(PlayerManager.instance); CloseItemMessageAndPanel(); LikePushCancelButton(); } public void CloseItemMessageAndPanel() { itemUI.canTapItemUI = true; itemUsePanel.canTapItemUsePanel = true; useItemMessaagePanel.SetActive(false); itemUsePanel.gameObject.SetActive(false); itemUIView.SetActive(true); } public void LikePushCancelButton() //キャンセルボタンが押された時の挙動をitemEventで実行 New { cancelButton.GetComponent<Button>().onClick.Invoke(); } }
このItemEventクラスには、ItemUIの項目がタップされた時に実行されるSetItemTargetPanelと、ItemUsePanelの項目がタップされた時に実行される(回復アイテムの場合は)UseHealItemを定義しています。UseHealItemは先程のItemUsePanelの項目のonclickで実装しました。次はItemUIの項目のonclickにSetItemTargetPanelを登録します。ItemUI.csのSetItemUI関数の以下の部分で記述します。
クリックで展開
//ボタンを押したときの処理に関連するitemEventコンポーネントを付ける var itemEvent = Contents.AddComponent<itemEvent>(); itemEvent.itemData = itemManager.itemDatas[i]; itemEvent.itemUsePanel = itemUsePanel; itemEvent.useItemMessaagePanel = useItemMessagePanel; buttonState.onClick.AddListener(itemEvent.SetItemTargetPanel); //New //-----------イベントここまで
これでitemUIのアイテムの項目が押された時にアイテムの使用者を選択するパネルが表示され、実際にそのパネルで対象を選ぶ(今回はPlayerのみですが)と、実際にアイテムの効果を使用対象に及ぼすことができるようになりました。
ついでにデバッグ用のUIも作成しておきました。
今回は以上です。
【Unity】RPG制作 その10- プレイヤーステータスUI作成
今回はプレイヤーのステータスを表示するUIを作成していきたいと思います。
前回作成した能力成長表のキャンバスをコピーして名前をPlayerStatusUICanvasにします。このCanvasの階層下にあるGrowlistをStatusListに改名し、ExpListの方は削除します。ステータスUIのレイアウトは下の写真のようにしました。
このUIを制御するスクリプトPlayerStatusUIを新規作成し、以下のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class PlayerStatusUI : MonoBehaviour { public Text nameText; public Text hpText; public Text mpText; public Text tpText; public Text atkText; public Text defText; public Text agiText; public Text lukText; public Text senceText; public Text magpowText; public Text magdefText; public Text learnAbiText; public void Start() { this.gameObject.SetActive(false); } public void SetStatusText() { nameText.text = "名前 " + PlayerManager.instance.pname; hpText.text = "HP " + PlayerManager.instance.hp + " / " + PlayerManager.instance.maxHp; mpText.text = "MP " + PlayerManager.instance.mp + " / " + PlayerManager.instance.maxMp; tpText.text = "TP " + PlayerManager.instance.tp; atkText.text = "攻撃 " + PlayerManager.instance.at; defText.text = "防御 " + PlayerManager.instance.def; agiText.text = "速さ " + PlayerManager.instance.agi; lukText.text = "運 " + PlayerManager.instance.luk; senceText.text = "感覚 " + PlayerManager.instance.sence; magpowText.text = "魔力 " + PlayerManager.instance.magPow; magdefText.text = "魔防 " + PlayerManager.instance.magDef; learnAbiText.text = "習得 " + PlayerManager.instance.learnAbi; } public void ShowStatusUI() { SoundManager.instance.PlaySE(6); SetStatusText(); this.gameObject.SetActive(true); } public void HideStatuIU() { SoundManager.instance.PlaySE(7); this.gameObject.SetActive(false); } }
このスクリプトをPlayerStatusUICanvasに貼り付けます。このUIを表示する際にプレイヤーの能力を参照してテキスト出力するようにしています。
このUIを呼び出せるように、StageUICanvasに新しくButtonを作成します。
このボタンを押したときの処理を実装したいので、PlayerStatusUIManagerの関数とは別に、StageUIManegerの方でも関数を作成します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //StageUIを管理(ステージ数のUI/進行ボタン/街に戻るボタン)の管理 public class StageUIManager : MonoBehaviour { public Text stageText; public GameObject nextButton; public GameObject toTownButton; public GameObject stageClearImage; public GameObject itemButton; public GameObject statusButton; //New public GameObject cancelButton; private void Start() { stageClearImage.SetActive(false); cancelButton.SetActive(false); } public void InitUI() //New { stageClearImage.SetActive(false); stageText.text = string.Format("ステージ:{0}", 1); cancelButton.SetActive(false); } public void UpdateUI(int currentStage) { stageText.text = string.Format("ステージ:{0}", currentStage); } public void HideButtons() { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); statusButton.SetActive(false); //New } public void ShowButtons() { nextButton.SetActive(true); toTownButton.SetActive(true); itemButton.SetActive(true); statusButton.SetActive(true); //New } public void ShowButtons(bool isTrue) { nextButton.SetActive(isTrue); toTownButton.SetActive(isTrue); itemButton.SetActive(isTrue); statusButton.SetActive(isTrue); //New } public void PushItemButton() { itemButton.SetActive(false); statusButton.SetActive(false); // New cancelButton.SetActive(true); } public void PushStatusButton() //New { itemButton.SetActive(false); statusButton.SetActive(false); cancelButton.SetActive(true); } public void PushCancelButton() { itemButton.SetActive(true); statusButton.SetActive(true); //New cancelButton.SetActive(false); } public void ShowClearText() { stageClearImage.SetActive(true); nextButton.SetActive(false); toTownButton.SetActive(true); itemButton.SetActive(false); //New statusButton.SetActive(false); //New } }
新しくstatusButtonを制御できるようにしました。StatusButtonを押した時にStageUIManagerのPushStatusButton関数と、PlayerStatusUIManagerのShowStatusUI関数を実行するように、StatusButtonのButtonコンポーネントのOnclickに関数を登録します。それから、CancelButtonを押した時にStatusUIを閉じるように、CancelButtonのOnClickにHideStatuUI関数を登録します。
これでステータスUIを呼び出せるようになりました。
今回は以上です。
【Unity】RPG制作 その9- プレイヤーのステータス成長処理
前回はプレイヤー死亡時の処理と戦闘終了時の経験値とお金の入手処理を実装しました。今回はプレイヤーの能力経験値が一定値溜まった時にプレイヤー能力を上げる処理を実装していきます。
最初に、ステータスのデータを扱いやすくするために、能力の項目を列挙型で宣言しておきます。
更にステータスを上昇させる際に使用する一時変数のDictionary型を宣言します。
このstatusUpCountには能力ごとに能力値を1上げる回数を格納します。(Exp100につき能力を1上昇させる)その回数を調べてリストに格納する関数と初期化関数,、実際にプレイヤーの能力を上げる関数を次のように定義します。
クリックで展開
public void InitStatusUpCount() { foreach(STATUS value in Enum.GetValues(typeof(STATUS))) { statusUpCount[value] = 0; } } public bool CheckPlayerExpForUpStatus() { InitStatusUpCount(); bool isUpStatus = false; // 経験値100につき能力を1上昇させる。 // 経験値が100以上ある場合はループさせる while(hpExp >= 100) { statusUpCount[STATUS.HP]++; hpExp -= 100; isUpStatus = true; } while (mpExp >= 100) { statusUpCount[STATUS.MP]++; mpExp -= 100; isUpStatus = true; } while (atExp >= 100) { statusUpCount[STATUS.ATK]++; atExp -= 100; isUpStatus = true; } while (defExp >= 100) { statusUpCount[STATUS.DEF]++; defExp -= 100; isUpStatus = true; } while (agiExp >= 100) { statusUpCount[STATUS.AGI]++; agiExp -= 100; isUpStatus = true; } while (lukExp >= 100) { statusUpCount[STATUS.LUK]++; lukExp -= 100; isUpStatus = true; } while (senceExp >= 100) { statusUpCount[STATUS.SENCE]++; senceExp -= 100; isUpStatus = true; } while (magPowExp >= 100) { statusUpCount[STATUS.MAGPOW]++; magPowExp -= 100; isUpStatus = true; } while (magDefExp >= 100) { statusUpCount[STATUS.MAGDEF]++; magDefExp -= 100; isUpStatus = true; } while (learnAbiExp >= 100) { statusUpCount[STATUS.LEARNABI]++; learnAbiExp -= 100; isUpStatus = true; } return isUpStatus; } public void PlayerStatusUp() { maxHp += statusUpCount[STATUS.HP]; maxMp += statusUpCount[STATUS.MP]; at += statusUpCount[STATUS.ATK]; def += statusUpCount[STATUS.DEF]; agi += statusUpCount[STATUS.AGI]; luk += statusUpCount[STATUS.LUK]; sence += statusUpCount[STATUS.SENCE]; magPow += statusUpCount[STATUS.MAGPOW]; magDef += statusUpCount[STATUS.MAGDEF]; learnAbi += statusUpCount[STATUS.LEARNABI]; InitStatusUpCount(); }
とりあえずこの関数を戦闘終了後に呼び出します。
クリックで展開
IEnumerator EndBattle() { Debug.Log("戦闘終了"); yield return new WaitForSeconds(2f); DialogTextManager.instance.SetScenarios(new string[] { "戦闘終了!!" }); yield return new WaitForSeconds(1f); battleResult.AddGoldToPlayer(); battleResult.AddExpToPlayer(player); player.CheckPlayerExpForUpStatus(); //能力経験値が一定値貯まってるかチェック player.PlayerStatusUp(); //プレイヤーのステータスを上げる処理 DialogTextManager.instance.SetScenarios(new string[] { battleResult.gainGold + "ゴールドを手に入れた。" }); yield return new WaitForSeconds(2f); SoundManager.instance.PlayBGM("Quest"); questManager.EndBattle(); }
これで経験値が一定値貯まったらプレイヤーのステータスを上げる処理が実装できました。ただ、今のままだとステータスが上がったかどうかが目で見て確認できないため、能力値が一つでも上がった場合は、成長がわかるUIを表示するようにします。
経験値取得表とステータス上昇表を画像のように作成しました。
戦闘終了時にこれらのUIを表示します。スクリプトを3つ新規作成し、名前をそれぞれExpList,GrowList,Grow_ExpUIManagerとします。ExpList.csを開き以下のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using System; using UnityEngine; using UnityEngine.UI; public class ExpList : MonoBehaviour { public Slider hpExpBar; public Slider mpExpBar; public Slider atkExpBar; public Slider defExpBar; public Slider agiExpBar; public Slider lukExpBar; public Slider senceExpBar; public Slider magPowExpBar; public Slider magDefExpBar; public Slider learnAbiExpBar; Dictionary<GeneralEnum.STATUS, float> fromValues = new Dictionary<GeneralEnum.STATUS, float>(); //値変更前の経験値Dictionary Dictionary<GeneralEnum.STATUS, float> toValues = new Dictionary<GeneralEnum.STATUS, float>(); //値変更後の経験値Dictionary // ゲージを徐々に増やしたいので、プレイヤーの能力経験値を加算する前に // 現在の能力経験値を保持しておく関数 public void StoreNowPlayerExp(PlayerManager player) { fromValues[GeneralEnum.STATUS.HP] = player.hpExp / 100f; fromValues[GeneralEnum.STATUS.MP] = player.mpExp / 100f; fromValues[GeneralEnum.STATUS.ATK] = player.atExp / 100f; fromValues[GeneralEnum.STATUS.DEF] = player.defExp / 100f; fromValues[GeneralEnum.STATUS.AGI] = player.agiExp / 100f; fromValues[GeneralEnum.STATUS.LUK] = player.lukExp / 100f; fromValues[GeneralEnum.STATUS.SENCE] = player.senceExp / 100f; fromValues[GeneralEnum.STATUS.MAGPOW] = player.magPowExp / 100f; fromValues[GeneralEnum.STATUS.MAGDEF] = player.magDefExp / 100f; fromValues[GeneralEnum.STATUS.LEARNABI] = player.learnAbiExp / 100f; } public IEnumerator ShowPlayerExpList(PlayerManager player) { toValues[GeneralEnum.STATUS.HP] = player.hpExp / 100f; toValues[GeneralEnum.STATUS.MP] = player.mpExp / 100f; toValues[GeneralEnum.STATUS.ATK] = player.atExp / 100f; toValues[GeneralEnum.STATUS.DEF] = player.defExp / 100f ; toValues[GeneralEnum.STATUS.AGI] = player.agiExp / 100f; toValues[GeneralEnum.STATUS.LUK] = player.lukExp / 100f; toValues[GeneralEnum.STATUS.SENCE] = player.senceExp / 100f; toValues[GeneralEnum.STATUS.MAGPOW] = player.magPowExp / 100f; toValues[GeneralEnum.STATUS.MAGDEF] = player.magDefExp / 100f; toValues[GeneralEnum.STATUS.LEARNABI] = player.learnAbiExp / 100f; SoundManager.instance.PlaySE(9); yield return StartCoroutine(GaugeIncrease(2.0f)); yield return new WaitForSeconds(0.3f); } //徐々にゲージを増やすコルーチン IEnumerator GaugeIncrease(float durationTime) { float nowTime = 0.0f; while(nowTime <= 2.0f) { hpExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.HP], toValues[GeneralEnum.STATUS.HP], nowTime / durationTime); mpExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.MP], toValues[GeneralEnum.STATUS.MP], nowTime / durationTime); atkExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.ATK], toValues[GeneralEnum.STATUS.ATK], nowTime / durationTime); defExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.DEF], toValues[GeneralEnum.STATUS.DEF], nowTime / durationTime); agiExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.AGI], toValues[GeneralEnum.STATUS.AGI], nowTime / durationTime); lukExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.LUK], toValues[GeneralEnum.STATUS.LUK], nowTime / durationTime); senceExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.SENCE], toValues[GeneralEnum.STATUS.SENCE], nowTime / durationTime); magPowExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.MAGPOW], toValues[GeneralEnum.STATUS.MAGPOW], nowTime / durationTime); magDefExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.MAGDEF], toValues[GeneralEnum.STATUS.MAGDEF], nowTime / durationTime); learnAbiExpBar.value = Mathf.Lerp(fromValues[GeneralEnum.STATUS.LEARNABI], toValues[GeneralEnum.STATUS.LEARNABI], nowTime / durationTime); nowTime += Time.deltaTime; yield return new WaitForSeconds(0.001f); } yield return new WaitForSeconds(1.0f); yield return new WaitUntil(() => Input.GetMouseButtonDown(0)); } }
ExpList.csを先ほど作成したUIのExpListに貼り付け、BattleManagerの変数に渡します。このスクリプトでは、戦闘終了時に表示する能力経験値の取得表を表示し、経験値の貯まり具合をゲージで視覚化します。元々の数値から少しづつ経験値加算する表現をMathf.Lerp関数で実装しています。
次にGrowList.csを次のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class GrowList : MonoBehaviour { public Text hpText; public Text mpText; public Text atkText; public Text defText; public Text agiText; public Text lukText; public Text senceText; public Text magpowText; public Text magdefText; public Text learnAbiText; Dictionary<GeneralEnum.STATUS, int> nowValue = new Dictionary<GeneralEnum.STATUS, int>(); //値変更前のステータスのDictionary //ステータスを上昇させる前の値を保持しておく関数 public void StoreNowPlayerStatus(PlayerManager player) { nowValue[GeneralEnum.STATUS.HP] = player.maxHp; nowValue[GeneralEnum.STATUS.MP] = player.maxMp; nowValue[GeneralEnum.STATUS.ATK] = player.at; nowValue[GeneralEnum.STATUS.DEF] = player.def; nowValue[GeneralEnum.STATUS.AGI] = player.agi; nowValue[GeneralEnum.STATUS.LUK] = player.luk; nowValue[GeneralEnum.STATUS.SENCE] = player.sence; nowValue[GeneralEnum.STATUS.MAGPOW] = player.magPow; nowValue[GeneralEnum.STATUS.MAGDEF] = player.magDef; nowValue[GeneralEnum.STATUS.LEARNABI] = player.learnAbi; } //ステータス上昇後に表示するリストのShow関数 public IEnumerator ShowStatusTexts(PlayerManager player) { SoundManager.instance.PlaySE(10); hpText.text = "HP " + nowValue[GeneralEnum.STATUS.HP] + " + " + player.statusUpCount[PlayerManager.STATUS.HP]; mpText.text = "MP " + nowValue[GeneralEnum.STATUS.MP] + " + " + player.statusUpCount[PlayerManager.STATUS.MP]; atkText.text = "攻撃 " + nowValue[GeneralEnum.STATUS.ATK] + " + " + player.statusUpCount[PlayerManager.STATUS.ATK]; defText.text = "防御 " + nowValue[GeneralEnum.STATUS.DEF] + " + " + player.statusUpCount[PlayerManager.STATUS.DEF]; agiText.text = "速さ " + nowValue[GeneralEnum.STATUS.AGI] + " + " + player.statusUpCount[PlayerManager.STATUS.AGI]; lukText.text = "運 " + nowValue[GeneralEnum.STATUS.LUK] + " + " + player.statusUpCount[PlayerManager.STATUS.LUK]; senceText.text = "感覚 " + nowValue[GeneralEnum.STATUS.LUK] + " + " + player.statusUpCount[PlayerManager.STATUS.LUK]; magpowText.text = "魔力 " + nowValue[GeneralEnum.STATUS.MAGPOW] + " + " + player.statusUpCount[PlayerManager.STATUS.MAGPOW]; magdefText.text = "魔防 " + nowValue[GeneralEnum.STATUS.MAGDEF] + " + " + player.statusUpCount[PlayerManager.STATUS.MAGDEF]; learnAbiText.text = "習得 " + nowValue[GeneralEnum.STATUS.LEARNABI] + " + " + player.statusUpCount[PlayerManager.STATUS.LEARNABI]; yield return new WaitForSeconds(2.0f); yield return new WaitUntil(() => Input.GetMouseButtonDown(0)); } }
このスクリプトを"GrowList"ゲームオブジェクトに渡します。このスクリプトは能力が上昇した際に表示する能力のもとの値と増加した値を、能力ごとにテキストに書き表すものです。
それぞれコルーチンのWaitUntil関数を使ってマウスクリック(あるいはタッチ)したら表を閉じるようにしています。
そしてGrow_ExpUIManager.csを次のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Grow_ExpUIManager : MonoBehaviour { public GameObject basePanel; public ExpList expList; public GrowList growList; private void Start() { basePanel.SetActive(false); } public void ShowExpList() { basePanel.SetActive(true); expList.gameObject.SetActive(true); growList.gameObject.SetActive(false); } public void ShowGrowList() { expList.gameObject.SetActive(false); growList.gameObject.SetActive(true); } public void CloseGrow_EXPUI() { basePanel.SetActive(false); } }
このスクリプトでは、ExpListとGrowListの表示非表示の制御を行います。
以上のスクリプトを制作したら、BattleManageで以下のように処理を実装します。
BattleManagerのEndBattle関数を以下のように記述します。
クリックで展開
IEnumerator EndBattle() { Debug.Log("戦闘終了"); yield return new WaitForSeconds(2f); DialogTextManager.instance.SetScenarios(new string[] { "戦闘終了!!" }); yield return new WaitForSeconds(1f); grow_ExpUIManager.expList.StoreNowPlayerExp(player); //加算前の能力経験値保存 grow_ExpUIManager.growList.StoreNowPlayerStatus(player); //変更前のプレイヤーステータス値保存 battleResult.AddGoldToPlayer(); //獲得金貨加算処理 battleResult.AddExpToPlayer(player);//能力経験値加算処理 grow_ExpUIManager.ShowExpList(); //経験値表を見せる yield return StartCoroutine(grow_ExpUIManager.expList.ShowPlayerExpList(player)); bool isStatusUp = player.CheckPlayerExpForUpStatus(); //能力経験値が一定値貯まってるかチェック if (isStatusUp) { grow_ExpUIManager.ShowGrowList(); //成長表を見せる yield return StartCoroutine(grow_ExpUIManager.growList.ShowStatusTexts(player)); player.PlayerStatusUp(); //UIを見せ終わったらプレイヤーのステータスを上げる処理 } grow_ExpUIManager.CloseGrow_EXPUI(); DialogTextManager.instance.SetScenarios(new string[] { battleResult.gainGold + "ゴールドを手に入れた。" }); yield return new WaitForSeconds(2f); SoundManager.instance.PlayBGM("Quest"); questManager.EndBattle(); }
経験値表と能力上昇表をコルーチンを使って表示します。能力上昇表は能力が一つでも上昇した場合のみ表示します。
実際にテストするとこんな感じです。
今回は以上です。
【Unity】RPG制作 その8- プレイヤー死亡時処理、戦闘勝利時処理(経験値、お金)
前回は戦闘時のプレイヤーと敵の行動順の処理を実装しました。今回はプレイヤー死亡時の処理と、戦闘勝利時の処理を実装していきたいと思います。
BattleManagerを開き、以下の部分を修正します。
クリックで展開
IEnumerator PlayerDeath() { yield return new WaitForSeconds(0.5f); SoundManager.instance.StopBGM(); DialogTextManager.instance.SetScenarios(new string[] { "全滅した・・・。" }); SoundManager.instance.PlayBGM("GameOver"); yield return new WaitForSeconds(10f); yield return StartCoroutine(questManager.GotoTown()); //New }
PlayerDeathはプレイヤーがやられた時に呼び出される関数です。この関数をIEnumeratoで定義し、プレーヤーのHPが0になった時に呼び出すようにします。questManager.GotoTown()は単に"Town"シーンに遷移する関数です。
クリックで展開
IEnumerator EnemyTurn(int index) { commandUI.ShowDialogUI(); SoundManager.instance.PlaySE(1);//攻撃のSE DialogTextManager.instance.SetScenarios(new string[] { enemysList[index].name + "の攻撃!\n" }); yield return new WaitForSeconds(0.8f); //1.2秒間待ってから以後の処理を実行 if (EnemyCheckHitAttack(index))//命中したら { playerDamagePanel.DOShakePosition(0.3f, 0.5f, 20, 0, false, true); //0.3秒かけて画面を揺らす bool isCritical = EnemyCheckCritical(index); int damage = enemysList[index].Attack(player,isCritical); //敵の攻撃関数 if (isCritical) { SoundManager.instance.PlaySE(5); //クリティカル時SE再生 DialogTextManager.instance.SetScenarios(new string[] { "クリティカル!!\n" + player.pname + "に" + damage + "ダメージを与えた。" }); yield return new WaitForSeconds(0.8f); } else { DialogTextManager.instance.SetScenarios(new string[] { player.pname + "に" + damage + "ダメージを与えた。" }); yield return new WaitForSeconds(0.8f); } playerUI.UpdateUI(player); } else { SoundManager.instance.PlaySE(4); //回避時SE再生 DialogTextManager.instance.SetScenarios(new string[] { player.pname + "は攻撃をかわした。" }); yield return new WaitForSeconds(0.8f); } yield return new WaitForSeconds(1f); if (player.hp <= 0) { // playerが死んだ場合の実装 yield return StartCoroutine(PlayerDeath()); } }
EnemyTurn関数で敵の攻撃の処理を終えた後にplayer.hp<0かどうかを判定し、trueであればPlayerDeathコルーチンを開始します。
次は経験値とお金の実装をします。新規でGameManager.csを作成し、ヒエラルキーに同名のゲームオブジェクトを作成して、GameManager.csを貼り付けます。GameManager.csを作成し、次のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //シングルトン(すべてのシーンで共有する)) public static GameManager instance; private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(this.gameObject); } else { Destroy(this.gameObject); } } //--シングルトン終わり-- public int gold; }
ゲームオブジェクトをシングルトンにして全てのシーンで共有できるようにします。中身は今は単にお金を保持するのみです。
それからプレイヤーの情報もシーンが変わっても保持するようにしたいので、PlayerManager.csの先頭に以下の記述をしてシングルトンにしておきます。
クリックで展開
private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(this.gameObject); } else { Destroy(this.gameObject); } } //--シングルトン終わり--
次にバトル中に獲得したお金や能力経験値を一時的に保持しておくスクリプトを作成します。BattkeResultスクリプトを新規作成し、次のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using System; using UnityEngine; public class BattleResult : MonoBehaviour { public int gainGold; //入手したゴールド public int hpExp; public int mpExp; public int atExp; public int defExp; public int agiExp; public int lukExp; public int senceExp; public int magPowExp; public int magDefExp; public int learnAbiExp; // コンストラクタ public BattleResult() { gainGold = 0; hpExp = 0; mpExp = 0; atExp = 0; defExp = 0; agiExp = 0; lukExp = 0; senceExp = 0; magPowExp = 0; magDefExp = 0; learnAbiExp = 0; } public void AddGainGold(EnemyManager enemy) { gainGold += enemy.gold; } public void AddGainExp(EnemyManager enemy) { hpExp += enemy.hpExp; mpExp += enemy.mpExp; atExp += enemy.atkExp; defExp += enemy.defExp; agiExp += enemy.agiExp; lukExp += enemy.lukExp; senceExp += enemy.senceExp; magPowExp += enemy.magPowExp; magDefExp += enemy.magDefExp; learnAbiExp += enemy.learnAbiExp; } public void AddExpToPlayer(PlayerManager player) { player.hpExp += this.hpExp; player.mpExp += this.mpExp; player.atExp += this.atExp; player.defExp += this.defExp; player.agiExp += this.agiExp; player.lukExp += this.lukExp; player.senceExp += this.senceExp; player.magPowExp += this.magPowExp; player.magDefExp += this.magDefExp; player.learnAbiExp += this.learnAbiExp; } public void AddGoldToPlayer() { GameManager.instance.gold += this.gainGold; } }
戦闘開始時にこのクラスのコンストラクタで初期化し、敵撃破時にAddGainGoldとAddGainExpを呼び出します。
クリックで展開
public void SetUpBattle() { SoundManager.instance.PlayBGM("Battle"); player = PlayerManager.instance; //変更 playerUI.SetupUI(player); commandUI.gameObject.SetActive(true); commandUI.SetBattleCommand(); battleResult = new BattleResult(); // クラスから実体生成 New }
クリックで展開
IEnumerator EnemyDeath(int enemyIndex) { yield return new WaitForSeconds(0.5f); SoundManager.instance.PlaySE(3); //敵撃破時SE enemysList[enemyIndex].enemyImage.transform.DOScale(new Vector3(2.0f, 2.0f, 2.0f), 1f); //敵画像を1秒かけて2倍の大きさにする enemysList[enemyIndex].enemyImage.DOColor(new Color(1f, 0f, 0f, 0f), 1f); //徐々に赤く、透明にする(敵撃破演出) yield return new WaitForSeconds(2f);//敵撃破演出に1秒時間がかかるので余裕を見て2秒処理を待つ DialogTextManager.instance.SetScenarios(new string[] { enemysList[enemyIndex].name + "を倒した" }); yield return new WaitForSeconds(0.5f); //敵を撃破したらbattleResultに獲得能力経験値と獲得ゴールドを加算する battleResult.AddGainGold(enemysList[targetIndex]); //New battleResult.AddGainExp(enemysList[targetIndex]); //New Debug.Log("enemysList[targetIndex]のオブジェクト破壊"); Destroy(enemysList[targetIndex].gameObject); enemysList.Remove(key: targetIndex); Debug.Log("enemysListの要素数は" + enemysList.Count); DeletAgiDicElemDeathEnemy(targetIndex); Debug.Log("AgiDicの要素数は" + AgiDic.Count); }
更に、戦闘が終了した時に、battleResultが保持していた能力経験値とゴールドをPlayerManagerの能力経験値とゴールドに加算します。
クリックで展開
IEnumerator EndBattle() { Debug.Log("戦闘終了"); yield return new WaitForSeconds(2f); DialogTextManager.instance.SetScenarios(new string[] { "戦闘終了!!" }); yield return new WaitForSeconds(2f); battleResult.AddGoldToPlayer(); battleResult.AddExpToPlayer(player); DialogTextManager.instance.SetScenarios(new string[] { battleResult.gainGold + "ゴールドを手に入れた。" }); yield return new WaitForSeconds(2f); SoundManager.instance.PlayBGM("Quest"); questManager.EndBattle(); }
これで戦闘で得たお金と経験値を入手する処理が完成しました。
今回はここまでです。
【Unity】RPG制作 その7-戦闘時の行動順の処理 他バグ修正
前回は戦闘時に敵を複数出現させる処理を実装しました。今回はプレイヤーと敵が速さ順に行動するように処理を実装していきたいと思います。
その前に、現状敵を撃破した時にの演出で、敵の画像と一緒に敵のUIも一緒に拡大するようになってしまっているため、修正します。
EnemyManagerに以下の記述を追加します。
[Header("敵の画像")]public SpriteRenderer enemyImage; //敵の画像
次に敵のプレハブを開き、一番上の階層のGameObjectのSpriteRendererコンポーネントを削除し、そのGameObjectの階層下に空のGameObjectを作成し、名前をImageにします。ImageにSpriteRendererコンポーネントを追加し、SpriteRendererのSpriteに表示させる敵画像を割り当てます。最後に、"Image"オブジェクトを、プレハブの最上位のオブジェクトのEnemyManagerコンポーネントのEnemyImageに割り当てて、プレハブを保存します。
ついでに、BoxCollider2Dの判定の大きさが画像に対してかなり大きかったので画像に合わせておきました。もともと敵をタップした時にindexの割当がうまくいってないかのような挙動をしていたのですが、これで修正できました。
次にBattleManager.csを開き、以下の部分を変更します。
クリックで展開
IEnumerator EnemyDeath(int enemyIndex) { yield return new WaitForSeconds(0.5f); SoundManager.instance.PlaySE(3); //敵撃破時SE enemysList[enemyIndex].enemyImage.transform.DOScale(new Vector3(2.0f, 2.0f, 2.0f), 1f); //敵画像を1秒かけて2倍の大きさにする **変更 enemysList[enemyIndex].enemyImage.DOColor(new Color(1f, 0f, 0f, 0f), 1f); //徐々に赤く、透明にする(敵撃破演出) **変更 yield return new WaitForSeconds(2f);//敵撃破演出に1秒時間がかかるので余裕を見て2秒処理を待つ DialogTextManager.instance.SetScenarios(new string[] { enemysList[enemyIndex].name + "を倒した" }); Debug.Log("enemysList[targetIndex]のオブジェクト破壊"); Destroy(enemysList[targetIndex].gameObject); enemysList.Remove(key: targetIndex); Debug.Log("enemysListの要素数は" + enemysList.Count); DeletAgiDicElemDeathEnemy(targetIndex); Debug.Log("AgiDicの要素数は" + AgiDic.Count); }
デバッグ用のメッセージ表示も一緒にしまっていますがご容赦ください。EnemyDeath関数内の死亡時の演出(1秒かけてサイズを2倍に、色をだんだん赤くする処理)を敵のImageのみに適用するようにします。enemysList[enemyIndex].enemyImage.transform.DOScaleで敵画像のみサイズを変え、enemysList[enemyIndex].enemyImage.DOColorで敵画像のみ色を変えられます。UIのサイズは変わりません。
バグの修正を終えたら、次は戦闘時の行動順の処理を実装していきたいと思います。前回の記事では中途半端に処理を書いてしまっていたので、戦闘時の挙動がバグだらけなので、それが正常に動くようにします。BattleManegerの以下の部分を修正します。
クリックで展開
public void CalcAttackOrder() //変更 { player.CalcAGI(player.agi); enemysList[0].CalcAGI(enemysList[0].agi); enemysList[1].CalcAGI(enemysList[1].agi); enemysList[2].CalcAGI(enemysList[2].agi); AgiDic[0] = player.battleAgi; //0番 プレイヤー AgiDic[1] = enemysList[0].battleAgi * UnityEngine.Random.Range(-30, 31);//1番 enemysID0の敵 AgiDic[2] = enemysList[1].battleAgi * UnityEngine.Random.Range(-30, 31); ;//2番 enemysID1の敵 AgiDic[3] = enemysList[2].battleAgi * UnityEngine.Random.Range(-30, 31); ;//3番 enemysID2の敵 }
この関数はプレイヤーと敵の行動順を計算する関数です。この書き方だと、敵が1体、2体の場合はenemysList[2](Dictionary)の範囲外アクセスになってしまいます。3体の場合でも敵を1体倒した後に
その倒した敵のenemyListにアクセスしようとするとエラーになります。なので、enemyListにアクセスする前にenemysList.ContainsKey(index)で指定したindexのKeyが存在するかを判定し、存在した場合のみenemysListにアクセスするようにします。
修正したものが以下になります。
クリックで展開
public void CalcAttackOrder() //変更 { Debug.Log("行動順の計算"); AgiDic.Clear(); //念の為行動順を格納する前にAgiDicの要素全消去 player.CalcAGI(player.agi); AgiDic[0] = player.battleAgi; //0番 プレイヤー for(int i = 0;i < 3; i++) { if (enemysList.ContainsKey(i)) { enemysList[i].CalcAGI(enemysList[i].agi); AgiDic[i + 1] = enemysList[i].battleAgi + (int)(enemysList[i].battleAgi * (UnityEngine.Random.Range(-0.3f, 0.3f)));//1番 enemysID0の敵 } } Debug.Log("速さのリストを表示"); Debug.Log("playerの速さ" + AgiDic[0]); for(int i = 0; i < 3; i++) { if (enemysList.ContainsKey(i)) { Debug.Log("enemyindex" + i + "の速さ" + AgiDic[i + 1]); } } }
ついでに速さのDictionaryに格納する際に、バトラーの速さ+-30%~30%するようにしました。
この関数をBattleManager.csのTurnProcess関数の中で呼び出します。
クリックで展開
IEnumerator TurnProcess() { CalcAttackOrder(); while (true) { Debug.Log("ターン開始"); yield return new WaitForSeconds(0.1f); int actionIndex = ReadAgiDic(); //actionIndexの数値でどのバトラーが行動するか判断する if (actionIndex == 0) { Debug.Log("プレイヤーの攻撃開始"); yield return StartCoroutine(StartPlayerAttack()); //変更 } else if (1 <= actionIndex && actionIndex <= 3) { yield return StartCoroutine(EnemyTurn(actionIndex - 1)); //変更 } DeleatAgiDicMaxElem(actionIndex); yield return new WaitForSeconds(0.1f); if (AgiDic.Count == 0) { Debug.Log("行動全て処理終わり"); yield return new WaitForSeconds(0.2f); TurnEnd(); yield return new WaitForSeconds(0.1f); Debug.Log("TurnProcessを抜ける"); break; } } }
TurnProcess関数の一番最初にCalcAttackOrder関数を呼び出し、行動順リストを作成し、その後while(true)でループ処理をさせます。ループ処理内で、ReadAgiDic関数で行動順リストの速さが一番大きいValueのKeyのindexを読み込み、actionIndexに格納します。actionIndexで処理を分岐させ、StartPlayerAttackもしくはEnemyTurnのコルーチンを開始します。それぞれのコルーチンには、呼び出しの際、頭にyield return を付けるようにしました。これでこのコルーチンの処理が終わるまでTurnProcess側の処理を待機させられるようになります(StartPlayerattackとTurnProcessが並列処理にならなくなる)。
StartAttack関数、もしくはEnemyTurnのコルーチンが終了したら、DeleatAgiDicMaxElem関数で処理をしたactionIndexのKeyの要素をAgiDicから削除します。前回の記事ではこの関数は引数をとらず、ReadAgiDic関数と同じように、AgiDic内のvalueが最大値のKeyの削除を行っていたのですが、それだとバグを発生させる可能性があるかもしれないと思い、actionIndexを引数に持たせて、DeleatAgiDicMaxElem(int key)関数内でAgiDic.Remove(key)としています。これでプレイヤーと敵が速さ順に行動するようになりました。
次回は戦闘勝利時、死亡時の処理を実装していきたいと思います。
【Unity】RPG制作 その6-敵を複数出現させる。
現在敵は1体のみで出現しますが、今回はそれを複数出現できるように処理を実装していきます。
敵のステータスを表示するUIは敵のプレハブが子オブジェクトとして保持するようにします。
ゲームオブジェクト「QuestManager」の階層下に空のオブジェクト「EnemyGroupList」を作成します。同名のスクリプトを新規作成し、EnemyGroupListに貼り付けます。更に空のオブジェクト「EnemyGroup0」を作成します。そして、syクリプト「EnemyGroup」を新規作成し、オブジェクト「EnemyGroup0」に貼り付けます。
EnemyGroup.csを開いて次のように編集します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyGroup : MonoBehaviour { public GameObject[] enemys; }
公開した配列に敵のプレハブを突っ込みます。これが1回の先頭に同時に出現する敵の組み合わせになります。
EnemyGroupList.csを開いて以下のように編集します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyGroupList : MonoBehaviour { public EnemyGroup[] enemyGroup; }
先程プレハブにしたEnemyGroupをEnemyGroupListの配列にいれます。この配列が探索時において遭遇する敵のグループのリストになります。
敵と遭遇する処理はQuestManagerに記述しているのでそちらを編集していきます。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; //クエスト全体を管理 public class QuestManager : MonoBehaviour { public StageUIManager stageUI; public GameObject[] enemyPrefabs; public BattleManager battleManager; public SceneTransisitonManager sceneTransisitonManager; public GameObject questBG; public CommandUIManager commandUI; public EnemyGroupList enemyGroupList; //出現し得る敵のグループを取得 New public int stageLength = 10; //探索中のステージの長さ New //int[] encountTable = {-1, -1, 0, -1, 0, -1 };削除 int encountRate = 20; //エンカウント率(%) New EnemyGroup enemyGroup; int currentStage = 1; //現在のステージ進行度 (0→1に変更) private void Start() { stageUI.UpdateUI(currentStage); commandUI.gameObject.SetActive(false); DialogTextManager.instance.SetScenarios(new string[] { "洞窟についた。"}); } IEnumerator Searching() { DialogTextManager.instance.SetScenarios(new string[] { "探索中..." }); //背景を大きく questBG.transform.DOScale(new Vector3(1.5f, 1.5f, 1.5f), 2f) .OnComplete(() => questBG.transform.localScale = new Vector3(1, 1, 1)); //フェードアウト SpriteRenderer questBGSpriteRenderer = questBG.GetComponent<SpriteRenderer>(); questBGSpriteRenderer.DOFade(0, 2f) .OnComplete(() => questBGSpriteRenderer.DOFade(1, 0)); //2秒間処理を待機 yield return new WaitForSeconds(2f); currentStage++; // 進行度をUIに反映 stageUI.UpdateUI(currentStage); //-----------変更-------------- if(stageLength <= currentStage) { QuestClear(); }else if (encountRate >= UnityEngine.Random.Range(1, 101)) { DialogTextManager.instance.SetScenarios(new string[] { "敵と遭遇した!!" }); yield return new WaitForSeconds(2f); EncountEnemy(); } else { stageUI.ShowButtons(); } //-----------変更ここまで-------- } // Nextボタンが押されたら public void OnNextButton() { SoundManager.instance.PlaySE(0); stageUI.HideButtons(); StartCoroutine(Searching()) ; } public void OnToTownButton() { } void EncountEnemy() { DialogTextManager.instance.SetScenarios(new string[] { "モンスターに遭遇した!" }); stageUI.HideButtons(); commandUI.gameObject.SetActive(true); battleManager.SetUpBattle(); //EnemyGroupの中からランダムに敵グループを生成00する //敵のプレハブを配列からランダムに生成する int randomEnemyGroupID = Random.Range(0, 3); enemyGroup = enemyGroupList.enemyGroup[randomEnemyGroupID]; if (enemyGroup.enemys.Length == 1) { GameObject enemyObj = Instantiate(enemyGroup.enemys[0]); EnemyManager enemy = enemyObj.GetComponent<EnemyManager>(); battleManager.SetEnemy(0, enemy); enemyObj.GetComponent<EnemyUIManager>().SetupUI(enemy);//敵のプレハブが保持しているUIのセット New }else if (enemyGroup.enemys.Length == 2) { GameObject enemyObj = Instantiate(enemyGroup.enemys[0],new Vector3(-1.5f, 0.0f, 0.0f), Quaternion.identity); EnemyManager enemy = enemyObj.GetComponent<EnemyManager>(); battleManager.SetEnemy(0, enemy); enemyObj.GetComponent<EnemyUIManager>().SetupUI(enemy); GameObject enemyObj2 = Instantiate(enemyGroup.enemys[1], new Vector3(1.5f, 0.0f, 0.0f), Quaternion.identity); EnemyManager enemy2 = enemyObj2.GetComponent<EnemyManager>(); battleManager.SetEnemy(1, enemy2); enemyObj2.GetComponent<EnemyUIManager>().SetupUI(enemy2); }else if (enemyGroup.enemys.Length == 3) { GameObject enemyObj = Instantiate(enemyGroup.enemys[0], new Vector3(-2.0f, 0.0f, 0.0f), Quaternion.identity); EnemyManager enemy = enemyObj.GetComponent<EnemyManager>(); battleManager.SetEnemy(0, enemy); enemyObj.GetComponent<EnemyUIManager>().SetupUI(enemy); GameObject enemyObj2 = Instantiate(enemyGroup.enemys[1], new Vector3(0.0f, 0.0f, 0.0f), Quaternion.identity); EnemyManager enemy2 = enemyObj2.GetComponent<EnemyManager>(); battleManager.SetEnemy(1, enemy2); enemyObj2.GetComponent<EnemyUIManager>().SetupUI(enemy2); GameObject enemyObj3 = Instantiate(enemyGroup.enemys[2], new Vector3(2.0f, 0.0f, 0.0f), Quaternion.identity); EnemyManager enemy3 = enemyObj3.GetComponent<EnemyManager>(); battleManager.SetEnemy(2, enemy3); enemyObj3.GetComponent<EnemyUIManager>().SetupUI(enemy3); } //int randomEnemyID = Random.Range(0,enemyPrefabs.Length); 削除 } public void EndBattle() { commandUI.gameObject.SetActive(false); stageUI.ShowButtons(); } void QuestClear() { DialogTextManager.instance.SetScenarios(new string[] { "終点に着いた。" }); SoundManager.instance.StopBGM(); JingleManager.instance.PlayJingle(0); //クエストクリア!と表示する //街に戻るボタンのみ表示する stageUI.ShowClearText(); //sceneTransisitonManager.LoadTo("Town"); } public void PlayerDeath() { StartCoroutine(DeathEnsyutu()); //New sceneTransisitonManager.LoadTo("Town"); } IEnumerator DeathEnsyutu() //New { SoundManager.instance.StopBGM(); DialogTextManager.instance.SetScenarios(new string[] { "全滅した・・・。" }); SoundManager.instance.PlayBGM("GameOver"); yield return new WaitForSeconds(10f); } }
進むボタンを押したときに20%の確率で敵と遭遇するように変更しました。一応同時に出現する敵の数は3体までにしようと思っているので、EncountEnemyメソッドの中で敵の数で場合分けして敵を出現させるようにしています。ついでにPlayerが死んだときの処理も付け加えておきました。
次に戦闘を開始した後の処理を書いていきます。BattleManagerを開いて次のように記述します。
クリックで展開
using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using DG.Tweening; //PlayerとEnemyの対戦の管理 public class BattleManager : MonoBehaviour { public Transform playerDamagePanel; public QuestManager questManager; public PlayerUIManager playerUI; public CommandUIManager commandUI; public PlayerManager player; public Dictionary<int, EnemyManager> enemysList = new Dictionary<int, EnemyManager>(); //Dictionaryに変更 new public EnemyGroup enemyGroup; public bool canAttackTap; //攻撃のタップが出来るかどうか(連打防止) public int targetIndex = 0; //敵ターゲットのインデックス(プレイヤー攻撃用) New private int nullCount = 0; private Dictionary<int, int> AgiDic = new Dictionary<int, int>();//攻撃の順番のリスト。0がプレイヤー、1~3敵(-1,-2を味方の予定) private void Start() { //enemyUI.gameObject.SetActive(false);削除 //StartCoroutine(SampleCol()); playerUI.SetupUI(player); //enemysList[0] = null; //enemysList[1] = null; //enemysList[2] = null; } //サンプルコルーチン /* IEnumerator SampleCol() { Debug.Log("コルーチン開始"); yield return new WaitForSeconds(2f); Debug.Log("2秒経過"); } */ public void SetUpBattle() { SoundManager.instance.PlayBGM("Battle"); //enemyUI.gameObject.SetActive(true);削除 playerUI.SetupUI(player); //canAttackTap = true; commandUI.gameObject.SetActive(true); commandUI.SetBattleCommand(); } public void SetEnemy(int index , EnemyManager enemyManager) //New { enemysList[index] = enemyManager; //enemyManager.GetComponent<EnemyUIManager>().SetupUI(enemyManager);//敵のプレハブが保持しているUIのセット New enemyManager.enemyIndex = index; enemyManager.AddEventListenerOnTap(EnemyTap); //enemy.transform.DOMove(new Vector3(0,10,0), 5f); } public void EnemyTap() { if (canAttackTap) //攻撃タップ可能であれば { StopAllCoroutines(); StartCoroutine(TurnProcess()); } } IEnumerator TurnProcess() { while (true) { yield return new WaitForSeconds(0.5f); int actionIndex = ReadAgiDic(); if (actionIndex == 0) { StartCoroutine(StartPlayerAttack()); } else if (1 <= actionIndex || actionIndex <= 3) { StartCoroutine(EnemyTurn(actionIndex - 1)); } DeleatAgiDicMaxElem(); if (AgiDic.Count == 0) { TurnEnd(); break; } } } IEnumerator StartPlayerAttack() { SoundManager.instance.PlaySE(1); commandUI.gameObject.SetActive(false); canAttackTap = false; //取り敢えず攻撃メッセージ DialogTextManager.instance.SetScenarios(new string[] { player.pname + "の攻撃!\n"}); yield return new WaitForSeconds(1.2f); //1.2秒間待ってから以後の処理を実行 if (PlayerCheckHitAttack(targetIndex))//命中したら { bool IsCritical = PlayerCheckCritical(targetIndex); int damage = player.Attack(enemysList[targetIndex],IsCritical); if (IsCritical) { SoundManager.instance.PlaySE(5); //クリティカル時SE再生 DialogTextManager.instance.SetScenarios(new string[] { "クリティカル!!\n" + enemysList[targetIndex].name + "に" + damage + "ダメージを与えた。" }); } else { DialogTextManager.instance.SetScenarios(new string[] { enemysList[targetIndex].name + "に" + damage + "ダメージを与えた。" }); } enemysList[targetIndex].gameObject.GetComponent<EnemyUIManager>().UpdateUI(enemysList[targetIndex]); } else { SoundManager.instance.PlaySE(4); //回避時SE再生 DialogTextManager.instance.SetScenarios(new string[] { enemysList[targetIndex].name + "に攻撃をかわされた。" }); } if (enemysList[targetIndex].hp <= 0) { StartCoroutine(EnemyDeath(targetIndex)); if(CheckAllEnemyDeath()) { StartCoroutine(EndBattle()); } } } IEnumerator EnemyTurn(int index) { yield return new WaitForSeconds(2f); //2秒間待ってから以後の処理を実行 SoundManager.instance.PlaySE(1);//攻撃のSE DialogTextManager.instance.SetScenarios(new string[] { enemysList[index].name + "の攻撃!\n" }); yield return new WaitForSeconds(1.2f); //1.2秒間待ってから以後の処理を実行 if (EnemyCheckHitAttack(index))//命中したら { playerDamagePanel.DOShakePosition(0.3f, 0.5f, 20, 0, false, true); //0.3秒かけて画面を揺らす bool isCritical = EnemyCheckCritical(index); int damage = enemysList[index].Attack(player,isCritical); //敵の攻撃関数 if (isCritical) { SoundManager.instance.PlaySE(5); //クリティカル時SE再生 DialogTextManager.instance.SetScenarios(new string[] { "クリティカル!!\n" + player.pname + "に" + damage + "ダメージを与えた。" }); } else { DialogTextManager.instance.SetScenarios(new string[] { player.pname + "に" + damage + "ダメージを与えた。" }); } playerUI.UpdateUI(player); } else { SoundManager.instance.PlaySE(4); //回避時SE再生 DialogTextManager.instance.SetScenarios(new string[] { player.pname + "は攻撃をかわした。" }); } yield return new WaitForSeconds(1f); if (player.hp <= 0) { // playerが死んだ場合の実装 questManager.PlayerDeath(); } } IEnumerator EnemyDeath(int enemyIndex) { yield return new WaitForSeconds(2f); SoundManager.instance.PlaySE(3); //敵撃破時SE enemysList[enemyIndex].transform.DOScale(new Vector3(2.0f, 2.0f, 2.0f), 1f); SpriteRenderer enemySpriteRenderer = enemysList[enemyIndex].GetComponent<SpriteRenderer>(); //enemySpriteRenderer.DOFade(0, 1f); enemySpriteRenderer.DOColor(new Color(1f, 0f, 0f, 0f), 1f); //徐々に赤く、透明にする(敵撃破演出) yield return new WaitForSeconds(2f);//敵撃破演出に1秒時間がかかるので余裕を見て2秒処理を待つ DialogTextManager.instance.SetScenarios(new string[] { enemysList[enemyIndex].name + "を倒した" }); Destroy(enemysList[targetIndex].gameObject); } public bool CheckAllEnemyDeath() { for (int i = 0; i < 3; i++) { if(enemysList[i] = null) { nullCount++; } if(nullCount == 3) { return true; } } return false; } //全キャラ行動終了して再びプレイヤーがコマンド入力出来るようになる。 public void TurnEnd() { commandUI.gameObject.SetActive(true); commandUI.SetBattleCommand();//バトルコマンドのUIをセット } IEnumerator EndBattle() { yield return new WaitForSeconds(2f); DialogTextManager.instance.SetScenarios(new string[] { "モンスターを倒した!" }); yield return new WaitForSeconds(2f); //enemyUI.gameObject.SetActive(false); 削除 SoundManager.instance.PlayBGM("Quest"); //enemyUI.gameObject.SetActive(false); 削除 questManager.EndBattle(); } public bool CanAttackProperty //プロパティ(未使用) { get { return canAttackTap; } set { canAttackTap = value; } } public void SetCanAttackTap(bool canTap) { canAttackTap = canTap; } public void CalcAttackOrder() { player.CalcAGI(player.agi); enemysList[0].CalcAGI(enemysList[0].agi); enemysList[1].CalcAGI(enemysList[1].agi); enemysList[2].CalcAGI(enemysList[2].agi); AgiDic[0] = player.battleAgi; //0番 プレイヤー AgiDic[1] = enemysList[0].battleAgi * UnityEngine.Random.Range(-30, 31);//1番 enemysID0の敵 AgiDic[2] = enemysList[1].battleAgi * UnityEngine.Random.Range(-30, 31); ;//2番 enemysID1の敵 AgiDic[3] = enemysList[2].battleAgi * UnityEngine.Random.Range(-30, 31); ;//3番 enemysID2の敵 } public int ReadAgiDic() { var maxElem = AgiDic.OrderByDescending(c => c.Value) .FirstOrDefault(); return maxElem.Key; } public void DeleatAgiDicMaxElem() { var maxElem = AgiDic.OrderByDescending(c => c.Value) .FirstOrDefault(); AgiDic.Remove(maxElem.Key); } public bool PlayerCheckCritical(int enemyIndex) { int calcCriticalRate; calcCriticalRate = (int)((player.criticalRate + (player.hitRate / 10.0f) + (player.luk / 10.0f)) - ((enemysList[enemyIndex].evasionRate / 30.0f) + (enemysList[enemyIndex].luk / 20.0f))); if (calcCriticalRate >= UnityEngine.Random.Range(1, 101)) { return true; } else { return false; } } public bool EnemyCheckCritical(int enemyIndex) { int calcCriticalRate; calcCriticalRate = (int)((enemysList[enemyIndex].criticalRate + (enemysList[enemyIndex].luk / 10.0f)) - ((player.evasionRate / 30.0f) + (player.luk / 20.0f))); if (calcCriticalRate >= UnityEngine.Random.Range(1, 101)) { return true; } else { return false; } } public bool PlayerCheckHitAttack(int enemyIndex) { int calcHitRate; calcHitRate = (int)((player.hitRate + (player.sence / 10.0f) + (player.luk / 50.0f)) - (enemysList[enemyIndex].evasionRate + (enemysList[enemyIndex].agi / 10.0f) + (enemysList[enemyIndex].luk / 20.0f))); if (calcHitRate >= UnityEngine.Random.Range(1, 101)) { return true; } else { return false; } } public bool EnemyCheckHitAttack(int enemyIndex) { int calcHitRate; calcHitRate = (int)((enemysList[enemyIndex].hitRate + (enemysList[enemyIndex].agi / 10.0f) + (enemysList[enemyIndex].luk / 50.0f)) - (player.evasionRate + (player.agi / 10.0f) + (player.luk / 20.0f))); if (calcHitRate >= UnityEngine.Random.Range(1, 101)) { return true; } else { return false; } } }
敵を複数出現させるために敵の情報にindexを追加して番号で管理するようにしています。それからプレイヤーと敵の行動が素早い順で行動できるように素早さを管理するDictinaryを用意しています。上記のスクリプトではまだ正常に動作しないのですが、敵を複数出現させることはできました。
次回は行動順が正常になるように調整していきます。
【Unity】RPG制作 その5-アイテムのUI作成
今回はアイテムのUIを作成していきたいと思います。
アイテムのリストを作るのにScreenViewを使います。ScreenViewの階層下にあるContentsに、所持数が1以上のアイテム名のTextコンポーネントを保持したゲームオブジェクトを追加していきます。
ヒエラルキーに新しくCanvasを作成し、名前をItemUICanvasにします。ItemUICanvasの階層下に「UI」→「ScrollView」を作成。ItemUICanvasのSortOrderを3に設定。ScrollViewはColorを適当に変更し、ScrollRectコンポーネントのMovementTypeをClampedに変更し、HorizontalScrollbarをNoneに変更。そしてScrollView階層下のScrollbarHorizontalを削除します。
新規に空のオブジェクトを作成し、ItemUISystemを作成。新しくItemUIスクリプトを作成してオブジェクト「ItemUISystem」に貼り付けます。コードは以下のようにします。
クリックで展開
using System.Collections; using System.Collections.Generic; using System; using UnityEngine; using UnityEngine.UI; public class ItemUI : MonoBehaviour { public ItemManager itemManager; public Canvas itemCanvas; ItemManager.Item iEnum; GameObject scrollView; Transform viewPort; Transform content; // Start is called before the first frame update void Start() { scrollView = GameObject.Find("Scroll View");//(注)同じ名前を作らない // スクリプトからScroll Viewのサイズを変更する場合はアンコメント //scrollView.transform.localPosition = new Vector3(0, 0, 0); //scrollView.transform.localScale = new Vector3(1,1,1); //scrollView.GetComponent<RectTransform>().sizeDelta = new Vector2(1000, 1000); viewPort = scrollView.transform.Find("Viewport");//(注)同じ名前を作らない content = viewPort.transform.Find("Content");//(注)同じ名前を作らない //デバッグでアイテムのUIを表示する前に、アイテムの所持数をセットする。 itemManager.TestInit(); SetItemUI(); itemCanvas.gameObject.SetActive(false); } // Update is called once per frame void Update() { } public void SetItemUI() { //テキストを表示する for (int i = 0; i < 20; i++) { iEnum = (ItemManager.Item)Enum.ToObject(typeof(ItemManager.Item), i); if (itemManager.itemCountDictionary[iEnum] == 0) { continue; } else { GameObject Contents = new GameObject("Contents" + (i + 1).ToString()); Contents.transform.parent = content.transform; var Rect = Contents.AddComponent<RectTransform>(); Rect.transform.localPosition = new Vector3(0, 0, 0); Rect.transform.localScale = new Vector3(1, 1, 1); Rect.sizeDelta = new Vector2(0, 100); Contents.AddComponent<CanvasRenderer>(); Contents.AddComponent<Image>(); Contents.AddComponent<LayoutElement>().preferredHeight = 100; var buttonState = Contents.AddComponent<Button>(); //背景の色を変えて見やすくする var colors = buttonState.colors; if (content.transform.childCount % 2 == 0) { colors.normalColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.highlightedColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.pressedColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); colors.disabledColor = new Color(0F / 255F, 0F / 255F, 0F / 255F, 128F / 255F); } else { colors.normalColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.highlightedColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.pressedColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); colors.disabledColor = new Color(0 / 255F, 0 / 255F, 0 / 255F, 50 / 255F); } buttonState.colors = colors; GameObject text = new GameObject("Item" + (i + 1).ToString()); text.transform.parent = Contents.transform; var rect = text.AddComponent<RectTransform>(); rect.transform.localPosition = new Vector3(0, 0, 0); rect.transform.localScale = new Vector3(1, 1, 1); rect.sizeDelta = new Vector2(900, 90); text.AddComponent<CanvasRenderer>(); var textChild = text.AddComponent<Text>(); //-----------アイテム名 × 所持数のテキスト生成 string viewtext = ""; viewtext = itemManager.itemDatas[i].itemName; string countText = " × " + (itemManager.itemCountDictionary[iEnum]).ToString(); textChild.text = itemManager.itemDatas[i].itemName + countText; textChild.color = new Color(255f / 255f, 255f / 255f, 255f / 255f, 255f / 255f); textChild.fontSize = 30; textChild.alignment = TextAnchor.MiddleCenter; textChild.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font; //--------------テキストここまで //ボタンを押したときの処理に関連するitemEventコンポーネントを付ける var itemEvent = Contents.AddComponent<itemEvent>(); itemEvent.itemData = itemManager.itemDatas[i]; //-----------イベントここまで //ボタン上の左側にアイテムのアイコンを表示する GameObject itemIcon = new GameObject("ItemIcon" + (i + 1).ToString()); itemIcon.transform.parent = buttonState.transform; var iconRect = itemIcon.AddComponent<RectTransform>(); iconRect.transform.localPosition = new Vector3(-250, 0, 0); iconRect.transform.localScale = new Vector3(1, 1, 1); //rect.sizeDelta = new Vector2(900, 90); itemIcon.AddComponent<CanvasRenderer>(); var iconImage = itemIcon.AddComponent<Image>(); iconImage.sprite = itemManager.itemDatas[i].itemSprite; } } } public void UpdateItemUI() { DestroyAllItem(); SetItemUI(); } public void DestroyAllItem() { if (content.gameObject.transform.childCount != 0) { foreach(Transform child in content.gameObject.transform) { //全要素削除 Destroy(child.gameObject); } } } public void ShowItemUI() { SoundManager.instance.PlaySE(6); UpdateItemUI(); itemCanvas.gameObject.SetActive(true); } //アイテムUIを消す(見えなくする) public void DeleteItemUI() { SoundManager.instance.PlaySE(7); DestroyAllItem(); itemCanvas.gameObject.SetActive(false); }
publicにしたItemManagerとItemCanvasにそれぞれオブジェクト「ItemManager」とオブジェクト「ItemUICanvas」を貼り付けます。スクリプトの実行内容としては、ItemManagerの全てのアイテムの所持数をチェックして、所持数が1以上のアイテムを ItemUICanvasのScrollViewにアイコン付きのテキストで追加するものです。後で使用時の処理を実装できるようにButtonコンポーネントと新しく作成したItemEventコンポーネントもつけています。
実行すると以下のようになります。
このアイテムのUIをアイテムボタンを押したときに呼び出すようにしたいので、探索時のUIにItemButtonとBackButtonを追加します。
StageUIManagerにアイテムボタンと戻るボタンが押されたときの処理を追加します。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; //StageUIを管理(ステージ数のUI/進行ボタン/街に戻るボタン)の管理 public class StageUIManager : MonoBehaviour { public Text stageText; public GameObject nextButton; public GameObject toTownButton; public GameObject stageClearImage; public GameObject itemButton; //New public GameObject cancelButton; //New private void Start() { stageClearImage.SetActive(false); cancelButton.SetActive(false); //New } public void UpdateUI(int currentStage) { stageText.text = string.Format("ステージ:{0}", currentStage + 1); } public void HideButtons() { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); } public void ShowButtons() { nextButton.SetActive(true); toTownButton.SetActive(true); itemButton.SetActive(true); } public void ShowButtons(bool isTrue) { nextButton.SetActive(isTrue); toTownButton.SetActive(isTrue); itemButton.SetActive(isTrue); } public void PushItemButton() //New { itemButton.SetActive(false); cancelButton.SetActive(true); } public void PushCancelButton() //New { itemButton.SetActive(true); cancelButton.SetActive(false); } public void ShowClearText() { stageClearImage.SetActive(true); nextButton.SetActive(false); toTownButton.SetActive(true); } }
ItemButtonのOnclickに、ItemUISystemオブジェクトのItemUI.ShowItemUI関数とStageUIManagerオブジェクトのStageUIManager.PushItemButton関数を指定します。
BuckButtonのOnClickには、ItemUISystemオブジェクトのItemUI.DeleteItemUI関数とStageUIManagerオブジェクトのStageUIManager.PushCanvasButton関数を指定します。
これでアイテムボタンを押せばItemUIを表示し、戻るボタンを押せばItemUIを閉じることが出来るようになりました。
次回からは戦闘時の処理を実装していきたいと思います。