【Unity】RPG制作 その17-プレイヤー移動したら一定の確率で敵とエンカウント
前回は探索ステージのマップを制作しました。今回はプレイヤーの移動ボタンを押したら一定の確率で敵をエンカウントするようにします。
プレイヤーのマップ移動を実装するまでは、「進む」ボタンを押したらエンカウントするかどうかを処理していましたが、それを4方向の移動ボタンを押した時に実行させようと思います。まずはじめに、questManagerを開いて、処理を追加していきます。
クリックで展開
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; //New 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 PlayerUIManager playerUIManager; public EnemyGroupList enemyGroupList; public SelectPanelBase selectPanel;//探索中に表示させるイベント用の選択肢ウィンドウ public int stageLength = 10; int encountRate = 40; //エンカウント率(%) EnemyGroup enemyGroup; List<GameObject> enemyObjs = new List<GameObject>(); //生成した敵のGameObjectを格納するList int currentFloor = 1; //現在の階層(表示はB1F,B2F・・・とする) //----------------テスト--------- [SerializeField] UnityEvent OnCompleteMoveRoom;//部屋移動した時に呼び出す関数 New //------------テストここまで------- private void Start() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.QUEST; // ゲームフェーズをQUESTに stageUI.UpdateUI(currentFloor); commandUI.gameObject.SetActive(false); DialogTextManager.instance.SetScenarios(new string[] { "洞窟についた。"}); playerUIManager.SetupUI(PlayerManager.instance); selectPanel.SetSelectablePanel(2); selectPanel.SetContentText(0, "テスト"); selectPanel.SetContentText(1, "Index1"); } 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); currentFloor++; // 進行度をUIに反映 stageUI.UpdateUI(currentFloor); if(stageLength <= currentFloor) { QuestClear(); }else if (encountRate >= UnityEngine.Random.Range(1, 101)) { DialogTextManager.instance.SetScenarios(new string[] { "敵と遭遇した!!" }); yield return new WaitForSeconds(1.2f); SoundManager.instance.PlaySE(8); EncountEnemy(); } else { stageUI.ShowButtons(); } } // Nextボタンが押されたら public void OnNextButton() { SoundManager.instance.PlaySE(0); stageUI.HideButtons(); StartCoroutine(Searching()) ; } public IEnumerator MoveOnRoom() //New { if(OnCompleteMoveRoom == null) OnCompleteMoveRoom = new UnityEvent();//イベント・インスタンスの生成 SoundManager.instance.PlaySE(0); stageUI.HideButtons(); yield return StartCoroutine(Searching()); Debug.Log("quest_MoveOnRoom実行"); OnCompleteMoveRoom.Invoke(); //イベント発行 } public void OnToTownButton() { } void EncountEnemy() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.BATTLE; // ゲームフェーズをBATTLEに 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のセット } } public void EndBattle() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.QUEST; // ゲームフェーズをQUESTに 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 IEnumerator GotoTown() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.TOWN; // ゲームフェーズをTOWNに yield return new WaitForSeconds(0.1f); sceneTransisitonManager.LoadTo("Town"); } 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(GotoTown()); } }
新しくMoveOnRoom関数を作成していますが、OnNextButton関数に関数の処理が終了した時の処理を登録しているだけであとは「進む」ボタンを押した時に実行されていたOnNextButtonと一緒です。
[SerializeField] UnityEvent OnCompleteMoveRoom;
関数の入れ物を定義しておきます。SerializeFieldにしてインスペクターから設定できるようにします。MoveOnRoom関数のはじめにOnCompleteMoveRoomのインスタンスを生成し、関数の最後のOnCompleteMoveRoom.Invoke();で登録された関数の呼び出しを行っています。
次にDangeonMap.csにOnCompleteMoveRoomに登録する関数を作成します。
クリックで展開
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class DangionMap : MonoBehaviour { public GameObject dangionMapSystem; DangionController dangionController; GameObject[,] roomImage = new GameObject[5,5]; //表示するroomImageの二次元配列 GameObject[,] eventIcons = new GameObject[5, 5]; //表示するイベントアイコンの二次元配列 public int[,] structOfDangeon = new int[5,5]{ { 4, 1, 5, 4, 1 }, {14,12, 9,14,12 }, {14, 0, 0, 0,12 }, {14, 0, 0, 0,12 }, { 3,13, 0,13, 2 }, }; public int[,] posOfEvent = new int[5, 5] { { -1,-1, 3,-1,-1 }, { 2,-1,-1,-1, 1 }, { 0,-1,-1,-1,-1 }, { -1,-1,-1, 2,-1 }, { -1,-1,-1,-1,-1 }, }; public bool isOpenDangeonMap = false; //New void Start() { this.gameObject.SetActive(false); dangionController = dangionMapSystem.GetComponent<DangionController>(); SetDangion(); SetEvent(); HideDangeonMap(); } public void ShowDangeonMap() { this.gameObject.SetActive(true); SoundManager.instance.PlaySE(21); isOpenDangeonMap = true; } public void HideDangeonMap() { this.gameObject.SetActive(false); //if (dangionController.isSetDangeonMap)//Dangeonがセットされていれば //{ // dangionController.isSetDangeonMap = false; //} isOpenDangeonMap = false; } public void HideDangeonMapForSearching()//探索時にマップが表示されている場合、一度消して進行が終わった時に再表示するためにisOpenDangeonMapをfalseにせずにDangeonMapを非表示にする関数 { this.gameObject.SetActive(false); } public void ShowDangeonMapForSearching()//quest時のSearchingが終わった時にもともとマップが表示されていたら再度表示、されていなかったら消したままにする { Debug.Log("ShowDangeonMapForSearching実行"); if (isOpenDangeonMap) { this.gameObject.SetActive(true); } } public void SetDangion() { dangionController.isSetDangeonMap = true; Transform parentRoomImage = transform.Find("Panel/roomImages"); Vector3 originPos = parentRoomImage.transform.position; Vector3 pos = new Vector3(originPos.x - 200, originPos.y - 200, 0); for(int j = 0; j < 5; j++) { for(int i = 0; i < 5; i++) { roomImage[j,i] = new GameObject("roomImage" + j + "_" +i); roomImage[j,i].transform.parent = parentRoomImage; Image image = roomImage[j, i].AddComponent<Image>(); image.sprite = dangionMapSystem.GetComponent<DangionData>().roomImages[structOfDangeon[j,i]]; pos.x = (originPos.x - 200) + (i * 100); pos.y = (originPos.y + 200) + (j * -100); roomImage[j, i].transform.position = pos; } } Transform playerParent = transform.Find("Panel/player"); Vector3 playerOPos = playerParent.transform.position; dangionController.playerMapObject = new GameObject("playerObj"); dangionController.playerMapObject.transform.parent = playerParent; Image playerimage = dangionController.playerMapObject.AddComponent<Image>(); playerimage.sprite = dangionController.playerSprite; playerimage.SetNativeSize(); Vector3 playerMapPos = new Vector3(); playerMapPos.x = (playerOPos.x - 200) + (dangionController.playerPos.x * 100); playerMapPos.y = (playerOPos.y + 200) + (dangionController.playerPos.y * -100); dangionController.playerMapObject.transform.position = playerMapPos; } public void DestroyMapInfo() { for (int j = 0; j < 5; j++) { for (int i = 0; i < 5; i++) { if(roomImage[j, i].gameObject != null) { Destroy( roomImage[j, i].gameObject); } if(eventIcons[j, i].gameObject != null) { Destroy(eventIcons[j, i].gameObject); } } } //Array.Clear(roomImage,0,roomImage.Length); //すべての要素をクリア } public void SetEvent() { Transform parentImageIcons = transform.Find("Panel/eventIcons"); Vector3 originPos = parentImageIcons.transform.position; Vector3 pos = new Vector3(originPos.x - 200, originPos.y - 200, 0); for (int j = 0; j < 5; j++) { for (int i = 0; i < 5; i++) { eventIcons[j, i] = new GameObject("eventIcon" + j + "_" + i); eventIcons[j, i].transform.parent = parentImageIcons; Image icon = eventIcons[j, i].AddComponent<Image>(); if(posOfEvent[j, i] != -1) { icon.sprite = dangionMapSystem.GetComponent<DangionData>().eventIcons[posOfEvent[j, i]]; icon.SetNativeSize(); } else { icon.color = new Color(1, 1, 1, 0); } pos.x = originPos.x - 200 + i * 100; pos.y = originPos.y + 200 + j * -100; eventIcons[j, i].transform.position = pos; } } } }
新しくHideDangeonMapForSearching関数とShowDangeonMapForSearching関数を用意しました。QuestManagerのOnCompleteMoveRoomにはShowDangeonMapForSearching関数を登録しておきます。そしてDangeonMapControllerにいって、プレイヤー移動ボタンを押した時にこれらの処理が実行されるように組み込みます。
クリックで展開
public void PlayerMoveDown() //プレイヤーの移動関数 { bool canMove = CheckCanMove(DIRECTION.Down); if (canMove) { dangionMap.HideDangeonMapForSearching(); //New playerPos.y++; StartCoroutine(questManager.MoveOnRoom()); //New } else { SoundManager.instance.PlaySE(18); } UpdatePlayerPosition(); } public void PlayerMoveUp() //プレイヤーの移動関数 { bool canMove = CheckCanMove(DIRECTION.Up); if (canMove) { dangionMap.HideDangeonMapForSearching(); //New playerPos.y--; StartCoroutine(questManager.MoveOnRoom()); //New } else { SoundManager.instance.PlaySE(18); } UpdatePlayerPosition(); } public void PlayerMoveLeft() //プレイヤーの移動関数 { bool canMove = CheckCanMove(DIRECTION.Left); if (canMove) { dangionMap.HideDangeonMapForSearching(); //New playerPos.x--; StartCoroutine(questManager.MoveOnRoom()); //New } else { SoundManager.instance.PlaySE(18); } UpdatePlayerPosition(); } public void PlayerMoveRight() //プレイヤーの移動関数 { bool canMove = CheckCanMove(DIRECTION.Right); if (canMove) { dangionMap.HideDangeonMapForSearching(); //New playerPos.x++; StartCoroutine(questManager.MoveOnRoom()); //New } else { SoundManager.instance.PlaySE(18); } UpdatePlayerPosition(); }
プレイヤー移動時に、元々マップが表示されていれば、移動後にサイドマップを表示するようにしました。実行すると以下の動画のようになります。
通常時はOKなのですが、戦闘に突入した時にマップが表示がされたまま(探索時のコマンドも)になってしまっているので、次はそれを直していきます。
クリックで展開
public bool nowBattle = false; //バトル中か? 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); currentFloor++; // 進行度をUIに反映 stageUI.UpdateUI(currentFloor); if(stageLength <= currentFloor) { QuestClear(); }else if (encountRate >= UnityEngine.Random.Range(1, 101)) { DialogTextManager.instance.SetScenarios(new string[] { "敵と遭遇した!!" }); yield return new WaitForSeconds(1.2f); SoundManager.instance.PlaySE(8); nowBattle = true; yield return StartCoroutine(EncountEnemy()); //コルーチン呼び出し 変更 } else { stageUI.ShowButtons(); } } public IEnumerator MoveOnRoom() { if(OnCompleteMoveRoom == null) OnCompleteMoveRoom = new UnityEvent();//イベント・インスタンスの生成 SoundManager.instance.PlaySE(0); stageUI.HideButtons(); yield return StartCoroutine(Searching()); Debug.Log("quest_MoveOnRoom実行"); if (!nowBattle) //New { OnCompleteMoveRoom.Invoke(); //イベント発行 (ダンジョンマップ再表示) } } public void EndBattle() { GameManager.instance.gamePhase = GameManager.GAMEPHASE.QUEST; // ゲームフェーズをQUESTに commandUI.gameObject.SetActive(false); stageUI.ShowButtons(); nowBattle = false; OnCompleteMoveRoom.Invoke();//ダンジョンマップ表示(インスペクター上でDangeonMap.csのShowDangeonMapForSearching関数を登録してある。) }
QuestManagerに戦闘中かどうかのフラグを宣言します。そのフラグをEncountEnemy関数の処理に入る前にtrueにし、EndBattle関数の中でfalseにします。そして、MoveOnRoomコルーチンの最後で、nowBattleがfalseの場合(戦闘に突入しなかった時)に OnCompleteMoveRoomの登録された関数を実行しています。EndBattle関数の中でも呼び出しています。
それから敵に遭遇した時にマップを表示している場合はCancelButtonとArrowsButtonsを非表示にしたいので、StageUIManager.csに処理を追加します。
クリックで展開
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; public GameObject cancelButton; public GameObject mapButton; public GameObject debugButton; public GameObject arrowsButtons; //New public GameObject itemUI; public GameObject itemUsePanel; private void Start() { stageClearImage.SetActive(false); cancelButton.SetActive(false); } public void InitUI() { 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); mapButton.SetActive(false); debugButton.SetActive(false); arrowsButtons.SetActive(false); //new cancelButton.SetActive(false); //new } public void ShowButtons() { nextButton.SetActive(true); toTownButton.SetActive(true); itemButton.SetActive(true); statusButton.SetActive(true); mapButton.SetActive(true); debugButton.SetActive(true); arrowsButtons.SetActive(true); //new } public void ShowButtons(bool isTrue) { nextButton.SetActive(isTrue); toTownButton.SetActive(isTrue); itemButton.SetActive(isTrue); statusButton.SetActive(isTrue); mapButton.SetActive(isTrue); debugButton.SetActive(isTrue); arrowsButtons.SetActive(isTrue); //new } public void ShowButtonsForSearching(bool isOpenMap) //new { if (dangionMap.isOpenDangeonMap) { cancelButton.SetActive(true); arrowsButtons.SetActive(true); } else { ShowButtons(); } } public void PushItemButton() { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); statusButton.SetActive(false); cancelButton.SetActive(true); mapButton.SetActive(false); debugButton.SetActive(false); arrowsButtons.SetActive(false); //New } public void PushMapButton() //New { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); statusButton.SetActive(false); cancelButton.SetActive(true); mapButton.SetActive(false); debugButton.SetActive(false); arrowsButtons.SetActive(true); } public void PushStatusButton() { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); statusButton.SetActive(false); cancelButton.SetActive(true); mapButton.SetActive(false); debugButton.SetActive(false); arrowsButtons.SetActive(false); //New } public void PushCancelButton() { if (itemUsePanel.activeInHierarchy) { itemUsePanel.gameObject.SetActive(false); itemUI.GetComponent<ItemUI>().canTapItemUI = true; return; } nextButton.SetActive(true); toTownButton.SetActive(true); itemButton.SetActive(true); statusButton.SetActive(true); cancelButton.SetActive(false); mapButton.SetActive(true); debugButton.SetActive(true); arrowsButtons.SetActive(true); //New } public void PushDebugButton() { nextButton.SetActive(false); toTownButton.SetActive(false); itemButton.SetActive(false); statusButton.SetActive(false); cancelButton.SetActive(true); mapButton.SetActive(false); debugButton.SetActive(false); arrowsButtons.SetActive(false); //New } public void ShowClearText() { stageClearImage.SetActive(true); nextButton.SetActive(false); toTownButton.SetActive(true); itemButton.SetActive(false); statusButton.SetActive(false); mapButton.SetActive(false); debugButton.SetActive(true); arrowsButtons.SetActive(false); //New } }
Searchingが終わった時に呼び出す新しい関数を作りました。dangeonMapが開いているときはcancelButtonとArrowButtonsをtrueにし、そうでないときはStageUIManagerのShowButton関数を実行するようにしています。
あとはQuestManagerのSearchingコルーチンとEndBattle関数内のstageUI.ShowButtons関数をstageUI.ShowButtonsForSearchingに差し替えれば終わりです。これでプレイヤー移動と敵とのエンカウント処理をうまくつなげることができました。
今回は以上です。次回はマップの特定の位置にたどり着いたらイベントを発生させる処理を作っていきたいと覆います。