概要
半月前に構想した内容がこちら
simplestar-tech.hatenablog.com
前々回、前回で進んだ内容が以下
1.R Ctrl でキューブ、SideA, SideB キューブと配置形状が切り替わる
2.オンラインのインベントリ情報を受け取って表示する機能
今回は 3/7 ステップとして
3.マウス操作でインベントリのアイテムを移動できる機能
を作ります。
構想した手順
スロットをクリックして
アイテムがあればアイテムをホールド、なければ何も起きない
クリックを取得
まずは、スロット側にクリックイベントで何か関数呼び出しできるようにしてみます
using UnityEngine; using UnityEngine.UI; public class ItemSlot : MonoBehaviour { void Start() { this.button = GetComponent<Button>(); this.button.onClick.AddListener(this.OnClick); } public void OnClick() { Debug.Log("clicked " + this.gameObject.name); } Button button; }
Image UI に Button コンポーネントをつける必要あり
ところでキューブが配置されても、呼ばれる?→はい、大丈夫
ホールドとは
クリックしたスロットにアイテムがあるか見て
ある場合は、そのアイテムをホールド
特別な入れ物にスロットのアイテムを全部入れて、元のスロットを空にする
実装のイメージがわいたのですが
slot の uGUI の slot に Button コンポーネントを静的にアタッチしておき、名前で解決している部分で、OnClickイベントハンドラをセットします。
そうすれば、すべてのスロットのクリックを取得することができ、スロットが空なのかどうか判定できる要素にアクセスできます。
以下が、具現化したコード 期待通り動きました。
// Start is called before the first frame update void Start() { // パネルに並ぶslot名からimage配列を作成 foreach (Transform slotImage in this.panelInventory) { var match = Regex.Match(slotImage.name, "slot([0-9]{2})"); if(match.Success && 2 == match.Groups.Count) { if(int.TryParse(match.Groups[1].Value, out int slotIndex)) { this.slotTransforms[slotIndex] = slotImage; // クリックイベントハンドラを登録 var button = slotImage.GetComponent<Button>(); if (null != button) { button.onClick.AddListener(() => { this.OnClickSlot(slotIndex); }); } } } } } private void OnClickSlot(int slotIndex) { Debug.Log("clicked " + slotIndex); }
スロットが空とは?
null でしょうか? → はい
このように見分けます。
private void OnClickSlot(int slotIndex) { Debug.Log("clicked " + slotIndex); var viewItem = this.viewItems[slotIndex]; if (null != viewItem && 0 < viewItem.count) { Debug.Log("itemName " + viewItem.item.DisplayName); } }
ホルダー
ホールドするときは特別なホルダーと呼ばれる ItemView 保管庫に ItemView が格納されます
それだけ
アイテムがあるスロットをクリックしたときに発生し
ホルダーにアイテムがあるときは、交換するようにホールドします
ホルダーは具体的には ItemView クラス変数
nullable です
ビューを更新するには?
render 関数を作って、データに更新があったら render するというのを考えたのですが、それだとキューブメッシュ作成が 81 回も行われてしまうので
変化が生まれるクリックしたスロットにのみ、キューブオブジェクトを除去したり、生成して設置したりすることにします。
デバッグしていて思ったことは、サーバーから情報を取得したときにアイテムslotの状況が残ってしまい、図のようにローカルでアイテムが複製されてしまいました。
サーバーから情報を引いてくるときにクリアします。
ホールド中はアイテムがカーソルに付随
ホールドしたアイテムが null じゃないなら
こんな感じのコードでマウスカーソルについてくる形です。
private void OnClickSlot(int slotIndex) { Debug.Log("clicked " + slotIndex); var item = this.items[slotIndex]; this.items[slotIndex] = this.itemHolder; this.itemHolder = item; // スロットの CubeObject を再作成 Destroy(this.slotCubes[slotIndex]); var cubeObject = this.CreateSlotCubeObject(this.items[slotIndex]); if (null != cubeObject) { cubeObject.transform.SetParent(this.slotTransforms[slotIndex]); cubeObject.transform.localPosition = Vector3.zero; cubeObject.transform.localScale = new Vector3(50, 50, 1); } this.slotCubes[slotIndex] = cubeObject; Destroy(this.holderCube); this.holderCube = CreateSlotCubeObject(this.itemHolder); if (null != this.holderCube) { this.holderCube.transform.SetParent(this.panelInventory); this.holderCube.transform.localScale = new Vector3(25, 25, 1); } } void OnGUI() { Event currentEvent = Event.current; Vector2 mousePos = new Vector2(); // Get the mouse position from Event. // Note that the y position from Event is inverted. mousePos.x = currentEvent.mousePosition.x; mousePos.y = cam.pixelHeight - currentEvent.mousePosition.y; var point = cam.ScreenToWorldPoint(new Vector3(mousePos.x + 14, mousePos.y + 14, cam.nearClipPlane + 0.008f)); if (null != this.holderCube) { this.holderCube.transform.position = point; } }
まとめ
アイテムビューを開いたとき、マウスカーソルが出てくるモードがあるので、そのときにクリックして
アイテムがあればアイテムをホールド、なければ何も起きない
ホールドしていた状態でクリックしたとき、スロットだったらスロットに配置する
スロットでなければ…の一歩手前まで動作確認できました。
3.マウス操作でインベントリのアイテムを移動できる機能
が動きました。
これで 3/7 の進捗ですね。
残りも頑張ります