You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        }
    }
}

方式二:多态优化(推荐长期扩展)

如果后续物品类型会增加,建议用多态简化逻辑,避免频繁的类型判断和转换:

  1. 修改基类,添加抽象的Use方法:
public abstract class itemData : ScriptableObject
{
    // 原有属性...
    public abstract void Use(CharacterStats stats);
}
  1. 在ConsumableItem中实现Use方法:
public class ConsumableItem: itemData
{
    // 原有属性...
    public override void Use(CharacterStats stats)
    {
        stats.DoStatRestoreValue(restoredType, restoredValue);
        // 还可以在这里处理物品使用后的转换逻辑,比如替换成useItem
    }
}
  1. 使用时的代码会变得非常简洁:
private void UseItem(Vector2Int tileGridPosition)
{
    inventoryItem usedItem = selectedItemGrid.GetItem(tileGridPosition.x, tileGridPosition.y);
    if (usedItem != null) 
    {
        usedItem.itemData.Use(thisCharacterStats);
    }
}

这种方式扩展性更强,后续新增武器、工具等物品类型时,只需要在对应派生类中实现Use方法即可,无需修改使用物品的核心逻辑。


内容的提问来源于stack exchange,提问作者Chuck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:37:43