今回はゲームシーンに装備スロットを表示し、対応するキーを押した時にその武器へ切り替える機能を作成していきます。
前回までに作成した持ち物画面と
持ち物画面で装備スロットに武器をセットする機能
を作成している必要があります。
今回の機能を作成すると
↑のような感じで持ち物画面で設定した装備スロットの武器を対応する番号のキーを押す事で切り替える事が出来るようになります。
キーの2を押せば左から2つ目の装備スロットを装備した事になっています。
3つの記事に渡って機能を追加してきたので、ちょっと処理が解り辛いかもしれません。(^_^;)
ゲームシーンに装備スロット表示パネルを作成
まずはゲームシーンに装備スロットのパネルを作成します。
以前作成した弾の弾数を表示するStatusUIの子要素に新しくPanelを作成し、名前をEquipPanelとします。
EquipPanelにはAdd ComponentからLayout→Horizontal Layout Groupを取り付けます。
新しくCanvasを作ってその子要素に作成してもかまいません。
↑のようにEquipPanelの子要素にさらにPanelを4つ作成し、Equipと名前の後に1~4の番号を付けます。
Equip1~4のImageのColorのAlphaを100にします。
さらにそれぞれの子要素にImageを作成します。
それぞれのImageゲームオブジェクトのImageコンポーネントでSourceにスタンダードアセットのbackgroundを設定し、ColorのAlphaを100にします。
子要素のImageにはアイテムに対応するスプライトをスクリプトから設定します。
↑のような装備スロットを表示するUIが完成しました。
持ち物画面の装備スロットをゲームのステータス画面の装備スロットに反映させる
持ち物画面の武器を、持ち物画面の装備スロットにドロップした時に持ち物画面の装備スロットにアイテムを設定していますが、その時に先ほど作成したEquipPanelの子要素の装備スロットにも反映するようにします。
MyStatusスクリプトに処理を追加
MyStatusスクリプトに処理を追加し、装備画面の装備スロットにアイテムがセットされたら、EquipPanelの子要素にアイテムをセットするようにします。
MyStatusスクリプトに以下の処理を追加します。
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 | // 装備スロットに装備している武器 [SerializeField] private ItemData[] equipSlotDatas = new ItemData[4]; // ゲーム画面の装備スロットの親パネル [SerializeField] private Transform equipPanel; // 現在選択しているスロット番号 private int selectedSlotNum = -1; // アイテムスロットにアイテムデータをセット public void SetItemData(ItemData itemData, int slotNum) { equipSlotDatas[slotNum] = itemData; equipPanel.GetChild(slotNum).GetChild(0).GetComponent<Image>().sprite = itemData.GetItemSprite(); } // 装備スロットの武器情報取得メソッド public ItemData GetEquipSlotData(int num) { return equipSlotDatas[num]; } // 選択しているスロット番号を設定 public void SetSelectedSlotNum(int slotNum) { selectedSlotNum = slotNum; } // 現在選択しているスロット番号 public int GetSelectedSlotNum() { return selectedSlotNum; } |
装備スロットに何がセットされているかをequipSlotDatasで保持します。
equipPanelフィールドにはインスペクタでEquipPanelゲームオブジェクトを設定します。
SetItemDataメソッドは指定したスロット番号にItemDataを設定します。
EquipPanel→Equip番号→Imageを取得する為にequipPanelの子要素のImageにItemDataから取得したスプライトを設定します。
GetEquipSlotDataメソッドは引数としてスロット番号を受け取りその番号のスロットに設定されているItemDataを返します。
SetSelectedSlotNumメソッドは現在選択しているスロット番号を設定します。
GetSelectedSlotNumメソッドは現在選択しているスロット番号を返します。
ProcessingEquipSlotに処理を追加
ProcessingEquipSlotが装備画面の装備スロットにアイテムをドロップした時の処理を行っているので、そこでMyStatusのSetItemDataメソッドを呼び出すようにします。
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 | [SerializeField] private MyStatus myStatus; // 装備スロットの番号 [SerializeField] private int slotNum; // 装備切り替え処理スクリプト [SerializeField] private ChangeEquip changeEquip; // スロットの上にアイテムがドロップされた時に実行 public void MouseDrop() { if (FindObjectOfType<DragItemData>() == null) { return; } // DragItemUIに設定しているDragItemDataスクリプトからアイテムデータを取得 var dragItemData = FindObjectOfType<DragItemData>(); myItemData = dragItemData.GetItem(); // ドラッグしているアイテムデータの削除 dragItemData.DeleteDragItem(); // MyStatusスクリプトの装備スロットにアイテム情報を設定 myStatus.SetItemData(myItemData, slotNum); // 持ち物画面の装備スロットに武器をドロップした時、ゲーム画面でスロットを選択していたら武器を変更 if (myStatus.GetSelectedSlotNum() == slotNum) { changeEquip.InstantiateWeapon(slotNum); } ShowInformation(); } |
myStatusは主人公に設定しているMyStatusをインスペクタで設定します。
ProcessingEquipSlotは装備画面のEquip子要素のEquipSlot1~4にそれぞれ設定されているので、インスペクタでslotNumに0~3の値をEquipSlotの後の番号の順に設定します。
myStatusのSetItemDataを呼び出す時に自身のスロットのItemDataとインスペクタで設定したslotNumを渡して呼び出します。
こうすることでゲームのステータス画面の装備スロットの番号と合わせる事が出来ます。
注意するべき点として装備スロット番号は1~4なのに対してProcessingEquipSlotのslotNumは0~3の番号が対応する点と、
ヒエラルキーのEquipSlotの番号の順番が昇順になっている必要がある点です。
またすでに装備している武器があって、持ち物画面で装備している武器のスロットのところに武器をドロップした時は装備を切り替える必要があります。
そこで自身のスロット番号と主人公のステータス管理スクリプトのMyStatusの現在選択しているスロットが一致した場合は今ドロップした武器をインスタンス化する処理を呼び出します。
装備切り替えスクリプトChangeEquipの修正
以前は1のキーを押した時に順々に装備品を変更していましたが、
装備の切り替えを1~4のキーが押された時にそのスロットに設定されている装備に変更するようにします。
装備切り替え機能はChangeEquipスクリプトなので、処理を修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class ChangeEquip : MonoBehaviour { [SerializeField] private GameObject[] weapons; private int equipment; private ProcessCharaAnimEvent processCharaAnimEvent; private CharacterScript characterScript; // キャラクターのステータススクリプト [SerializeField] private MyStatus myStatus; // 武器の親のTransform [SerializeField] private Transform equipTransform; private Animator animator; private Shot shot; // ゲーム画面の装備スロットの親 [SerializeField] private Transform equipPanel; // Use this for initialization void Start() { characterScript = GetComponentInParent<CharacterScript>(); processCharaAnimEvent = transform.root.GetComponent<ProcessCharaAnimEvent>(); animator = GetComponent<Animator>(); // 初期装備設定 equipment = -1; shot = GetComponent<Shot>(); } // Update is called once per frame void Update() { if (characterScript.GetState() == CharacterScript.MyState.Normal) { if (Input.GetKeyDown("1")) { InstantiateWeapon(0); } else if (Input.GetKeyDown("2")) { InstantiateWeapon(1); } else if (Input.GetKeyDown("3")) { InstantiateWeapon(2); } else if (Input.GetKeyDown("4")) { InstantiateWeapon(3); } else if (Input.GetKeyDown("5")) { // 今装備している武器を削除 if (equipTransform.childCount != 0) { Destroy(equipTransform.GetChild(0).gameObject); } // 装備スロットで装備している武器の背景を変える for (int i = 0; i < equipPanel.childCount; i++) { equipPanel.GetChild(i).GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.5f); } myStatus.SetSelectedSlotNum(-1); } } } public void InstantiateWeapon(int slotNum) { equipment = slotNum; // 今装備している武器を削除 if (equipTransform.childCount != 0) { Destroy(equipTransform.GetChild(0).gameObject); } // 装備スロットで装備している武器の背景を変える for (int i = 0; i < equipPanel.childCount; i++) { if (i == slotNum) { equipPanel.GetChild(i).GetComponent<Image>().color = new Color(1f, 0f, 0f, 0.5f); } else { equipPanel.GetChild(i).GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.5f); } } // 現在選択しているスロット番号をセット myStatus.SetSelectedSlotNum(slotNum); // MyStatusスクリプトからItemDataを取得 var equipSlotData = myStatus.GetEquipSlotData(slotNum); // 装備スロットに装備が設定されていなければ以降処理をしない if (equipSlotData == null) { return; } // 素手ではない時だけ武器をインスタンス化 if (equipment != -1) { // プレハブの名前 string prefabName = ""; // 面倒くさいけど装備スロットのItemDataの名前からプレハブの名前を設定 if (equipSlotData.GetItemName() == "FlashLight") { prefabName = "FlashLight"; } else if (equipSlotData.GetItemName() == "HandGun") { prefabName = "HandGun"; } else if (equipSlotData.GetItemName() == "BroadSword") { prefabName = "Broadsword"; } GameObject weapon = null; // プレハブ名とweapons配列に設定したゲームオブジェクトの名前で一致した物をインスタンス化 foreach (var item in weapons) { if (item.name == prefabName) { // 新しく装備する武器をインスタンス化 weapon = Instantiate<GameObject>(item); weapon.name = item.name; processCharaAnimEvent.SetCollider(weapon.GetComponent<Collider>()); break; } } // 武器が存在しない場合はこれ以降の処理をしない if(weapon == null) { return; } var weaponStatus = weapon.GetComponent<WeaponStatus>(); // 武器の位置や角度を設定 weapon.transform.SetParent(equipTransform); weapon.transform.localPosition = weaponStatus.GetPos(); weapon.transform.localEulerAngles = weaponStatus.GetRot(); weapon.transform.localScale = weaponStatus.GetScale(); if (weapon.CompareTag("Sword")) { animator.SetBool("HaveSword", true); } else { animator.SetBool("HaveSword", false); } myStatus.SetEquip(weapon); // 武器を変更し、銃だったら情報をセット if (weaponStatus.GetWeaponType() == WeaponStatus.WeaponType.HandGun) { shot.InitializeGun(weapon); } } else { animator.SetBool("HaveSword", false); } } } |
Updateメソッドで1~4のキーを押したらInstantiateWeaponに対応する数字を渡して呼び出します。
5のキーを押したら装備を解除します。
InstantiateWeaponではint型の数字を受け取ります。
装備している武器のスロットの背景を赤に、その他を白に設定します。
MyStatusスクリプトから装備スロットのItemDataを取得し、装備スロットに武器が設定されていなければ以降の処理をしません。
ItemDataとゲームオブジェクトの関連付けはアイテムの名前から武器のプレハブの名前を作成し、その名前と一致するweaponsに指定した武器のプレハブを探し、それをインスタンス化する事にします。
これで機能が完成しました。
DestroyImmediateについて
以前はDestroyImmediateをつかってゲームオブジェクトを削除してましたが、通常はDestroyメソッドを使った方がいいようです。
DestroyImmediateだと第2引数にtrueを設定した場合UnityEditor上からアセット自体を削除してしまう可能性があるからかもしれません。
なのでDestroyで問題が出た時だけDestroyImmediateを使った方がいいかもしれません。
UnityのスクリプトリファレンスではDestroyImmediateの使いどころとしては
となっています。
以前はDestroyImmediateを使っていましたが、現在はDestoryにしています。