c# - 物品清单、库存系统、堆叠问题
问题描述
我正在统一创建一个简单的 RPG 3D 游戏。我几乎是编码的初学者。我创建了一个名为 Item 的 ScriptableObject 类,这样我就可以简单地创建一个新项目并从 Inspector 传递它们的属性。然后我创建了列表来将项目存储在角色库存中。我已经设法制作了一个代码,一旦它们被拾起,就会将它们添加到列表中。下一步是一个物品栏用户界面——我创建了 60 个类似于物品栏中的插槽的按钮,以及一个在按下它们后会显示的弹出菜单。该菜单包含两个按钮 - 使用和删除。打开菜单后,我正在创建使用和删除按钮的侦听器,如果按下其中任何一个,我将调用方法使用或删除。我将有关所选库存槽的信息存储在 Item 类的对象中。
现在的问题是 - 每当我多次单击库存中的不同物品时,即使我不断尝试将所选对象重置为空,以某种方式获取有关所选物品堆栈的信息。一旦我按下使用或删除 - 我点击的每个项目都会受到影响。
Unity 控制台向我显示(通过 Debug.Log)我已经输入了一个带有一个对象集的方法,但是当该方法更进一步时 - 它会为之前单击的每个项目执行多次。我很困惑。
ScriptableObject 项的代码
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{
new public string name = "New Item";
public Sprite icon = null;
public Image itemImage;
public bool isDefaultItem = false;
[TextArea(3, 10)]
public string ItemInfo;
public GameObject Prefab;
public Animator chestObtain;
public virtual void Use()
{
// Use the item
Debug.Log("Using " + name);
}
}
库存代码(物品存储)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public int space = 30;
#region Singleton
public delegate void OnItemChanged();
public OnItemChanged onItemChangedCallback;
public static Inventory instance;
private void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one instance of Inventory found!");
instance = this;
}
}
#endregion
public List<Item> items = new List<Item>();
public bool Add (Item item)
{
if (!item.isDefaultItem)
{
if (items.Count >= space)
{
Debug.Log("Not enough space");
return false;
}
items.Add(item);
if (onItemChangedCallback != null)
{
onItemChangedCallback.Invoke();
}
}
return true;
}
public void Remove(Item item)
{
Debug.Log("Removing " + item);
items.Remove(item);
if (onItemChangedCallback != null)
{
onItemChangedCallback.Invoke();
}
}
public void RemoveAt(int i)
{
if (i <= items.Count)
{
Debug.Log("Removing " + items[i]);
Debug.Log("List: " + items);
//items.RemoveAt(i);
if (onItemChangedCallback != null)
{
onItemChangedCallback.Invoke();
}
}
}
}
库存方法代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class InventoryItemInfo : MonoBehaviour
{
public GameObject ItemInfoTab;
public bool ItemInfoTabIsOpen = false;
public Button OpenTab;
public Text Title;
public Text UseButtonText;
public Text RemoveButtonText;
public Text ItemInfo;
public int SlotID;
public Image ItemImage;
public Item test;
public Button RemoveButton;
public Button UseButton;
Inventory inventory;
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
ItemInfoTabIsOpen = false;
}
}
public void OpenTabPressed()
{
Debug.Log("test object after opening a tab: " + test);
test = null;
Debug.Log("test object after opening a tab and reseting: " + test);
UseButton.onClick.RemoveListener(OnUseButton);
RemoveButton.onClick.RemoveListener(OnRemoveButton);
if (ItemInfoTab.activeInHierarchy)
{
ItemInfoTabIsOpen = false;
}
int itemsCount = GameObject.Find("GameManager").GetComponent<Inventory>().items.Count;
if (test == null)
{
if (itemsCount > SlotID)
{
if (ItemInfoTabIsOpen == false)
{
ItemInfoTab.SetActive(true);
ItemInfoTabIsOpen = true;
}
test = GameObject.Find("GameManager").GetComponent<Inventory>().items[SlotID];
UseButton.onClick.AddListener(OnUseButton);
RemoveButton.onClick.AddListener(OnRemoveButton);
Debug.Log("SlotID: " + SlotID);
Debug.Log("Clicked item: " + test);
Title.text = test.name;
ItemInfo.text = test.ItemInfo;
ItemImage.sprite = test.icon;
}
else
{
Debug.Log("Emtpy inventory slot");
}
}
else
{
test = null;
Debug.Log("Test object set to null");
}
}
public void OnUseButton()
{
Debug.Log("test object to use: " + test);
test.Use();
test = null;
Debug.Log("test object after using: " + test);
}
public void OnRemoveButton()
{
ItemInfoTab.SetActive(false);
ItemInfoTabIsOpen = false;
Debug.Log("test object to remove: " + test);
FindObjectOfType<Inventory>().Remove(test);
test = null;
}
}
最后一点 - 我已经把所有这些"test = null"; 试图克服这个问题,但它没有奏效。
编辑
我刚刚在检查器中注意到,一旦我单击项目并将其分配到库存中的插槽-仅在调用 Use 或 Remove 方法后它才会变空-在任何其他条件下都不会(并且应该在单击其他项目后)。
空槽截图https://prnt.sc/rezehc 占用槽截图 Screenshot2
想法 - 这是否意味着我只在脚本中重置“对象变量”而不是在实际项目插槽上?如果是 - 如何将其设置回 null?我有超过 60 个项目槽,看起来像这样 ->层次结构 每个操作按钮都包含相同的脚本(查看之前的屏幕截图)。
解决方案
当您调用 时RemoveListener
,您必须使用对之前调用它的实例的方法的引用来调用。基本上,您必须获得对上一个InventoryItemInfo
then 调用的引用:
UseButton.onClick.RemoveListener(previousInventoryItemInfo.OnUseButton);
RemoveButton.onClick.RemoveListener(previousInventoryItemInfo.OnRemoveButton);
好吧,你似乎没有提到那个容易获得的东西。我建议简单地使用RemoveAllListeners
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class InventoryItemInfo : MonoBehaviour
{
public GameObject ItemInfoTab;
public bool ItemInfoTabIsOpen = false;
public Button OpenTab;
public Text Title;
public Text UseButtonText;
public Text RemoveButtonText;
public Text ItemInfo;
public int SlotID;
public Image ItemImage;
public Item test;
public Button RemoveButton;
public Button UseButton;
Inventory inventory;
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
ItemInfoTabIsOpen = false;
}
}
public void OpenTabPressed()
{
Debug.Log("test object after opening a tab: " + test);
test = null;
Debug.Log("test object after opening a tab and reseting: " + test);
UseButton.onClick.RemoveAllListeners();
RemoveButton.onClick.RemoveAllListeners();
if (ItemInfoTab.activeInHierarchy)
{
ItemInfoTabIsOpen = false;
}
int itemsCount = GameObject.Find("GameManager").GetComponent<Inventory>().items.Count;
if (test == null)
{
if (itemsCount > SlotID)
{
if (ItemInfoTabIsOpen == false)
{
ItemInfoTab.SetActive(true);
ItemInfoTabIsOpen = true;
}
test = GameObject.Find("GameManager").GetComponent<Inventory>().items[SlotID];
UseButton.onClick.AddListener(OnUseButton);
RemoveButton.onClick.AddListener(OnRemoveButton);
Debug.Log("SlotID: " + SlotID);
Debug.Log("Clicked item: " + test);
Title.text = test.name;
ItemInfo.text = test.ItemInfo;
ItemImage.sprite = test.icon;
}
else
{
Debug.Log("Emtpy inventory slot");
}
}
else
{
test = null;
Debug.Log("Test object set to null");
}
}
public void OnUseButton()
{
Debug.Log("test object to use: " + test);
test.Use();
test = null;
Debug.Log("test object after using: " + test);
}
public void OnRemoveButton()
{
ItemInfoTab.SetActive(false);
ItemInfoTabIsOpen = false;
Debug.Log("test object to remove: " + test);
FindObjectOfType<Inventory>().Remove(test);
test = null;
}
}
推荐阅读
- yii2 - Yii2 社交登录出错:设置未知属性:yii\web\Application::authClientCollection
- ruby-on-rails - 使用 Devise 重定向到带有附加参数的登录页面
- php - 将数据从表单传递到字段集
- php - ElasticSearch:如何同步关系的属性(使用数据透视表)?
- javascript - 从postgres(node js)获取的数据如何在表格中漂亮的展示出来?
- bash - Bash - 如何合并重复的行并对 csv 文件的值求和?
- string - 高阶函数haskell
- android - 当上一个频道的视频在 android 应用程序的 youtube 视图中播放完时,如何自动播放来自其他频道的视频
- c - MISRA 2012 违规 - 类型不匹配(规则 10.1、10.4)
- r - R:纯固定效应模型中 sigma 的置信区间