How to downcast in Unity C# when you need to access specific subclass information on hover?

EDIT 2

This is what is being shown in the unity debugger when I look at the list of Items. It is seeing the baseball bat as an Item type, not WeaponItem type.

enter image description here

EDIT 1

This is currently all I have in my ItemsContainer ( my old file has the bloated xml file)

<?xml version="1.0" encoding="utf-8" ?>
<ItemsContainer>
  <Items>
    <!--Weapon Items-->
       <WeaponItem Name = "Baseball Bat" ID="501">
          <!--General Info-->
          <Description>A wooden bat that may have belonged to somone famous</Description>
          <!--Weapon Item-->
          <Damage>4</Damage>
          <Targets>1</Targets>
       </WeaponItem>
   </Items>
</ItemsContainer>

ORIGINAL POST

I'm working on a complex inventory system for a 2D game in Unity.

I read game data with XML serialization and have set up a drag/drop system that works using an Item class. I've also gotten pretty far with a dialogue system that can tell what items are in certain characters inventories.

Issue:

Want to be able to downcast to an item subclass in order to access specific data about it on a UI hover (At least that's what I think I need to do), or find another solution on how to setup my item database

For eg:

  • When you hover over a WeaponItem - it shows you damage information + desc
  • When you hover over a general Item - it only shows you desc

History:

What I have right now is a very bloated Items.xml file that has all tags necessary, regardless of item category. This is really painful to maintain as I come up with new Items and categories - so I wanted to try inheritance.

At first, my initial approach was:

<ItemsContainer>
   <Items>
      <Item>
          <!-- Generic tags or attributes: ID, Name, Description etc -->
          <!-- Other tags that define the item, is there in every item!-->
      </Item>
   </Items>
</ItemsContainer>

and now it's this:

<ItemsContainer>
   <Items>
      <WeaponItem>
          <!-- Generic tags or attributes: ID, Name, Description etc -->
          <!-- WeaponItem specific tags: Damage, Targets Etc -->
      </WeaponItem>
      <ClothingItem>
          <!-- Generic tags or attributes: ID, Name, Description etc -->
          <!-- ClothingItem specific tags: Exposure, ArmorRating Etc -->
      </ClothingItem>
      .
      . 
      .
   </Items>
</ItemsContainer>

My Item class looks like this:

public class Item 
{
   [XmlAttribute("Name")]
   public string Name;
   [XmlAttribute("ID")]
   public int ID;
   // General Info
   public string Description;
   public virtual string GetDetails()
   {
      return
        "\nName: " + Name +
        "\nID: " + ID +
        "\nDescription: " + Description;
   }
}

While my subclass WeaponItem looks like this:

class WeaponItem : Item
{
   public int Damage;
   public int Targets;
   public override string GetDetails()
   {
       return
           base.ToString() +
        "\nDamage: " + Damage +
        "\nTargets: " + Targets;
   }
}

Here's what my ItemsContainer looks like:

[XmlRoot("ItemsContainer")]
public class ItemsContainer
{
    [XmlArray("Items"),XmlArrayItem("WeaponItem"), XmlArrayItem("ClothingItem"),...]
    public List<Item> Items = new List<Item>();

    public static ItemsContainer Load(string path)
    {
       TextAsset _xml = Resources.Load<TextAsset>(path);
       XmlSerializer Serializer = new XmlSerializer(typeof(ItemsContainer));
       StringReader reader = new StringReader(_xml.text);
       ItemsContainer items = Serializer.Deserialize(reader) as ItemsContainer;
       reader.Close();
       return items;
    }
}

My ItemUI class contains:

  • Item variable
  • Several public game objects (I have a prefab of the game object this class is attached to)
  • OnDrag, EndDrag, BeginDrag methods

What I want to do in ItemUI - is sort of something like this:

public void HoverOn()
{
    if(Item.Class == "Weapon")
    { 
        WeaponItem WeaponItem = (WeaponItem) Item; // Downcast Fail
        Damage.txt = WeaponItem.GetDamage() + "";
        Damage.gameObject.SetVisible(true);
    }
    else if(Item.Class = "Clothing")
    {
        ClothingItem ClothingItem = (ClothingItem) Item; // Downcast Fail
        Exposure.txt = ClothingItem.GetExposure() + "";
        Exposure.gameObject.SetVisible(true);
    }
    ItemInfo.gameObject.SetVisible(true);
}

Question:

With the fact that I want my items in separate categories in XML, how would I accomplish showing Item information based on the subclass of the Item, if all I have at the time of access is a higher level Item class type?

Jon Skeet
people
quotationmark

The problem isn't the cast - it's the XML deserialization. The serializer is only creating Item instances, which is why the cast is failing.

You need to change your attributes to tell the serializer which types you want to create:

[XmlArray("Items")]
[XmlArrayItem("WeaponItem", Type = typeof(WeaponItem))]
[XmlArrayItem("ClothingItem", Type = typeof(ClothingItem))]
[...]
public List<Item> Items = new List<Item>();

Note that rather than a series of if statements, you might also want to just switch on Item.Class. (And in C# 7, you'll be able to use pattern matching to switch on the type instead...)

people

See more on this question at Stackoverflow