Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Question about Inherited Classes

Discussion in 'Scripting' started by khanstruct, Mar 18, 2015.

  1. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,870
    I have a quick question that I can't find an answer for (primarily because I don't really know how to word it).

    I'm in the process of planning out my RPG. I've created a BaseSkill class. I will then be creating individual skills that inherit from that class. Easy.

    My question is this: Can I create a character with an array of BaseClass[], but then populate it with inherited classes? Will the system accept the inherited classes as the BaseClass type?

    Thanks!
     
  2. shaderop

    shaderop

    Joined:
    Nov 24, 2010
    Posts:
    942
    Yes, you can. It's called "polymorphism," and it's one of the pillars of OOP. Basically, every class that derives from BaseSkill is also a BaseSkill, and the relationship is sometimes referred to as an is-a relationship in technical literature.
     
    khanstruct likes this.
  3. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,870
    Thank you!
     
  4. Zerot

    Zerot

    Joined:
    Jul 13, 2011
    Posts:
    135
    Do note that this breaks in unity with regards to serialization. E.g. if your BaseClass[] array is editable in the inspector then during deserialization it will only contain entries of type BaseClass and not your derived classes.

    If you only use that array at runtime then there is no problem. This is only a problem when unity stores the data for you. If you do want unity to store the data for you, then your BaseClass needs to inherit from ScriptableObject. ScriptableObjects are stored as their own instances, so the inheritance data is preserved.

    See also http://blogs.unity3d.com/2014/06/24/serialization-in-unity/
     
    TonyLi and khanstruct like this.
  5. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,870
    Ah, thank you. Not with this project, but I have gotten the "must inherit from a scriptable object" error in the past. I never quite understood what it meant.
     
  6. Deleted User

    Deleted User

    Guest

    Not sure if that's correct. I have a "Parent" array filled with "Child" objects and it's serialized just fine by Unity when I launch the game.
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    As @Zerot writes, check out the blog link. In particular, jump down to the section titled "No support for polymorphism".

    For MonoBehaviours, instead of using inherited classes, consider using C# interfaces or, if you're using Unity 4.6+, the event system. For skills, C# interfaces are probably more appropriate than polymorphism anyway.
     
  8. Deleted User

    Deleted User

    Guest

    That's good to know. I guess I never encountered any problems so far due to the way I ended up interacting with the classes stored in the array.

    What do you mean about the interface part? Unity can't serialize Interfaces from what I understand. Am I wrong?
     
  9. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Not serializing interfaces isn't just a Unity thing. Serialization is about saving and loading an object's state. Interfaces don't have state unto themselves.

    That being said - you can use ISerializable with an interface but the implementation needs to be in the concrete class anyway so I don't know how much that buys you beyond just enforcing 2 contracts.
     
  10. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    Right. If you're using interfaces, you don't need to worry about serialization and inheritance. Just define the data you need in each skill class (each of which derives from MonoBehaviour and implements the interface), and let it serialize itself normally.

    For example:
    Code (csharp):
    1. public interface ISkill {
    2.     bool AreRequirementsMet(); // Can the character use the skill right now?
    3.     void Use(); // Use the skill.
    4. }
    The examples below are only meant to demonstrate the use of interfaces. They're not good examples of making reusable classes (e.g., never hardcode "50" in real practice).
    Code (csharp):
    1. public class Fireball : MonoBehaviour, ISkill {
    2.     public GameObject fireballPrefab;
    3.  
    4.     public bool AreRequirementsMet() {
    5.         return Player.mana > 50;
    6.     }
    7.  
    8.     public void Use() {
    9.         Instantiate(fireballPrefab, transform.position, transform.rotation);
    10.     }
    11. }
    Code (csharp):
    1. public class Heal : MonoBehaviour, ISkill {
    2.     public float healAmount;
    3.  
    4.     public bool AreRequirementsMet() {
    5.         return Player.mana > 20;
    6.     }
    7.  
    8.     public void Use() {
    9.         Player.target.Heal(healAmount);
    10.     }
    11. }
     
    Last edited: Mar 18, 2015
  11. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    That works (aside from a skill also being a MonoBehaviour, which is probably the wrong approach) until you need an array of them. IE - this would not work.
    Code (csharp):
    1.  
    2. public class Player : MonoBehaviour
    3. {
    4.     [SerializeField]
    5.     ISkill[] skills;
    6. }
    7.  
     
  12. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    Good point. Don't know where I got it in my head that the OP was talking specifically about MonoBehaviours. Well, if the OP did mean MonoBehaviours, you can get an array of the ISkill MonoBehaviours on the GameObject using:
    Code (csharp):
    1. GetComponents(typeof(ISkill))
    plyGame does a good job with skills. They're defined in data, rather than in code, so you can define a new skill without having to write a script for it. If I recall correctly, a plyGame skill is a scriptable object backed by a library of general-purpose methods that can be configured for each skill.
     
  13. Deleted User

    Deleted User

    Guest

    So what's wrong with just having an abstract class with "AreRequirementsMet" and "Use" functions? You'd still be able to do:

    Code (CSharp):
    1. public class Player : MonoBehaviour {
    2.  
    3. public Skill[] skills;
    4.  
    5. void OnKeyDown(){
    6. skills[0].Use();
    7. }
    8.  
    9. }
     
  14. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Does that work? I know the generic overload of GetComponent constrains T to Component so it would be funny if the non-generic version took interfaces.

    Unity doesn't do polymorphic serialization so you'd lose the state of those skill instances that inherited from Skill.
     
  15. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    Yup. It gets all components, then returns those whose types match ISkill. I use this in the Dialogue System to find components that use the IDialogueUI interface.

    Then you get into the inheritance tree problem. Say your inheritance tree looks like this:
    • BaseSkill
      • ProjectileSkill (defines ShootProjectile method)
        • FireballSkill
      • HealSkill (defines HealTarget method)
        • LayHands
    It's kind of a contrived example (and not the best example at that), but it makes the point clearer so bear with me. :)

    In ProjectileSkill, you define a method ShootProjectile.

    In HealSkill, you define a method HealTarget.

    Now you want to make a skill that shoots a ball of healing energy. If you make it a subclass of HealSkill, it inherits HealSkill's HealTarget method. But it doesn't inherit ShootProjectile, since that method is defined in ProjectileSkill, which is a separate branch of the inheritance tree.

    You could move ShootProjectile into BaseSkill, but:

    1. You'll eventually end up with everything in BaseSkill this way,
    2. You risk affecting all child classes when you modify a parent class,
    3. Your classes inherit all kinds of unnecessary baggage. (LayHands doesn't need a ShootProjectile method.)
     
  16. Deleted User

    Deleted User

    Guest

    So how would you do this using Interfaces as you mentioned previously? (Create a healing projectile)

    Maybe I'm not understanding this right but doesn't RectTransform inherit from Transform yet it serializes just fine? Same for the colliders where they all inherit from Collider.
     
  17. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Check out the blog post @Zerot linked. UnityEngine.Object derivatives aren't serialized inline and therefore work just fine.
     
  18. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    Clumsily. I think the best approach is a single ScriptableObject for which you define skill-specific behavior in data, not in code (like plyGame goes).

    But the idea behind the interfaces idea was that another script could call ISkill.Use() on any skill class without having to know anything about the skill class. It's similar to the polymorphism idea, but skills aren't tied into an inheritance tree. They don't have to be related at all, in fact, other than promising to implement the methods specified by the interface.
     
  19. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,870
    :eek: This got way more complicated than I expected. :(
     
  20. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    The simplest approach would be to use a single monobehavior called Skills for example. Let it have a field that is a List<ISkill>. You would add all of your classes that implement ISkill to this behavior and be able to look them up with a single lookup of GetComponent<Skills>.
     
  21. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,384
    Pretty awesome thread now though =)
     
    khanstruct likes this.
  22. Deleted User

    Deleted User

    Guest

    OK I re-read it. I didn't see the part about where it only applies to custom classes--those that don't inherit from MonoBehaviour.
     
  23. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    How about this for a simpler solution? It doesn't require interfaces, scriptable objects, inheritance, or anything like that. It's similar to what's done in some FPS frameworks to handle multiple weapons.

    1. Create each skill as a child GameObject. For example:
    • Player
      • FireballSkill
        • Fireball (inactive)
      • LayHandsSkill
      • etc...
    One nice thing about this is that you can add children to each skill GameObject, such as the Fireball object that you can clone when casting a fireball. You can also save these GameObjects as prefabs so you can assign them to other characters, too.

    2. Tag each child GameObject as "Skill".

    3. If the player has a skill manager script, you can keep track of the skills like this:
    Code (csharp):
    1. public GameObject[] skills;
    Or find them at runtime like this:
    Code (csharp):
    1. using System.Linq;
    2. skills = transform.Cast<Transform>().Where(child => child.CompareTag("Skill")).ToArray();
    4. When a skill is available to use, make its GameObject active. Otherwise keep it inactive.

    5. Add scripts to each skill GameObject. The scripts don't have to be related at all, but each one should implement a common method name such as "Use".

    6. When the player hits the "use current skill" button, broadcast "Use":
    Code (csharp):
    1. BroadcastMessage("Use");
    You could fiddle with all kinds of tweaking and optimization (no need to complicate the post with that), but this approach seems pretty simple.

    If you use a skillbar, you could keep all of the skill GameObjects active. Instead of #4 and #6 above, send "Use" only to the skill GameObject associated with the skillbar button that the player clicks.

    The scripts on a skill GameObject can all be short and simple, easy to write and verify for correctness. You can compose complex behavior out of a small handful of scripts such as:
    • PlayAnimation -- plays an animation such as casting
    • PlayAudio -- plays an audio clip such as a fireball whoosh
    • InstantiateObject -- instantiates an object, possibly with force, such as a fireball or a summoned pet
    • ChangeTargetStats -- increase/decrease HP, etc.
    • RequireMana -- keeps the other scripts disabled (so they won't respond to "Use") while the player's mana is below a certain minimum, and decrements the player's mana when it receives "Use".
    For example, a punch skill could be composed of PlayAnimation (punch) and PlayAudio (grunt). A fireball skill could be composed of PlayAnimation (cast spell), PlayAudio (whoosh), InstantiateObject (fireball), and RequireMana (50 mana).

    This approach follows a lot of good software design principles, such as:
    • Single script - single responsibility
    • Code reuse
    • Favor data-driven design over code-driven design when defining content
    • Favor composition over inheritance when objects only partially overlap in functionality
     
    Last edited: Mar 19, 2015
  24. Zerot

    Zerot

    Joined:
    Jul 13, 2011
    Posts:
    135
    RectTransform inherits from Transform.
    Transform inherits from Component.
    Component inherits from UnityEngine.Object
    UnityEngine.Object inherits from System.Object

    BoxCollider inherits from Collider.
    Collider inherits from Component.
    Component inherits from UnityEngine.Object
    UnityEngine.Object inherits from System.Object

    MonoBehaviour inherits from Behaviour
    Behaviour inherits from Component
    Component inherits from UnityEngine.Object
    UnityEngine.Object inherits from System.Object

    ScriptableObject inherits from UnityEngine.Object
    UnityEngine.Object inherits from System.Object

    YourDerivedClass inherits from YourBaseClass
    YourBaseClass inherits from System.Object

    See where all of unity's components that serialize correctly with polymorphism have UnityEngine.Object in their inheritance tree? Also see how your own class doesn't? That is why it works correctly for unity types and not for your own custom types unless you inherit from a MonoBehaviour or ScriptableObject.
     
    Deleted User likes this.
  25. kippert

    kippert

    Joined:
    Mar 13, 2015
    Posts:
    3
    Going back to the OP, might I make the suggestion that using inheritance in this situation isn't necessarily the right thing to do?
    There are some key questions to ask here:
    1. What is going to be different between your concrete classes?
    2. Are the differences largely going to be in Unity specific stuff? For example an animation?

    I think what I'm getting at is that you may be able to just have a single skill class which you can have multiple instances of, and have just the data contained within it different rather than the behaviour.

    Ask yourself if there is a way to push different behaviour into Unity specific components so that you can work around the polymorphic limitation.

    As a side note, this is more to do with polymorphism implemented through inheritance, an important note on inheritance (another pillar in OOP) is that it's uses are actually not that great. Sometimes inheritance should be used, but most of the time it should not simply because it can introduce some serious coupling (this is a bad thing and you can research in your own time).

    TLDR;
    Is inheritance and polymorphism the correct thing to do? Is there another, simpler and more optimal solution out there?

    Hope this helps.