Search Unity

Need help with derived classes.

Discussion in 'Scripting' started by Chance-Touchstone, May 16, 2019.

  1. Chance-Touchstone

    Chance-Touchstone

    Joined:
    Dec 26, 2013
    Posts:
    43
    I could use some help. I'm trying to use derived classes dynamically but I'm having trouble referencing them. Please see TODOs 1-3.

    Code (CSharp):
    1. public class BaseClass : MonoBehaviour
    2. {
    3.     public DoSomethingShared()
    4.     {
    5.     }
    6. }
    7.  
    8.  
    9. public class DerivedClassA : BaseClass
    10. {
    11.     public DoSomething()
    12.     {
    13.         //Do something A
    14.     }
    15. }
    16.  
    17. public class DerivedClassB : BaseClass
    18. {
    19.     public DoSomething()
    20.     {
    21.         //Do something B
    22.     }
    23. }
    24.  
    25.  
    26. public class GameManager : MonoBehaviour
    27. {
    28.  
    29.     public CharacterType characterType;
    30.  
    31.     TODO derivedScriptRef;
    32.  
    33.     void Start()
    34.     {
    35.  
    36.  
    37. //This works to add the appropriate script/component except for the part when I'm trying place the component in a variable.
    38.         AddDerivedComponent();
    39.  
    40. //TODO2
    41.         derivedScriptRef.DoSomething(); //TDOO: Call the appropriate DoSomething() function for the matching derived class?
    42.  
    43.  
    44. //TODO3: How can I remove the derived class component by characterType without a bunch of if statements or a switch statement? Is there a cleaner way? Perhaps if I had a better variable holding the reference to it from TODO1, this would be easier?!
    45.         Destroy(derivedScriptRef);
    46.  
    47.     }
    48.  
    49.     private void AddDerivedComponent()
    50.     {
    51.  
    52.         string _scriptname = "Derived" + characterType.ToString();
    53.         //Fetch the appropriate script
    54.         System.Type _script = System.Type.GetType(_scriptname + ",Assembly-CSharp");
    55.         //Add Component by CharacterType
    56.         this.gameObject.AddComponent(_script); //This works but below does not
    57.  
    58.  
    59.  //TODO1: How can I Make a reference to the derived script no matter which derived class it is?
    60.  //I see that I can save either type in MonoBehaviour derivedScriptRef; But then I have errors trying to call the DoSomethingA() or DoSomethingB() functions.
    61. //System.Type derivedScriptRef = this.gameObject.AddComponent(_script); //This Does not work.
    62.  
    63.     }
    64. }
    65.  
    66. public enum CharacterType
    67. {
    68.     ClassA,
    69.     ClassB,
    70. }
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    This is basically what polymorphism is all about.

    I'll start with TODO1 and TODO3: if you have a BaseClass derivedScriptRef; as the object you hold on to the reference for, then you'll be able to have the reference no matter the type, and "Destroy" will work on it no matter the type.
    You'll need to "catch" the new component when it's created, and assign it:
    Code (csharp):
    1.  
    2.  System.Type _script = System.Type.GetType(_scriptname + ",Assembly-CSharp");
    3.         //Add Component by CharacterType
    4.         Component createdComponent = this.gameObject.AddComponent(_script);
    5. derivedScriptRef = createdComponent as BaseScript;
    6. // OR
    7. derivedScriptRef = (BaseScript)createdComponent;
    The "as" keyword will convert createdComponent (which will be of type Component, from which your BaseClass inherits ultimately) into BaseClass, and if createdComponent is not BaseClass or a child class of BaseClass, will be null instead. The second line, which is typecasting, does the same but will throw an exception if createdComponent is not Base or an inherited type.

    Now in order for this to compile, you need to make the compiler understand that DoSomething is something your classes have in common. As written, your two DoSomething's are entirely unrelated as far as the compiler is concerned, which is why you can't do much with them. The magic words you'll need are "virtual", "abstract", and "override".
    Code (csharp):
    1.  
    2. public class BaseClass : MonoBehaviour
    3. {
    4.  
    5. public virtual void DoSomething() { }
    6. // OR
    7. public abstract void DoSomething(); // if you do this, the class must be declared like "public abstract class BaseClass"
    8. }
    9.  
    10.  
    11. public class DerivedClassA : BaseClass
    12. {
    13.     public override void DoSomething()
    14.     {
    15.         //Do something A
    16.     }
    17. }
    18.  
    19. public class DerivedClassB : BaseClass
    20. {
    21.     public override void DoSomething()
    22.     {
    23.         //Do something B
    24.     }
    25. }
    26.  
    (Note: the difference between "virtual" and "abstract" is that, abstract forces child classes to override that method, or they won't compile, unless they too are abstract; and you can never create an instance of an abstract class, only of its derived children. Abstract is useful if you need DerivedClassA and DerivedClassB and they have shared functionality, but you never intend to use BaseClass directly. With "virtual", BaseClass will compile and run just fine, and you can even put some default behavior in there if you want, which will be overridden by the child classes.)

    Now, you can call derivedScriptRef.DoSomething();. If it's an instance of your base class (which is only possible if you used "virtual" above), nothing will happen. If it's DerivedClassA, the DoSomething from that class will run, etc.

    One additional note: GetComponent<BaseClass>, GetComponentsInChildren<BaseClass>, and all functions like that will return instances of the children as well.
     
    Chance-Touchstone likes this.
  3. Chance-Touchstone

    Chance-Touchstone

    Joined:
    Dec 26, 2013
    Posts:
    43
    That's exactly what I needed! - That example with the createdComponent helped me tie it all together. I was already using override but was missing the virtual declaration on my base class.

    Thank you so much for your time.