Unity中如何从itemData基类实例获取派生类ConsumableItem的restoredValue?
问题:如何从ScriptableObject基类实例获取派生类的属性值?
我正在开发一款游戏,物品系统采用ScriptableObject实现。有一个抽象基类itemData,用于存储所有物品的通用信息(如尺寸、编辑器图标等);通过派生类实现特定物品类型,比如带伤害的武器、可恢复属性的消耗品。
目前遇到的问题:无法获取消耗品的restoredValue值。编辑器中能看到itemData变量对应的ScriptableObject标注为“Consumable Item”,但不知道如何获取派生类的专属信息。
基类itemData代码
public abstract class itemData : ScriptableObject { public EquipmentSlot equipmentType; public ItemType type; [TextArea(15,20)] public string Itemdescription; public int width = 1; public int height = 1; public Sprite itemIcon; public GameObject thisItemObject; }
派生类ConsumableItem代码(当前仅需获取restoredValue)
public class ConsumableItem: itemData{ // 后续会扩展成支持恢复多种属性,比如同时补水和饱腹的食物,可能会把RestoredType和RestoredValue改成列表 public SpecialEffect specialeffect;// 物品使用时触发的特殊效果,比如增益buff public RestoredType restoredType;// 物品恢复的属性类型,比如食物、水分 public int restoredValue;// 属性恢复的数值 public itemData decayItem;// 物品在特定条件下(比如过期)会变成的物品 public itemData useItem;// 物品被使用后会变成的物品 }
使用场景代码
在获取值的脚本中,已判断物品为消耗品类型,无需担心空值问题。inventoryItem仅存储itemData实例,当前在UseItem方法中需要获取restoredValue:
private void UseItem(Vector2Int tileGridPosition) { inventoryItem usedItem = selectedItemGrid.GetItem(tileGridPosition.x, tileGridPosition.y); if ((usedItem != null)&&(usedItem.itemData.type == ItemType.Consumable)) { // 需要在这里获取消耗品的restoredType和restoredValue,调用thisCharacterStats.DoStatRestoreValue } }
解决方法
方式一:直接类型转换
因为usedItem.itemData是基类引用,实际指向的是ConsumableItem实例,直接做类型转换即可获取派生类属性:
private void UseItem(Vector2Int tileGridPosition) { inventoryItem usedItem = selectedItemGrid.GetItem(tileGridPosition.x, tileGridPosition.y); if ((usedItem != null) && (usedItem.itemData.type == ItemType.Consumable)) { // 强制转换(已判断类型,不会失败) ConsumableItem consumable = (ConsumableItem)usedItem.itemData; thisCharacterStats.DoStatRestoreValue(consumable.restoredType, consumable.restoredValue); } }
也可以用as关键字做安全转换(虽然这里已判断类型,可省略空值检查,但更符合C#编码习惯):
private void UseItem(Vector2Int tileGridPosition) { inventoryItem usedItem = selectedItemGrid.GetItem(tileGridPosition.x, tileGridPosition.y); if ((usedItem != null) && (usedItem.itemData.type == ItemType.Consumable)) { ConsumableItem consumable = usedItem.itemData as ConsumableItem; if(consumable != null) { thisCharacterStats.DoStatRestoreValue(consumable.restoredType, consumable.restoredValue); } } }
方式二:多态优化(推荐长期扩展)
如果后续物品类型会增加,建议用多态简化逻辑,避免频繁的类型判断和转换:
- 修改基类,添加抽象的
Use方法:
public abstract class itemData : ScriptableObject { // 原有属性... public abstract void Use(CharacterStats stats); }
- 在
ConsumableItem中实现Use方法:
public class ConsumableItem: itemData { // 原有属性... public override void Use(CharacterStats stats) { stats.DoStatRestoreValue(restoredType, restoredValue); // 还可以在这里处理物品使用后的转换逻辑,比如替换成useItem } }
- 使用时的代码会变得非常简洁:
private void UseItem(Vector2Int tileGridPosition) { inventoryItem usedItem = selectedItemGrid.GetItem(tileGridPosition.x, tileGridPosition.y); if (usedItem != null) { usedItem.itemData.Use(thisCharacterStats); } }
这种方式扩展性更强,后续新增武器、工具等物品类型时,只需要在对应派生类中实现Use方法即可,无需修改使用物品的核心逻辑。
内容的提问来源于stack exchange,提问作者Chuck
相关产品推荐
相关产品推荐

