ドラッグアンドドロップで、インベントリのアイテムを合成して新しいアイテムを作るときや、インベントリのアイテムを消すときの処理を、今まではタグで場合分けしていましたが、UIに付けたスクリプトに書くようにしてみました。
まずドロップできるUIのクラスが継承するインターフェースを定義しました。とりあえず、メニュー管理オブジェクトを引数に渡して、ドロップ先では、管理オブジェクトのメソッドを呼ぶようにしました。
interface IDroppable
{
void Drop(DisplayMenuOperation disp);
}
例えばゴミ箱にスクリプトを付けて、このインターフェースを継承させ、ドロップのメソッドには、今までメニュー管理クラスにあった処理を書きます。
using UnityEngine;
public class DustBox : MonoBehaviour, IDroppable
{
public void Drop(DisplayMenuOperation disp)
{
// 掴んだアイテムを隠す
disp.HideDraggedItem();
// ダイアログを出す
Notification.GetInstance().ShowDialog("アイテムを捨てますか?", "はい", "いいえ",
// 「はい」のとき
() =>
{
// 掴んだアイテムを捨てる
disp.RemoveDraggedItem();
// ダイアログを消す
Notification.GetInstance().DestroyDialog2();
},
// 「いいえ」のとき
() =>
{
// 掴んだアイテム画像をもとに戻す
disp.DropFail();
// ダイアログを消す
Notification.GetInstance().DestroyDialog2();
});
}
}
掴んだアイテムを削除したり、元に戻すときに、管理クラスのメソッドが呼ばれています。
// メニュー管理クラス
Goods currentImage; // 掴んだアイテム画像のスクリプト
Transform previousParent; // 掴む前のアイテム画像の親オブジェクト
Transform currentParent; // 掴んでいるときのアイテム画像の親オブジェクト
int index; // 掴む前のインベントリでの表示順
[SerializeField] Image emptyImage; // 掴んでいる時に置く透明画像
// 掴んだアイテムを非アクティブにする
public void HideDraggedItem()
{
currentImage.gameObject.SetActive(false);
}
// 掴んだアイテム画像とアイテムを削除
public void RemoveDraggedItem()
{
ThrowAway(currentImage);
}
// 捨てる
public void ThrowAway(Goods goods)
{
// インベントリのアイテムを破壊
Inventory.GetInstance().DestroyGoods(goods);
}
// 元に戻す
public void DropFail()
{
// 空のとき終了
if (currentImage == null) return;
// アイテム画像をアクティブにする
currentImage.gameObject.SetActive(true);
// 元の親の子に戻す
currentImage.transform.parent = previousParent;
// レイに当たるようにする
currentImage.SetRaycastTarget(true);
// 順番をもとに戻す
currentImage.transform.SetSiblingIndex(index);
// 空にする
currentImage = null;
}
それによって、管理クラスではタグで場合分けして処理を書く必要がなくなりました。ドラッグ中にクリックを放したら、インターフェース名でスクリプトを取得して、取得できたらドロップのメソッドを呼ぶだけです。
else if (Input.GetMouseButtonUp(0) && isDragging)
{
// 掴んでいない状態にする
isDragging = false;
// スクリプトを取得
IDroppable droppable = target.gameObject.GetComponent<IDroppable>();
// 取得できたとき
if (droppable != null)
{
droppable.Drop(this);
}
//できなかった時
else
{
// もとに戻す
DropFail();
}
// 透明画像を非表示にする
emptyImage.gameObject.SetActive(false);
}
アイテム画像の上にドロップして、アイテムを合成したり、順番を入れ替えるときのために、アイテム画像のクラスでも上のインターフェースを継承して、ドロップしたときの処理を書いています。
// アイテム画像のクラス
using UnityEngine;
using UnityEngine.UI;
public class Goods :MonoBehaviour, IDroppable
{
// ...
string itemName;
public string GetItemName()
{
return itemName;
}
// ...
public void Drop(DisplayMenuOperation disp)
{
// 合体可能なとき
if (ItemManager.GetInstance().GetItem(GetItemName()).GetIsCombinable())
{
Item item = ItemManager.GetInstance().GetItem(GetItemName()).GetCombinableInto(disp.GetDraggedItemName());
// 見つからない時
if (item == null)
{
disp.SwapIndex(this);
}
else
{
// 取得するための情報とメソッドを含むオブジェクトを作る
Obtainable obt = new Obtainable(item.GetItemName());
// 取得メソッドを呼ぶと、合体してできるアイテムのインスタンスがインベントリに入る
obt.Obtain(Instantiate(item.GetPrefab(), transform.position, Quaternion.identity), transform.GetSiblingIndex());
// 掴んでいたアイテムを削除
disp.RemoveDraggedItem();
// ドロップ先のアイテムを削除
disp.ThrowAway(this);
}
}
// 合体可能でないとき
else
{
disp.SwapIndex(this);
}
}
}
このときも管理クラスのメソッドを使っています。
Goods currentImage; // 掴んだアイテム画像のスクリプト
Transform previousParent; // 掴む前のアイテム画像の親オブジェクト
Transform currentParent; // 掴んでいるときのアイテム画像の親オブジェクト
int index; // 掴む前のインベントリでの表示順
[SerializeField] Image emptyImage; // 掴んでいる時に置く透明画像
// 掴んだアイテムとドロップ先のアイテムの画像の順番を入れ替える
public void SwapIndex(Goods goods)
{
// 元の親の子に戻す
currentImage.transform.parent = previousParent;
// レイに当たるようにする
currentImage.SetRaycastTarget(true);
// 順番を入れ替える
currentImage.transform.SetSiblingIndex(goods.transform.GetSiblingIndex());
goods.transform.SetSiblingIndex(index);
// 空にする
currentImage = null;
// 透明画像を非アクティブにする
emptyImage.gameObject.SetActive(false);
}
// 掴んだアイテムの名前
public string GetDraggedItemName()
{
return currentImage.GetItemName();
}
// 掴んだアイテム画像とアイテムを削除
public void RemoveDraggedItem()
{
ThrowAway(currentImage);
}
// 捨てる
public void ThrowAway(Goods goods)
{
// インベントリのアイテムを破壊
Inventory.GetInstance().DestroyGoods(goods);
}
アイテム画像はタグで見分けているので、とりあえずドロップのメソッドの呼び出しは二箇所に書いています。
foreach (RaycastResult target in results)
{
// アイテム画像の時
if (target.gameObject.tag == "ItemImage")
{
Goods goods = target.gameObject.GetComponent<Goods>();
// スクリプトがあれば
if (goods != null)
{
// クリック
if (Input.GetMouseButtonDown(0))
{
// 掴んでいる状態にする
isDragging = true;
// 掴むときの処理...
}
// アイテム画像の上にドロップしたとき
else if (Input.GetMouseButtonUp(0) && isDragging)
{
// 掴んでいない状態にする
isDragging = false;
// スクリプトを取得
IDroppable droppable = target.gameObject.GetComponent<IDroppable>();
// 取得できたとき
if (droppable != null)
{
droppable.Drop(this);
}
//できなかった時
else
{
// もとに戻す
DropFail();
}
// 透明画像を非表示にする
emptyImage.gameObject.SetActive(false);
}
}
}
// それ以外のターゲットのとき
else
{
if (Input.GetMouseButtonUp(0) && isDragging)
{
// 掴んでいない状態にする
isDragging = false;
// スクリプトを取得
IDroppable droppable = target.gameObject.GetComponent<IDroppable>();
// 取得できたとき
if (droppable != null)
{
droppable.Drop(this);
}
//できなかった時
else
{
// もとに戻す
DropFail();
}
// 透明画像を非表示にする
emptyImage.gameObject.SetActive(false);
}
}
}
掴んでいる状態を変えたり、透明画像を非表示にする処理を書く場所を変えたので、メニューを閉じたり、何も無いところをクリックしたときに、もとに戻すメソッドと並べて書いています。
// メニューを閉じたときや、何も無いところで放したとき
// もとに戻す
DropFail();
// 透明画像を非表示にする
emptyImage.gameObject.SetActive(false);
// 掴んでいない状態にする
isDragging = false;
これで、ドロップしたときの処理を増やしやすくなったと思います。