Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by TheNoobieWaffle · Jul 09, 2016 at 05:39 AM · listclassinheritancedictionary

List of Different Classes That All Inherit From One Class

So, I have a list (using System.Collections.Generic) of a class 'Item'. Four classes inherit from Item: MeleeWeapon, RangedWeapon, MagicWeapon, and Armor. I use upcasting (or downcasting, not sure which one it technically is) to get the information I need fairly regularly. I'm going to do my best to explain everything so no one will need to dive into the scripts below because they are quite lengthy. I have an ID based inventory system, and each item is 'logged' with the Item class, or classes that inherit from Item. All of the items are kept within a class 'Database'. I have an custom editor script for database so I can add items a bit easier. I find myself having to keep a 'reference' variable of the type of inherited class (MeleeWeapon, RangedWeapon, MagicWeapon, and Armor) to later up/downcsat to the right type of class because all of the items are kept in a list of 'Item'. This list is known as 'visibleItems' that I modify with the editor script. In the start method of the Database class, I transfer all of the visibleItems to a Dictionary for a better numerical order. Seems more suiting for an ID based inventory system. Is there a better way to work with this? I thought that keeping this list of 'Item' that consists of Item and its child classes would be the best approach, but I just don't know anymore. And, my main issue is the fact that when I transfer everything over from visibleItems to the dictionaly of Items, everything seems to become just 'Item' and if it was supposed to be one of the child classes it appears to forget that data.

I currently only have two items in visibleItems, and both are 'MeleeWeapon', but they become Item when I start the game. They forget the variables that are declared within Melee.

Hopefully no one has to dig though this too much, but Ill list the scripts I mentioned. I want to note, though, that I will not include MagicWeapon, RangedWepaon, and Armor as they are blank. I also want to note that whenever I have switch statements that go through the different sub classes, MeleeWeapon is the only one you should worry about because I have not yet begun to implement the other weapons, so those statements are blank for the most part. You can also probably ignore most of the ditor script, but maybe note how I keep track of that reference variable I mentioned and what I do with it.

I will take any tips anyone has for me as well. The editor script isnt too unorganized, but I'm a bit more lax to organization in that compared to script that actually go in the game itself.

 //Item.cs
 using UnityEngine;
 using System.Collections.Generic;
 using Inventory;

 [System.Serializable]
 public class Item {

     //DataBase Preference
     public int id;
     public string name;
     public int maxStack;

     public string icon;//Path to icon

     public bool handEquippable = false;//NOT ARMOR, Weapon Only
     public bool matchHandRotation = true;//Does the object always point a certain way (according to the player look direction)
     public bool lockToHand = true;//Does the object lock itself to the players hand even if the animation attempts to take it far away
     public string model;
     public string fps_equipAnimation;//Path to animation
     public string world_equipAnimation;//Path to animation

     //Reference Variables
     public ItemType type = ItemType.Generic;

     //Constructor
     public Item( ItemType iType = ItemType.Generic ) {
         type = iType;
     }
     public Item( int iId, string iName, int iMaxStack ) {
         id = iId;
         name = iName;
         maxStack = iMaxStack;
     }
     public Item( int iId, string iName, int iMaxStack, bool iHandEquippable, bool iMatchHandRotation, bool iLockToHand, ItemType iType = ItemType.Generic ) {
         id = iId;
         name = iName;
         maxStack = iMaxStack;
         handEquippable = iHandEquippable;
         matchHandRotation = iMatchHandRotation;
         lockToHand = iLockToHand;
         type = iType;
     }
 }











 //MeleeWeapon.cs
 using UnityEngine;
 using System.Collections.Generic;
 using Inventory;

 public class MeleeWeapon : Item {

     //Preference Variables
     public float damage;
     public float range;

     //Constructor
     public MeleeWeapon() : base( ItemType.MeleeWeapon ) {
     }

 }









 //Database.cs
 using UnityEngine;

 using System;
 using System.Collections;
 using System.Collections.Generic;

 using Inventory;

 public class Database : MonoBehaviour {

     //Static Variables
     public static Database database;
     public static Dictionary<int, Item> items = new Dictionary<int, Item>();

     //Preference Variables
     public Item[] visibleItems;

     void Start() {
         if ( database == null ) {
             database = this;
             SetupDatabase();
         } else {
             Destroy( this );
         }
     }

     private void SetupDatabase() {
         for ( int i = 0; i < visibleItems.Length; i++ ) {
             switch ( visibleItems[i].type ) {
                 case ItemType.Armor:
                     items.Add( i, visibleItems[i] as Armor );
                 break;
                 case ItemType.MeleeWeapon:
                     items.Add( i, visibleItems[i] as MeleeWeapon );
                 break;
                 case ItemType.RangedWeapon:
                     items.Add( i, visibleItems[i] as RangedWeapon );
                 break;
                 case ItemType.MagicWeapon:
                     items.Add( i, visibleItems[i] as MagicWeapon );
                 break;
                 case ItemType.Generic:
                     items.Add( i, visibleItems[i] );
                 break;
             }
         }
     }

     public static int FindItemIdByName( string name ) {
         foreach ( KeyValuePair<int, Item> i in items ) {
             if ( i.Value.name == name ) {
                 return i.Key;
             }
         }
         return -1;
     }

 }

 namespace Inventory {

     public enum ItemType {
         Generic,//Item base-class (non-eqippable)
         Armor,
         MeleeWeapon,
         RangedWeapon,
         MagicWeapon
     }

     public enum DualHandType {
         SingleHandEquip,
         BothHands,
         LeftHandOnly,
         RightHandOnly
     }

 }










 //DatabaseEditor.cs
 using UnityEngine;
 using UnityEditor;
 using System.Collections.Generic;
 using Inventory;

 [CustomEditor( typeof( Database) )]
 public class DatabaseEditor : Editor {

     Database db;

     void OnEnable() {
         db = (Database)target;
         for ( int i = 0; i < db.visibleItems.Length; i++ ) {
             Reininialize( i );
         }
     }

     public override void OnInspectorGUI() {

         int toRemove = -1;

         for ( int i = 0; i < db.visibleItems.Length; i++ ) {
             GUILayout.BeginVertical( "box" );
             GUILayout.BeginVertical( "box" );
             //Start:Intro
             Item item = db.visibleItems[i] as Item;
             GUILayout.BeginHorizontal();
             GUILayout.Label( "ID: " + ( i <= 9 ? "0" : "" ) + ( i <= 99 ? "0" : "" ) + i );
             GUILayout.FlexibleSpace();
             if ( GUILayout.Button( "Up", GUILayout.Width( 50 ) ) )
                 MoveUp( i );
             if ( GUILayout.Button( "Down", GUILayout.Width( 50 ) ) )
                 MoveDown( i );
             if ( GUILayout.Button( "Remove", GUILayout.Width( 80 ) ) )
                 toRemove = i;
             if ( GUILayout.Button( "Add At", GUILayout.Width( 60 ) ) )
                 AddItem( i );
             GUILayout.EndHorizontal();
             GUILayout.BeginHorizontal();
             GUILayout.Label( "Name:" );
             item.name = EditorGUILayout.TextField( item.name );
             GUILayout.FlexibleSpace();
             ItemType was = item.type;
             item.type = (ItemType)EditorGUILayout.EnumPopup( "as", item.type );
             if ( was != item.type ) {
                 Reininialize( i );
             }
             GUILayout.EndHorizontal();
             //End
             GUILayout.EndVertical();
             GUILayout.BeginVertical( "box" );
             //Start:Generic
             if ( item.type == ItemType.Generic ) {
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Max stack:" );
                 GUILayout.FlexibleSpace();
                 item.maxStack = EditorGUILayout.IntField( item.maxStack );
                 GUILayout.EndHorizontal();
             } else {
                 item.maxStack = 1;
             }
             GUILayout.BeginHorizontal();
             GUILayout.Label( "Icon path:" );
             GUILayout.FlexibleSpace();
             item.icon = EditorGUILayout.TextField( item.icon );
             GUILayout.EndHorizontal();
             if ( item.type == ItemType.Generic ) {
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Hand equippable:" );
                 GUILayout.FlexibleSpace();
                 item.handEquippable = EditorGUILayout.Toggle( item.handEquippable );
                 GUILayout.EndHorizontal();
             } else {
                 item.handEquippable = true;
             }
             if ( item.handEquippable ) {
                 //item.dualHandType = (DualHandType)EditorGUILayout.EnumPopup( "Dual Hand Type:", item.dualHandType );
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Match hand rotation:" );
                 GUILayout.FlexibleSpace();
                 item.matchHandRotation = EditorGUILayout.Toggle( item.matchHandRotation );
                 GUILayout.EndHorizontal();
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Lock to hand:" );
                 GUILayout.FlexibleSpace();
                 item.lockToHand = EditorGUILayout.Toggle( item.lockToHand );
                 GUILayout.EndHorizontal();
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Model path:" );
                 GUILayout.FlexibleSpace();
                 item.model = EditorGUILayout.TextField( item.model );
                 GUILayout.EndHorizontal();
                 GUILayout.BeginHorizontal();
                 GUILayout.Label( "Equip anim. path (FPS):" );
                 GUILayout.FlexibleSpace();
                 item.fps_equipAnimation = EditorGUILayout.TextField( item.fps_equipAnimation );
                 GUILayout.EndHorizontal();
                 /*
                 GUILayout.BeginVertical( "box" );
                 GUILayout.Label( "FPS Animations" );
                 for ( int j = 0; j < item.fps_animations.Count; j++ ) {
                     GUILayout.BeginHorizontal();
                     GUILayout.Label( "Anim. path (FPS):" );
                     GUILayout.FlexibleSpace();
                     item.fps_animations[j] = EditorGUILayout.TextField( item.fps_animations[j] );
                     if ( GUILayout.Button( "X", GUILayout.Width( 35 ) ) )
                         item.fps_animations.RemoveAt( j );
                     GUILayout.EndHorizontal();
                 }
                 if ( GUILayout.Button( "Add FPS Animation" ) )
                     item.fps_animations.Add( "null" );
                 GUILayout.EndVertical();
                 */
             }
             //End
             GUILayout.EndVertical();
             if ( item.type != ItemType.Generic ) {
                 GUILayout.BeginVertical( "box" );
                 //Start:Specific
                 switch ( item.type ) {
                     case ItemType.Armor:
                         Armor armor = db.visibleItems[i] as Armor;
                         GUILayout.BeginHorizontal();
                         GUILayout.Label( "Protection:" );
                         GUILayout.FlexibleSpace();
                         armor.protection = EditorGUILayout.IntField( armor.protection );
                         GUILayout.EndHorizontal();
                         break;
                     case ItemType.MeleeWeapon:
                         MeleeWeapon meleeWeapon = db.visibleItems[i] as MeleeWeapon;
                         GUILayout.BeginHorizontal();
                         GUILayout.Label( "Damage:" );
                         GUILayout.FlexibleSpace();
                         meleeWeapon.damage = EditorGUILayout.FloatField( meleeWeapon.damage );
                         GUILayout.EndHorizontal();
                         GUILayout.BeginHorizontal();
                         GUILayout.Label( "Range:" );
                         GUILayout.FlexibleSpace();
                         meleeWeapon.range = EditorGUILayout.FloatField( meleeWeapon.range );
                         GUILayout.EndHorizontal();
                         break;
                     case ItemType.RangedWeapon:
                         RangedWeapon rangedWeapon = db.visibleItems[i] as RangedWeapon;
                         break;
                     case ItemType.MagicWeapon:
                         MagicWeapon magicWeapon = db.visibleItems[i] as MagicWeapon;
                         break;
                 }
                 //
                 GUILayout.EndVertical();
             }
             GUILayout.EndVertical();
         }

         GUILayout.Space( 20 );

         if ( GUILayout.Button( "Add Item" ) )
             AddItem( -1 );

         if ( toRemove != -1 )
             Remove( toRemove );

         //base.OnInspectorGUI();
     }

     void AddItem( int location ) {
         if ( location == -1 ) {
             Item[] newArray;
             if ( db.visibleItems == null ) {
                 newArray = new Item[1];
             } else {
                 newArray = new Item[db.visibleItems.Length + 1];
                 for ( int i = 0; i < db.visibleItems.Length; i++ ) {
                     newArray[i] = db.visibleItems[i];
                 }
             }
             db.visibleItems = newArray;
         } else {
             Item[] newArray = new Item[db.visibleItems.Length + 1];
             for ( int i = 0; i < location + 1; i++ ) {
                 newArray[i] = db.visibleItems[i];
             }
             newArray[location + 1] = new Item();
             for ( int i = location + 1; i < newArray.Length - 1; i++ ) {
                 newArray[i + 1] = db.visibleItems[i];
             }

             db.visibleItems = new Item[newArray.Length];
             for ( int i = 0; i < newArray.Length; i++ ) {
                 db.visibleItems[i] = newArray[i];
             }
         }
     }

     void MoveUp( int index ) {
         if ( index == 0 )
             return;
         Item temp = db.visibleItems[index - 1];
         db.visibleItems[index - 1] = db.visibleItems[index];
         db.visibleItems[index] = temp;
     }

     void MoveDown( int index ) {
         if ( index == db.visibleItems.Length - 1 )
             return;
         Item temp = db.visibleItems[index + 1];
         db.visibleItems[index + 1] = db.visibleItems[index];
         db.visibleItems[index] = temp;
     }

     void Reininialize( int index ) {
         Item old = db.visibleItems[index];
         switch ( db.visibleItems[index].type ) {
             case ItemType.Armor:
                 db.visibleItems[index] = new Armor();
             break;
             case ItemType.MeleeWeapon:
                 db.visibleItems[index] = new MeleeWeapon();
             break;
             case ItemType.RangedWeapon:
                 db.visibleItems[index] = new RangedWeapon();
             break;
             case ItemType.MagicWeapon:
                 db.visibleItems[index] = new MagicWeapon();
             break;
             case ItemType.Generic:
                 db.visibleItems[index] = new Item();
             break;
         }
         db.visibleItems[index].id = old.id;
         db.visibleItems[index].name = old.name;
         db.visibleItems[index].maxStack = old.maxStack;
         db.visibleItems[index].icon = old.icon;
         db.visibleItems[index].handEquippable = old.handEquippable;
         db.visibleItems[index].matchHandRotation = old.matchHandRotation;
         db.visibleItems[index].lockToHand = old.lockToHand;
         db.visibleItems[index].model = old.model;
         db.visibleItems[index].fps_equipAnimation = old.fps_equipAnimation;
     }

     void Remove( int index ) {
         Item[] newArray = new Item[db.visibleItems.Length - 1];
         for ( int i = 0; i < index; i++ ) {
             newArray[i] = db.visibleItems[i];
         }
         for ( int i = index; i < db.visibleItems.Length - 1; i++ ) {
             newArray[i] = db.visibleItems[i + 1];
         }

         db.visibleItems = new Item[newArray.Length];
         for ( int i = 0; i < newArray.Length; i++ ) {
             db.visibleItems[i] = newArray[i];
         }
     }

 }

I couldnt get it to format correctly in seperate areas, so its all together with like 10 spaces between each. It consists of Item.cs, MeleeWeapon.cs, Database.cs and DatabaseEditor.cs. The namespace 'Inventory' is part of the database script, located at the bottom of it. FYI.

I also want to thank anyone who looks into this. I know it a lot of code to go through. I appreciate it a whole lot, as this is the foundation of a game I am working on and I care about it very dearly. :)

Thanks -Michael

PS: Going to bed, very late. I plan on checking back here early tomorrow! ~9-10hr

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Masterio · Jul 09, 2016 at 06:34 AM 0
Share

I reworked Reinitialize method:

  • I am not sure if it will works, i wrote it in notepad.

void Reininialize( int index ) { Item old = db.visibleItems[index];

 if(old == null)
 {
     Debug.LogError("Index: "+index+" not found.");
     return;
 }
 
 Item item = null;
 
 switch ( db.visibleItems[index].type ) 
 {
     ...
     case ItemType.$$anonymous$$eleeWeapon:
         // add a constructor for the additional properties in the $$anonymous$$eleeWeapon class.
         item = new $$anonymous$$eleeWeapon((($$anonymous$$eleeWeapon)old).damage, (($$anonymous$$eleeWeapon)old).range); 
         break;
     ...
 }
 
 if(item != null)
 {
     item.id = old.id;
     item.name = old.name;
     item.maxStack = old.maxStack;
     item.icon = old.icon;
     item.handEquippable = old.handEquippable;
     item.matchHandRotation = old.matchHandRotation;
     item.lockToHand = old.lockToHand;
     item.model = old.model;
     item.fps_equipAnimation = old.fps_equipAnimation;
     
     db.visibleItems[index] = item; 
 }
 else
     Debug.LogError("Item ID: "+old.id+" error.");

}

avatar image TheNoobieWaffle · Jul 09, 2016 at 04:08 PM 0
Share

item = new $$anonymous$$eleeWeapon( ( ($$anonymous$$eleeWeapon)old ).damage, ( ($$anonymous$$eleeWeapon)old ).range );

InvalidCastException: Cannot cast from source type to destination type. DatabaseEditor.Reininialize (Int32 index) (at Assets/Scripts/InventorySystem/Inventory/Editor/DatabaseEditor.cs:225)

I've been trying different ways to fix that but I'm just not sure how to retrieve the data correctly to use it.

1 Reply

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Bunny83 · Jul 09, 2016 at 05:09 PM

Well your approach has a big problem: Unity's seriaization system does not support inheritance for custom classes. Unity serializes custom classes based on the variable/field type. So since the type of your visibleItems array is "Item", all instances will be of type "Item" once they got serialized / deserialized.

You would need to either use ScriptableObjects or MonoBehaviours which do support inheritance. However they are serialized as standalone assets. So if you want them to persist in your project you should save each item as asset into your project.

Custom classes are not real objects from the serialization system's point of view. The fields of that custom class are simply "added" to the object which is serialized as asset. So a MonoBehaviour in the scene is a seperate serialized object. Custom (serializable) classes fields in that monobehaviour are serialized like fields of the MonoBehaviour. There is no type information saved. Unity recreates those custom classes based on the variable type. So all your custom class instances will be "Items" after deserialization.

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image TheNoobieWaffle · Jul 09, 2016 at 05:25 PM 0
Share

I haven't looked into scriptable objects yet... but, will I able to create different classes of a scriptable object and store them all in a list like I was trying to with my four classes that inherit from 'Item'? If I can, they are certainly worth looking into.

avatar image Bunny83 TheNoobieWaffle · Jul 09, 2016 at 06:21 PM 0
Share

Yes you can. However ScriptableObjects can't be created with "new". If you create them manually you have to use ScriptableObject.CreateInstance<YourClass>().

Another difference is that they won't show up in the inspector like nested fields as they are actual assets. Those have to be viewed seperately in the inspector unless you write a custom inspector / property drawer for them.

avatar image TheNoobieWaffle Bunny83 · Jul 10, 2016 at 12:16 AM 0
Share

I suppose I might not really need a custom inspector for it, if I create an item one time and never really return to it because I have no need for it...

I want to thank you guys ($$anonymous$$asterio, and Bunny83) for helping me. $$anonymous$$uch appreciated :)

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

47 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

A node in a childnode? 1 Answer

Inherited member variables not set to base value c# 1 Answer

Loading component by superclass. 1 Answer

How do I Sort a List of Classes by a property? 2 Answers

Custom class always returning Null 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges