今回はユニティちゃんRPGでコマンド画面の作成と選択したキャラクターのステータスを表示するところを作っていきたいと思います。
前回はユニティちゃんRPGで使用するキャラクターステータスデータとアイテムデータの作成を行いました。
ユニティちゃんのRPGを作ってみようの他の記事は
から見ることが出来ます。
前回キャラクターのステータスをアセットファイルに設定したので、そのデータを取得して画面に表示するという事を行っていきますが、今回から処理が複雑になってきますので注意してください。
どうやって注意するんだ!!というのはナシでお願いします。(^_^;)
コマンド画面の作成
キャラクターのステータスを表示する為にはコマンド画面を作成し、その中でステータスコマンドを選択できるようにしなければなりません。
そこで最初にコマンド画面UIの作成を行いたいと思います。
コマンドパネルの作成
Villageシーンのヒエラルキー上で右クリックからUI→Canvasを選択し、名前をCommandとします。
Commandを選択し、インスペクタのCanvas ScalerのUI Scale ModeをScale With Screen Sizeにします。
これはゲーム画面のサイズに合わせてUIのScaleを変化させる為です。
Referrence ResolutionはXに1024、Yに768を設定しこの画面サイズを元にUIのScaleを調整します。
Referrence Resolutionは適宜変更してください。
Screen Match ModeはExpandにし、UIが広がるようにします。
Commandを選択した状態で右クリックからUI→Panelを選択し、名前をCommandPanelとします。
CommandPanelのサイズを調整します。
シーンビューでCommandPanelの四隅の矢印をShiftキーを押しながらドラッグし左上に表示されるようにします。
今回は以下のような感じのサイズにしました。
CommandPanelの子要素にはコマンドの選択肢を表示しますが、その選択肢を整列させる為にVertical Layout Groupを追加します。
CommandPanelのインスペクタのAdd ComponentからLayout→Vertical Layout Groupを選択します。
Child AlignmentはMiddle Centerにし、中央に表示するようにし、Child Force ExpandのWidthとHeightにチェックを入れて子要素のコマンドボタンを強制的に広げるようにします。
次にAdd ComponentからLayout→Canvas Groupを取り付けます。
Canvas Groupはコマンドを選択していった時にパネル毎に選択出来たり出来なくしたりの切り替えで使用します。
Canvas Groupが設定されたUIはCanvas Groupのinteractableのチェックを外すとマウスで選択したりフォーカスの移動等が出来なくなります。
なのでスクリプトからCanvas Groupのinteractableの値を操作し、パネル毎に選択の可・不可を切り替えます。
CommandPanelを選択した状態で右クリックからUI→Buttonを選択し、名前をStatusButtonとします。
StatusButtonのインスペクタでWidthに150、Heightに50を設定します。
またImageのチェックを外すかコンポーネント自体を削除しボタンの背景は使わないことにします。
ButtonのTransitionをNoneにし、マウスが上に来た時やボタンの移動等をしてもボタン自体のアニメーションはしないことにします。
その代わりにボタンが選択された時はボタンの横にアイコンを表示しわかりやすくします。
StatusButtonを選択した状態で右クリックからUI→Imageを選択します。
Imageはボタンの横に表示されるように移動させます。
Imageにはボタンが選択された時に表示するSource Imageを設定します。
StatusButtonの子要素のTextにはステータスという文字列を設定し、Font SizeやAlignmentを変更し、Colorでテキストの色を変更します。
これで最初のCommandを表示する部分が出来ました。
ステータスを表示するキャラクター選択パネル
StatusButtonを押した時に次はどのキャラクターのステータスを表示するかを選ぶ必要があります。
Commandを選択した状態で右クリックからUI→Panelを選択し、名前をSelectCharacterPanelとします。
SelectCharacterPanelにもCanvas GroupとVertical Layout Groupコンポーネントを取り付けます。
SelectCharacterPanelはStatusButtonが押されるまでは有効にならないので、Canvas Groupのinteractableのチェックは外しておきます(SelectCharacterPanelの子要素のUIを操作させない)。
SelectCharacterPanelはCommandPanelと近接して作成しました。
パネルの表示位置やサイズ等は同じにする必要はありません。
キャラクターのステータスを表示するパネル
最後にキャラクターのステータスを表示するパネルを作成します。
Commandを選択した状態で右クリックからUI→Panelを選択し、StatusPanelという名前にします。
StatusPanelの子要素にPanelを作成し、名前をCharacterNamePanelとし、その子要素にTextを作成します。
StatusPanelの子要素にPanelを作成し、名前をStatusParamPanelとし、その子要素にTextを4つ作成します。
Textの名前はTitle、Param1、Division、Param2とします。
Font Sizeは25にします。
StatusPanelの階層は
上のようになります。
StatusPanelの領域は
上のような感じで領域を分けました。
StatusParamPanelの4つのTextではAlignmentをそれぞれ変えます。
Titleは左寄せの上から表示
Param1は右寄せの上から表示
DivisionはTextに最初の行に空の文字、次の行に/を書いて改行し、/を書きます。
これは
HP / 最大HP
という表示にする為です。
Divisionは真ん中の上から表示
Param2は右寄せの上から表示とします。
これでコマンドの選択パネルとキャラクターの選択パネル、ステータス表示パネルが出来ました。
コマンド処理スクリプトの作成
コマンドのUIが出来たので次はコマンドの画面を表示したりボタンが押された時の処理を行うスクリプトを作成していきます。
コマンド画面を開くスクリプトの作成
まずはAssets/RPG/Scriptsフォルダの中に新しくUnityChanCommandScriptスクリプトを作成します。
UnityChanCommandScriptスクリプトはMenuボタンを押した時にコマンド画面を開く処理とコマンド画面を閉じる処理を記述しています。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public class UnityChanCommandScript : MonoBehaviour { private LoadSceneManager sceneManager; // コマンド用UI [SerializeField] private GameObject commandUI = null; private UnityChanScript unityChanScript; // Start is called before the first frame update void Start() { sceneManager = GameObject.Find("SceneManager").GetComponent<LoadSceneManager>(); unityChanScript = GetComponent<UnityChanScript>(); } // Update is called once per frame void Update() { if(sceneManager.IsTransition() || unityChanScript.GetState() == UnityChanScript.State.Talk ) { return; } // コマンドUIの表示・非表示の切り替え if(Input.GetButtonDown("Menu")) { // コマンド if (!commandUI.activeSelf) { // ユニティちゃんをコマンド状態にする unityChanScript.SetState(UnityChanScript.State.Command); } else { ExitCommand(); } // コマンドUIのオン・オフ commandUI.SetActive(!commandUI.activeSelf); } } // CommandScriptから呼び出すコマンド画面の終了 public void ExitCommand() { EventSystem.current.SetSelectedGameObject(null); unityChanScript.SetState(UnityChanScript.State.Normal); } } |
LoadSceneManager型のフィールドを用意しシーンの遷移処理をしているスクリプトを保持するようにします。
LoadSceneManagerは後で変更しますが、シーン遷移途中にコマンド画面が開いてしまうと困るのでシーン遷移途中ではコマンド画面を開けないようにします。
その為LoadSceneManagerスクリプトではシーン遷移中かどうかのフラグ(isTransition)を用意し、UnityChanCommandScriptからその値を調べてコマンド画面を開くかどうかを決めます。
シーン遷移中やユニティちゃんが会話の途中だった場合はreturnを実行し、その後の処理をしません。
これは会話途中にコマンド画面が開くと困るからです。
Menuボタンを押したらCommandがアクティブの時はUnityChanScriptスクリプトのSetStateメソッドを呼び出してユニティちゃんをCommand状態に変更します。
CommandがアクティブでなければExitCommandメソッドを呼んで選択しているボタンを解除し、ユニティちゃんの状態をNormal状態に変更します。
現時点ではエラーが起きているので修正を加えていきます。
LoadSceneManagerとUnityChanScriptスクリプトの修正
UnityChanCommandScriptからユニティちゃんの状態を変更したりシーン遷移中かどうかを調べる必要が出たのでその処理を追加します。
UnityChanScriptスクリプトに処理を追加
UnityChanScriptに新しくCommand状態を作成します。
1 2 3 4 5 6 7 8 9 | public enum State { Normal, Talk, Command, Wait } |
次にUpdateメソッド内でCommandの時の処理を追加します。
UnityChanScript内では特に何もしないので空白のままです。
1 2 3 4 5 6 7 8 | } else if(state == State.Talk) { } else if(state == State.Command) { } else if(state == State.Wait) { |
状態変更メソッド内を変更します。
Command状態にする時は移動速度を0にするのとアニメーションパラメータのSpeedを0にしているだけです。
他の状態とやっていることが同じなのでまとめて処理をしたいところですが、今後処理を分ける可能性もあるのでとりあえずそのままにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 状態変更と初期設定 public void SetState(State state) { this.state = state; if(state == State.Talk) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); unityChanTalkScript.StartTalking(); } else if(state == State.Command) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); } else if (state == State.Wait) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); } } |
LoadSceneManagerスクリプトに処理を追加
LoadSceneManagerに処理を追加します。
まずはシーン遷移中かどうかを表すisTransitionフィールドを用意します。
1 2 3 4 | // シーン遷移中かどうか private bool isTransition; |
GoToNextSceneメソッドが呼び出されたらisTransitionにtrueを入れます。
1 2 3 4 5 6 7 8 | // 次のシーンを呼び出す public void GoToNextScene(SceneMovementData.SceneType scene) { isTransition = true; sceneMovementData.SetSceneType(scene); StartCoroutine(FadeAndLoadScene(scene)); } |
新しくIsTransitionメソッドを用意しisTransitonの値を確認出来るようにします。
1 2 3 4 5 | public bool IsTransition() { return isTransition; } |
エラーが消えたと思いますので、UnityChanゲームオブジェクトにUnityChanCommandScriptを取り付けます。
UnityChanゲームオブジェクトのインスペクタでUnityChanCommandScriptのCommandUIにCommandゲームオブジェクトをドラッグ&ドロップして設定しておきます。
コマンド処理をするCommandScriptの作成
次はUnityChanCommandScriptでコマンド画面を開いた後のコマンド処理を行うCommandScriptスクリプトを作成します。
処理が分かり辛いので細かく作成していきます。
フィールド宣言部
まずはフィールドの宣言部分です。
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 | using System.Collections; using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public class CommandScript : MonoBehaviour { public enum CommandMode { CommandPanel, StatusPanelSelectCharacter, StatusPanel, } private CommandMode currentCommand; // ユニティちゃんコマンドスクリプト private UnityChanCommandScript unityChanCommandScript; // 最初に選択するButtonのTransform private GameObject firstSelectButton; // コマンドパネル private GameObject commandPanel; // ステータス表示パネル private GameObject statusPanel; // キャラクター選択パネル private GameObject selectCharacterPanel; // コマンドパネルのCanvasGroup private CanvasGroup commandPanelCanvasGroup; // キャラクター選択パネルのCanvasGroup private CanvasGroup selectCharacterPanelCanvasGroup; // キャラクター名 private Text characterNameText; // ステータスタイトルテキスト private Text statusTitleText; // ステータスパラメータテキスト1 private Text statusParam1Text; // ステータスパラメータテキスト2 private Text statusParam2Text; // パーティーステータス [SerializeField] private PartyStatus partyStatus = null; // キャラクター選択のボタンのプレハブ [SerializeField] private GameObject characterPanelButtonPrefab = null; // 最後に選択していたゲームオブジェクトをスタック private Stack<GameObject> selectedGameObjectStack = new Stack<GameObject>(); } |
CommandModeは今現在コマンドがどの状態なのかを表す列挙型です。
CommandPanelはコマンドパネルを開いた状態、StatusPanelSelectCharacterはどのキャラクターのステータスを見るかの選択パネルを開いた状態、StatusPanelはキャラクターのステータスを表示している状態です。
currentModeはその状態を入れておくフィールドです。
firstSelectButtonはコマンド画面を開いた時に最初に選択状態にするボタンを設定します。
commandPanel、statusPanel、selectCharacterPanelは先ほど作ったパネルを設定します。
コマンドパネルとキャラクター選択パネルのCanvas Groupの保持し、状況に応じてinteractableの値を切り替えます。
characterNameTextはCharacterNamePanelの子要素のText、statusTitleTextはStatusParamPanelのTitle、statusParam1TextはParam1、statusParam2TextはParam2を設定します。
PartyStatus型のpartyStatusフィールドはパーティーに関するステータスを保持するアセットファイルを設定します。
これは後で作成します。
characterPanelButtonPrefabはSelectCharacterPanelの子要素に表示するボタンのプレハブでパーティーメンバー数分のインスタンスを生成することになります。
これも後で作成します。
Stack
StackはPushでデータを入れ、Popで取り出しますが、最後に入れたものからPopで取り出せるので最後に選択したゲームオブジェクトから取り出せます。
Stackについては
を参照してください。
今回はステータスコマンドしかないですが例えばアイテムコマンドを作成した場合に
アイテムコマンドの選択→どのキャラクターのアイテムを表示するかのパネル→ユニティちゃんの持っているアイテム表示パネル
と開いてCancelボタンを押して「どのキャラクターのアイテムを表示するかのパネル」に戻った時にユニティちゃんのボタンを選択した状態にする為です。
Awakeメソッド
次にAwakeメソッドを追記します。
Awakeメソッドはスクリプトが設定されたゲームオブジェクトがアクティブになった初めの1回だけ実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | void Awake() { // コマンド画面を開く処理をしているUnityChanCommandScriptを取得 unityChanCommandScript = GameObject.FindWithTag("Player").GetComponent<UnityChanCommandScript>(); // 現在のコマンドを初期化 currentCommand = CommandMode.CommandPanel; // 階層を辿ってを取得 firstSelectButton = transform.Find("CommandPanel/StatusButton").gameObject; // パネル系 commandPanel = transform.Find("CommandPanel").gameObject; statusPanel = transform.Find("StatusPanel").gameObject; selectCharacterPanel = transform.Find("SelectCharacterPanel").gameObject; // CanvasGroup commandPanelCanvasGroup = commandPanel.GetComponent<CanvasGroup>(); selectCharacterPanelCanvasGroup = selectCharacterPanel.GetComponent<CanvasGroup>(); // ステータス用テキスト characterNameText = statusPanel.transform.Find("CharacterNamePanel/Text").GetComponent<Text>(); statusTitleText = statusPanel.transform.Find("StatusParamPanel/Title").GetComponent<Text>(); statusParam1Text = statusPanel.transform.Find("StatusParamPanel/Param1").GetComponent<Text>(); statusParam2Text = statusPanel.transform.Find("StatusParamPanel/Param2").GetComponent<Text>(); } |
unityChanCommandScriptはGameObject.FindWithTagを使ってPlayerタグを設定したUnityChanを探し、そこから取得しています。
その他は現在のコマンドの設定や自身(Command)のから階層を辿ってゲームオブジェクト、CanvasGroup、Text等を取得する処理を記述しています。
OnEnableメソッド
OnEnableメソッドはゲームオブジェクトがアクティブになった時に実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private void OnEnable() { // 現在のコマンドの初期化 currentCommand = CommandMode.CommandPanel; // コマンドメニュー表示時に他のパネルは非表示にする statusPanel.SetActive(false); selectCharacterPanel.SetActive(false); // キャラクター選択ボタンがあれば全て削除 for (int i = selectCharacterPanel.transform.childCount - 1; i >= 0; i--) { Destroy(selectCharacterPanel.transform.GetChild(i).gameObject); } selectedGameObjectStack.Clear(); commandPanelCanvasGroup.interactable = true; selectCharacterPanelCanvasGroup.interactable = false; EventSystem.current.SetSelectedGameObject(firstSelectButton); } |
OnEnableが呼び出されるのはCommandゲームオブジェクトがアクティブになった時なので、コマンド画面を最初に開いた状態の時に実行されます。
なので初期化処理を実行しています。
SelectCharacterPanelでキャラクターを選択するボタンをプレハブからインスタンス化して表示しますが、Cancelボタンを押したり、コマンド画面自体を閉じた時にインスタンス化したボタンが残っていると困るので、SelectCharacterPanelの子要素がある場合は削除しています。
子要素の最初の要素から削除するとうまく処理が出来ません。
なぜなら0番目の要素を削除すると1番目の子要素が0番目になるからです。
なのでSelectCharacterPanelの最後の子要素から削除しています。
selectedGameObjectStackは次のパネルを開いた時に前のパネルで選択していたゲームオブジェクトを登録しているフィールドなので、新しくコマンド画面を開いた時はClearメソッドを使って中身を削除しています。
コマンド画面を開いた時はCommandPanelのCanvas GroupのinteractableをtrueにしCommand Panelだけを選択出来るようにします。
それ以外のCanvas Groupのinteractableはfalseにします。
EventSystem.current.SetSelectedGameObjectメソッドを使って最初に選択するゲームオブジェクトを設定しています。
Updateメソッド
Updateメソッドを追記します。
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 | private void Update() { // キャンセルボタンを押した時の処理 if (Input.GetButtonDown("Cancel")) { // コマンド選択画面時 if (currentCommand == CommandMode.CommandPanel) { unityChanCommandScript.ExitCommand(); gameObject.SetActive(false); // ステータスキャラクター選択またはステータス表示時 } else if (currentCommand == CommandMode.StatusPanelSelectCharacter || currentCommand == CommandMode.StatusPanel ) { selectCharacterPanelCanvasGroup.interactable = false; selectCharacterPanel.SetActive(false); statusPanel.SetActive(false); // キャラクター選択パネルの子要素のボタンを削除 for (int i = selectCharacterPanel.transform.childCount - 1; i >= 0; i--) { Destroy(selectCharacterPanel.transform.GetChild(i).gameObject); } // 前のパネルで選択していたゲームオブジェクトを選択 EventSystem.current.SetSelectedGameObject(selectedGameObjectStack.Pop()); commandPanelCanvasGroup.interactable = true; currentCommand = CommandMode.CommandPanel; } } } |
UpdateメソッドではCancelボタンを押した時の処理を記述しています。
キャンセルボタンを押した時に現在どのコマンドの状態なのか?を調べて処理を分岐させます。
CommandMode.CommandPanelの時はCommandPanelを開いたばかりの状態でCancelボタンを押したのでコマンド画面を閉じます。
コマンド画面を閉じる時はUnityChanCommandScriptのExitCommandメソッドを呼んでユニティちゃんの状態を変化させます。
CommandMode.StatusPanelSelectCharacterもしくはCommandMode.StatusPanelの時はCommandPanel状態に変化させる為の処理を記述しています。
行っていることは現在のパネルのCanvas Groupを無効にし、CommandPanelのCanvas Groupを有効にしたり、作成したキャラクター選択ボタンの削除をしています。
CommandPanelに戻る時はCommandPanelで最後に選択していたゲームオブジェクトがStackのselectedGameObjectStackに入っているのでPopしてそのゲームオブジェクトを選択状態にします。
SelectCommandメソッド
SelectCommandメソッドはStatusButtonを押した時に実行させるようにします(後で設定します)。
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 | // 選択したコマンドで処理分け public void SelectCommand(string command) { if (command == "Status") { currentCommand = CommandMode.StatusPanelSelectCharacter; // UIのオン・オフや選択アイコンの設定 commandPanelCanvasGroup.interactable = false; selectedGameObjectStack.Push(EventSystem.current.currentSelectedGameObject); GameObject characterButtonIns; // パーティーメンバー分のボタンを作成 foreach (var member in partyStatus.GetAllyStatus()) { characterButtonIns = Instantiate<GameObject>(characterPanelButtonPrefab, selectCharacterPanel.transform); characterButtonIns.GetComponentInChildren<Text>().text = member.GetCharacterName(); characterButtonIns.GetComponent<Button>().onClick.AddListener(() => ShowStatus(member)); } } // 階層を一番最後に並べ替え selectCharacterPanel.transform.SetAsLastSibling(); selectCharacterPanel.SetActive(true); selectCharacterPanelCanvasGroup.interactable = true; EventSystem.current.SetSelectedGameObject(selectCharacterPanel.transform.GetChild(0).gameObject); } |
引数で受け取った文字列がStatusだった時に現在のコマンドを変更し、現在選択しているゲームオブジェクトをselectedGameObjectStackにPushします。
その後partyStatus.GetAllyStatusメソッドでパーティーメンバー数分のボタンを作成しSelectCharacterPanelの子要素に配置します。
作成したボタンの子要素のTextを取得しそこにキャラクター名を入れます。
作成したボタンのonClickにイベントリスナーを取り付けボタンが押されたらShowStatusメソッドにキャラクターステータスを渡して呼び出します。
ここら辺は少し分かり辛いですが用はcharacterPanelButtonPrefabというボタンのプレハブをインスタンス化してヒエラルキー上に配置し、そのボタンが押された時に実行するメソッドを指定しているのが
1 2 3 | characterButtonIns.GetComponent<Button>().onClick.AddListener(() => ShowStatus(member)); |
の部分です。
引数はラムダ式で記述していますので、ラムダ式ってなんだ?と思った方は
を参照してみてください。
1 2 3 | selectCharacterPanel.transform.SetAsLastSibling(); |
上の部分でSelectCharacterPanelを階層の一番最後に並び替えています。
こうすることでSelectCharacterPanelと他のパネルが重なって見えづらくなっていてもSelectCharacterPanelが一番手前に表示され見えやすくなります。
キャラクター選択ボタンを作成したら子要素の0番目(一番最初の子)を選択状態にします。
ShowStatusメソッド
先ほどのcharacterPanelButtonPrefabから作成したインスタンスのボタンがクリックされた時に呼び出していたのがShowStatusメソッドです。
引数ではキャラクターのステータスを受け取っています。
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 | // キャラクターのステータス表示 public void ShowStatus(AllyStatus allyStatus) { currentCommand = CommandMode.StatusPanel; statusPanel.SetActive(true); // キャラクターの名前を表示 characterNameText.text = allyStatus.GetCharacterName(); // タイトルの表示 var text = "レベル\n"; text += "HP\n"; text += "MP\n"; text += "経験値\n"; text += "状態異常\n"; text += "力\n"; text += "素早さ\n"; text += "打たれ強さ\n"; text += "魔法力\n"; text += "装備武器\n"; text += "装備鎧\n"; text += "攻撃力\n"; text += "防御力\n"; statusTitleText.text = text; // HPとMPのDivision記号の表示 text = "\n"; text += allyStatus.GetHp() + "\n"; text += allyStatus.GetMp() + "\n"; statusParam1Text.text = text; // ステータスパラメータの表示 text = allyStatus.GetLevel() + "\n"; text += allyStatus.GetMaxHp() + "\n"; text += allyStatus.GetMaxMp() + "\n"; text += allyStatus.GetEarnedExperience() + "\n"; if (!allyStatus.IsPoisonState() && !allyStatus.IsNumbnessState()) { text += "正常"; } else { if (allyStatus.IsPoisonState()) { text += "毒"; if (allyStatus.IsNumbnessState()) { text += "、痺れ"; } } else { if (allyStatus.IsNumbnessState()) { text += "痺れ"; } } } text += "\n"; text += allyStatus.GetPower() + "\n"; text += allyStatus.GetAgility() + "\n"; text += allyStatus.GetStrikingStrength() + "\n"; text += allyStatus.GetMagicPower() + "\n"; text += allyStatus?.GetEquipWeapon()?.GetKanjiName() ?? ""; text += "\n"; text += allyStatus.GetEquipArmor()?.GetKanjiName() ?? ""; text += "\n"; text += allyStatus.GetPower() + (allyStatus.GetEquipWeapon()?.GetAmount() ?? 0) + "\n"; text += allyStatus.GetStrikingStrength() + (allyStatus.GetEquipArmor()?.GetAmount() ?? 0) + "\n"; statusParam2Text.text = text; } |
やっていることは単純で受け取ったAllyStatusのデータから該当するデータを取得し、それをStatusParamPanelの子要素のTitle、Param1、Param2に代入し表示しているだけです。
毒と痺れ等の状態異常は持っている状態異常を連結させて表示する為に条件分岐をしています。
装備している武器と装備している鎧はNullの可能性があるのでちょっと工夫をしています。
1 2 3 | allyStatus.GetEquipWeapon()?.GetAmount() ?? 0; |
上の部分ではまずallyStatus.GetEquipWeapon()で装備している武器を取得し、GetAmout()でその武器の強さを取得しています。
?.GetAmount()はNull条件演算子でNullではなければallyStatus.GetEquipWeapon().GetAmount()の値を取得し、NullだったらNullを返します。
その後の??はNull合体演算子でallyStatus.GetEquipWeapon()?.GetAmount()がNullだったら??の後の0の値を取得し、NullじゃなければallyStatus.GetEquipWeapon().GetAmount()の値を取得します。
結果としてallyStatus.GetEquipWeapon().GetAmount()がNullじゃなければallyStatus.GetEquipWeapon().GetAmount()、Nullだったら0を入れます。
ここら辺はちょっとわかりずらいので普通のif文を使った処理で書いても問題ありません。
1 2 3 4 5 6 7 | if(allyStatus.GetEquipWeapon() == null) { text += allyStatus.GetPower(); } else { text += allyStatus.GetPower() + allyStatus.GetEquipWeapon().GetAmount(); } |
のような感じです。
スクリプトを作成した時点ではPartyStatusを作っていないのでエラーが発生します。
パーティーステータスの作成
パーティーステータスはScriptableObjectクラスを継承してアセットファイルとして作成します。
作り方は前回の記事でやった事と同じなので同じように作成してみてください。
Assets/RPG/Scripts/Statusフォルダ内に新しくPartyStatusスクリプトを作成します。
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 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] [CreateAssetMenu(fileName = "PartyStatus", menuName = "CreatePartyStatus")] public class PartyStatus : ScriptableObject { [SerializeField] private int money = 0; [SerializeField] private List<AllyStatus> partyMembers = null; public void SetMoney(int money) { this.money = money; } public int GetMoney() { return money; } public void SetAllyStatus(AllyStatus allyStatus) { if (!partyMembers.Contains(allyStatus)) { partyMembers.Add(allyStatus); } } public List<AllyStatus> GetAllyStatus() { return partyMembers; } } |
パーティー全体のステータスなので、持っているお金とパーティーメンバーのステータスを保持するListを持たせています。
SetAllyStatusメソッドではパーティーメンバーに登録されていなければメンバーにキャラクターのステータスを登録します。
Assets/RPG/Data/Statusフォルダ内で右クリックからCreate→CreatePartyStatusを選択します。
作成したPartyStatusには前回作成したUnityChanStatusとYujiStatusのアセットファイルを追加します。
これでPartyStatusが出来ました。
CharacterButtonの作成
SelectCommandメソッド内でパーティーメンバー数分のボタンをプレハブからインスタンス化する処理を記述しましたが、そのプレハブをまだ作っていませんでした。
SelectCharacterPanelを選択した状態で右クリックからUI→Buttonを選択し、名前をCharacterButtonとします。
CharacterButtonのインスペクタでWidthを200、Heightを50にします。
Imageは使わないのでチェックを外すかコンポーネントを削除します。
Buttonのアニメーションは行わないのでTransitionはNoneにします。
CharacterButtonを選択した状態で右クリックからUI→Imageを選択します。
StatusButtonを作成した時と同じようにボタンが選択されたら表示するアイコンがImageになります。
CharacterButtonの子要素のTextのFont Sizeを25にし、Colorをしろいろ
Imageの位置を調整しボタンの左側に来るようにします。
上のような感じの位置にしました。
Assets/RPG/Scriptsフォルダ内に新しくCommandPanelButtonScriptスクリプトを作成し、CharacterButtonに取り付けます。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; public class CommandPanelButtonScript : MonoBehaviour, ISelectHandler, IDeselectHandler { // ボタンを選択した時に表示する画像 private Image selectedImage; // 選択した時の音のAudioSource private AudioSource audioSource; void Awake() { selectedImage = transform.Find("Image").GetComponent<Image>(); audioSource = GetComponent<AudioSource>(); } private void OnEnable() { // アクティブになった時自身がEventSystemで選択されていたら if (EventSystem.current.currentSelectedGameObject == this.gameObject) { selectedImage.enabled = true; audioSource.Play(); } else { selectedImage.enabled = false; } } // ボタンが選択された時に実行 public void OnSelect(BaseEventData eventData) { selectedImage.enabled = true; audioSource.Play(); } // ボタンが選択解除された時に実行 public void OnDeselect(BaseEventData eventData) { selectedImage.enabled = false; } } |
ISelectHandlerとIDeselectHandlerインターフェースを継承して作成します。
これらはOnSelectメソッドとOnDeselectメソッドを実装する必要があります。
OnSelectメソッドはボタンが選択された時に呼び出され、OnDeselectメソッドはボタンの選択が解除された時に呼び出されます。
このスクリプトが行っていることは単純で、CharacterButtonボタンの子要素のImageゲームオブジェクトのImageコンポーネントをselectedImageに保持し、ボタンが選択された時はそれを表示し、選択解除された時に非表示にしているだけです。
Awakeメソッドではコンポーネントの取得を行っています。
OnEnableメソッドではボタンが表示された時にEventSystemで現在選択されているのが自身だったらアイコンの表示、そうじゃない場合は非表示にしています。
Awakeメソッドで似たような処理を記述していましたが、なぜかWorldMapシーンだとうまくいかない為OnEnableメソッドで処理をするようにしました。
またCharacterButtonのインスペクタのAdd ComponentからAudio→Audio Sourceを取り付けます。
アセットストアで「Minimal UI Sounds」で検索しボタン選択時の効果音をインポートします。
CharacterButtonのAudio SourceのAudio ClipにAssets/Minimal UI SoundsのClack_Minimal UI Soundsを設定します。
Play On Awakeのチェックを外し自動で音声を再生しないようにします。
CharacterButtonは以下のようになりました。
ButtonのOn Clickには何も設定していませんが、スクリプトからOn ClickにShowStatusメソッドを実行するように設定したので問題はありません。
ここまで出来たらAssets/RPG/Prefabs/UI/Commandフォルダ内にCharacterButtonゲームオブジェクトをドラッグ&ドロップしプレハブにします。
ヒエラルキー上のCharacterButtonはいらないので削除してください。
CommandのCommandScriptの設定
CommandゲームオブジェクトにCommandScriptを取り付けます。
CommandのCommandScriptのインスペクタでpartyStatusとcharacterPanelButtonPrefabを設定します。
partyStatusはAssets/RPG/Data/Status/PartyStatusを設定し、characterPanelButtonPrefabにはAssets/RPG/Prefabs/UI/Command/CharacterButtonを設定します。
StatusButtonを押した時に呼び出すメソッドの設定
StatusButtonを押した時に初めてキャラクターの選択パネルを開きます。
つまりStatusButtonを押したらCommandScriptスクリプトのSelectCommandメソッドに実行するコマンドの文字列を渡して実行する必要があります。
StatusButtonを選択し、インスペクタのButtonのOn ClickでCommandをドラッグ&ドロップして実行するメソッドにSelectCommandを指定し、文字列にStatusと入力します。
StatusButtonを選択した時や選択を解除した時もCharacterButtonと同じようにアイコンに表示や非表示を行いたいので、StatusButtonにもCommandPanelButtonScriptを取り付けておきます。
ボタンを選択した時に音を鳴らしたいのでCharacterButtonと同じようにAudio Sourceを取り付けAudio ClipにAssets/Minimal UI Sounds/Clack_Minimal UI Soundsを設定します。
Audio SourceのPlay On Awakeのチェックを外し最初から音が鳴らないようにします。
CancelボタンとMenuボタンの設定変更
コマンドを表示するボタンにMenuを指定し、元のパネルに戻る時はCancelボタンを指定しました。
またボタンを押す時はSubmitボタンになるのでSubmitボタンも変更します。
改めてUnityメニューのEdit→Project Settings→Inputでボタンの設定を行います。
Submitボタンの設定にActionボタンと同じPS3の〇ボタンを押した時と同じ設定を追加します(使用するコントローラーによって変わります)。
Actionボタンはjoystick button 1を設定しているので、SubmitのAlt Positiveにも同じ設定をします。
Alt PositiveはPositiveの二つ目の設定です。
次はCancelボタンです。
PS3の×ボタンを押した時の設定を追加します。×ボタンはjoystick button 2になります。
次はMenuボタンです。
Menuボタンは以前作成しましたが、キーボードとマウスで操作する場合のキーボードの設定をしていなかったので、設定を追加します。
Alt Positiveに1を入力し、キーボードの1キーが押されたらコマンド画面を開くようにします。
コマンド画面の初期状態の設定
コマンド画面のUIが少し複雑になりましたが、ここでコマンド画面のアクティブと非アクティブの設定をしておきます。
Command、SelectCharacterPanel、StatusPanelはインスペクタで名前の横のチェックを外し非アクティブにしておきます。
CommandPanelはインスペクタでチェックし、アクティブな状態にしておきます。
コマンド画面の確認
コマンド画面とステータスの表示機能が出来たので確認してみましょう。
上のようになりました。
終わりに
今回はCommandをVillageシーンにしか配置していないのでシーンを遷移するとコマンド画面が開きません。
WorldMapシーンのUnityChanにUnityChanCommandScriptを取り付け、VillageシーンのCommandを複製してWorldMapシーンに移動させた後にUnityChanCommandScriptのcommandUIをWorldMapのCommandに変更します。
そうすればWorldMapシーンでも同じようにコマンド画面を開くことが出来ます。
ただ・・・この後アイテムコマンド等も追加していくのでそれが作り終わったらWorldMapシーンでもコマンド機能が使えるようにしていきます。
この作品はユニティちゃんライセンス条項の元に提供されています