Search Unity

Inheritance and Update()/Start()/Awake()

Discussion in 'Scripting' started by Antoids, Apr 6, 2018.

Thread Status:
Not open for further replies.
  1. Antoids

    Antoids

    Joined:
    Apr 11, 2015
    Posts:
    12
    When experimenting with script inheritance I noticed the following behavior:

    Code (CSharp):
    1. public class Base : Monobehaviour {
    2.  
    3.     void Start(){
    4.     Debug.Log("0");
    5.     }
    6. }
    7.  
    8. public class Inherit : Base {
    9.  
    10.     void Start(){
    11.     Debug.Log("1");
    12.     }
    13. }
    14.  
    15. public class LastInherit : Inherit {
    16.  
    17.     void Start(){
    18.     Debug.Log("2");
    19.     }
    20. }
    If these three are separate scripts, and you attach LastInherit to a GameObject, the only output will be "2". This kind of makes sense since that's the script running, but I would also think that due to inheritance, all three should be included with LastInherit. Is there a way to not make LastInherit's Start() supercede all others? Is there a way to yield "0", "1", and "2" as the output without attaching all three scripts to the same GameObject? I would think attaching all three scripts would somewhat defeat the purpose of inheritance.
     
    Last edited: Apr 6, 2018
    Rs likes this.
  2. Pagefile

    Pagefile

    Joined:
    Feb 10, 2013
    Posts:
    51
    Declare Start() as virtual protected (or public), and in the child classes override it and call base.Start()
    Code (csharp):
    1. class BaseClass : Monobehaviour
    2. {
    3.     public virtual Start()
    4.     {
    5.         Debug.Log("BaseClass.Start()");
    6.     }
    7. }
    8.  
    9. class ChildClass : BaseClass
    10. {
    11.     public override Start()
    12.     {
    13.         base.Start();
    14.         Debug.Log("ChildClass.Start()");
    15.     }
    16. }
     
    nessylum, Bunny83, pizzadev69 and 2 others like this.
  3. Antoids

    Antoids

    Joined:
    Apr 11, 2015
    Posts:
    12
    Thank you for the help. It pointed me in the right direction. However, some small alterations led me to something more like this:

    Code (CSharp):
    1. public class Base : MonoBehaviour {
    2.  
    3.     public static void Start() {
    4.         Debug.Log("base");
    5.     }
    6. }
    7.  
    8. public class Child : Base {
    9.  
    10.     public void Start() {
    11.         Base.Start();
    12.         Debug.Log("1");
    13.     }
    14. }
    Using the version you posted (after adding void before both Start()s), threw an error that it required an object reference for Base.Start(), since it was non-static. I see no reason not to make it static, however, so I did so.
     
  4. Static is different.What you're looking for is:
    Code (CSharp):
    1. public class BaseClass : MonoBehaviour {
    2.  
    3.     public virtual void Start() {
    4.         Debug.Log("baseClass");
    5.     }
    6. }
    7.  
    8. public class Child : BaseClass {
    9.  
    10.     public override void Start() {
    11.         base.Start();
    12.         Debug.Log("1");
    13.     }
    14. }
    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/base
     
    Last edited by a moderator: Apr 6, 2018
    Bunny83 and Eristen like this.
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You probably got confused because your class was named 'Base' where 'base' is a keyword inside the class, meaning its parent.. :)
    So, if you typed "Base.Start" you would have that msg.

    Notice the casing of the letter 'b'.
     
    Bunny83, bezier and AbleArcher like this.
  6. sitnduck

    sitnduck

    Joined:
    Jun 29, 2016
    Posts:
    15
    If anyone from the future ever wonders: the issue with the initial post by Pagefile is that it was missing "void" in the method definitions.
     
    Ishkur, Eristen, Toke and 1 other person like this.
  7. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    Thank you, from the future.
     
  8. pizzadev69

    pizzadev69

    Joined:
    Jan 10, 2016
    Posts:
    24

    This works but I'm facing an issue:

    I have a class called "Animal" and 2 child classes (Dog and Cat)
    when I call base.Awake() in both of them, Animal's Awake() gets called twice
     
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,004
    Unnecessary necro post. Should be settled over here as it's about a misunderstanding. We expect it to be called twice.
     
  10. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I personally use a coding structure that I call "Static Inheritance", as all class references are static, and each class is inherited from the Main(which I call Slave.cs).

    Code (CSharp):
    1. public class Slave : MonoBehaviour
    2. {
    3.    public static List<Animals> allDogs = new List<Animals>();
    4.    public static List<Animals> allCats = new List<Animals>();
    5.    // etc...
    6. }
    So then, you can easily create a function within Slave.cs, especially with Object Pooling, to reference or even iterate through the particular List you need. Which I've benchmarked to be way more performant when sorting calls and references.

    The problem you mention, is a problem I once had when perfecting said code structure. The parent class, unless placed in an object in the scene, will not "perform" any code on it's own. Which I found, through many hardships along the way, is a very good thing. Because in all intents and purposes, you don't want the parent class to read during runtime, only each child which is meant to be running within the scene.

    So, ergo, the parent class doesn't need an Awake(), Start(), or even Update(). The parent class only needs to contain variables(public) that each child class would use, that can be called in a generalized function that would be called from elsewhere. Or as you'll find when playing around with Inheritance, the parent also can contain functions that either said child would use, or not use(which is main argument on why some say Inheritance is bad).

    Now I do notice, that in your example, you show class01 > class02 > class03. Which is fine as long as class01 and class02 do not contain structured voids(Awake, Start, etc...), then using structured voids within class03 is perfectly fine. However, if you need a particular(parent) class to have it's own Awake()? The best way I found was something like this:

    Code (CSharp):
    1. private void Awake()
    2.     {
    3.         if ([classReference] == null && this.name == "[className]")
    4.         {
    5.             [classReference] = this;
    6.             DontDestroyOnLoad([classReference].gameObject);
    7.         }
    8.     }
    To which I only needed in one of my projects. For the most part, each structured parent class is to only group and define certain classes, or allow certain functions to be called in each group. I will show an example:

    Code (CSharp):
    1. public class Animals : Slave
    2. {
    3.    public bool action;
    4.  
    5.    public void Bark()
    6.    { playSound(Bark001.wmv); }
    7.  
    8.    public void Meow()
    9.    { playSound(Meow007.wmv); }
    10. }
    Code (CSharp):
    1. public class Cat : Animals
    2. {
    3.    void Awake() { allCats.Add(this); }
    4.  
    5.    void Update()
    6.    {
    7.       if (action)
    8.       { Meow(); action = false; }
    9.    }
    10. }
    Code (CSharp):
    1. public class Dog : Animals
    2. {
    3.    void Awake() { allDogs.Add(this); }
    4.  
    5.    void Update()
    6.    {
    7.       if (action)
    8.       { Bark(); action = false; }
    9.    }
    10. }
    There are many more ways to use Inheritance properly, and ways that solve many issues with other forms of code structure, but I feel like I've typed enough for now, lol. I hope anything I've said helps someone in the future, or better yet gave them a spark for a new idea! Cheers!
     
  11. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    In Unity in place of inheritance, I prefer to use prefabs and variants. So I can have Prefab Animal, and from it, I can make Variant Dog, Cat, Bird, etc. How? Just by adding an additional component on the earlier prefab. There is no problem to make reference between Animal component and Dog, Cat, Bird component, because you have one object the glue it all - GameObject.
     
  12. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    GameObject is very limited, as you cannot get said class components without calling GetComponent(), which is very taxing and slow in the view of performance.

    Now to explain further, the class of Dog and Cat are prefabs in my structure. And to get their class(script component) how? By simply iterating through the static List of said reference, which is way faster than using GetComponent() and especially Interfaces. Now one thing you mention that I agree with, GameObject glues it all together, and was the best way I found when using Object Pooling:

    In Slave.cs:
    Code (CSharp):
    1. public Animal GetFromList(List<Animal> list, Vector3 position, GameObject prefab)
    2.     {
    3.         if (list.Count > 0)
    4.         {
    5.             for (int i = 0; i < list.Count; i++)
    6.             {
    7.                 if (!list[i].gameObject.activeSelf)
    8.                 {
    9.                     list[i].trans.position = position;
    10.                     list[i].gameObject.SetActive(true);
    11.                     return list[i];
    12.                 }
    13.             }
    14.         }
    15.         Instantiate(prefab, position, Quaternion.identity);
    16.         return list[list.Count - 1];
    17.     }
    Call for get or instantiate:
    Code (CSharp):
    1. public class Spawner : Slave
    2. {
    3.     void SpawnDog(Vector3 position)
    4.     {
    5.         Dog dog = (Dog)GetFromList(allDogs, position, dogPrefab);
    6.         dog.scale = 1.25f;
    7.         dog.soundVolume = 0.75f;
    8.         dog.movementSpeed = 0.5f;
    9.         // etc...
    10.     }
    11. }
    Call for get script during runtime:
    Code (CSharp):
    1. public class Player : Slave
    2. {
    3.     Dog currentDog;
    4.  
    5.     void OnCollisionEnter(Collision coll)
    6.     {
    7.         if (coll.gameObject.layer == dogLayer)
    8.         {
    9.             for(int i = 0; i < allDogs.Count; i++)
    10.             {
    11.                 if (allDogs[i].gameObject == coll.gameObject)
    12.                 { currentDog = allDogs[i]; currentDog.Bark(); return; }
    13.             }
    14.         }
    15.     }
    16. }
    To which is the biggest problem when making any game, getting the current GameObjects script. Now my example is poorly written, as I just drew it up on the fly, but even still, is far more performant than other "beginner" methods. But I will agree, GameObject is a very important and needed class, as you notice in the GetFromList() and also the layer reference. But when it comes to using GameObject, especially when defining which script you are looking for, it falls short on it's face.
     
    Last edited: Jul 25, 2023
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    This is like complaining that oxygen falls short on its face.

    The entire UnityEngine infrastructure builds on GameObjects.

    Any subjective observation about them is completely irrelevant. Don't waste your time.

    Every object that Unity returns, GameObject or Component or otherwise, can be used by you to track it.

    For instance, if every Enemy GameObject has an EnemyController on it, keep the reference to the EnemyController so you don't have to go digging for it.

    Keep in mind that using GetComponent<T>() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

    This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

    If you run into an issue with any of these calls, start with the documentation to understand why.

    There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

    In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

    It is ALWAYS better to go The Unity Way(tm) and make dedicated public fields and drag in the references you want.

    EDIT: also just noticed this is a necro-post. Don't do that, it's against forum rules for a good reason.

    If you have an actual problem, make a new post. It's FREE!

    When you make your new post, here is how to report your problem productively in the Unity3D forums:

    http://plbm.com/?p=220

    This is the bare minimum of information to report:

    - what you want
    - what you tried
    - what you expected to happen
    - what actually happened, log output, variable values, and especially any errors you see
    - links to documentation you used to cross-check your work (CRITICAL!!!)

    The purpose of YOU providing links is to make our job easier, while simultaneously showing us that you actually put effort into the process. If you haven't put effort into finding the documentation, why should we bother putting effort into replying?

    Do not TALK about code without posting it. Do NOT retype code. Copy/paste and post code properly. ONLY post the relevant code, and then refer to it in your discussion.

    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/
     
    Last edited: Jul 23, 2023
  14. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I severely apologize if I went against any rules, that was not my intent. I noticed a issue I was having myself led me to this post, and like any other future person, if in the same spot, would appreciate any relevant information on the matter.

    True, I probably went down my own rabbit hole, in trying to explain myself or in this case the benefits from the aforementioned code structure. So again, I apologize.

    My intent was in reference to the post, and my own experience with using Inheritance, and especially if one didn't want the parent scripts running within the game themselves, in order to need to use a virtual override. Nor was my intent malicious in any way, as the code structure examples I posted is the same within 12 of my current projects, and I've personally had no issues.

    I understand fully on using links, and factual references for any statements, as this would make a statement legitimate. However, there are none, as any reference to what I referenced would have been created by me. I have searched to see if anyone uses a "Static inheritance" like method, to speed my process along of figuring it out, and to no avail there isn't anything close.

    If you will concur that my posts are irrelevant, I will personally delete them no problem.

    However, I must ask, you mentioned that the list order is never guaranteed, and that I may personally be getting different results inclusive of the snippet:


    Code (CSharp):
    1. Instantiate(prefab, position, Quaternion.identity);
    2. return list[list.Count - 1];
    To which I am most curious. As I have found that upon an Instantiate() call, that the class being "awoke" reads it's Awake() method, before the original call then reads its next line. Are you saying that this always isn't case, per operating system or version of Unity? I would be greatly appreciative of any further insight on that particular matter. As it would mean I need to completely re-work my structure. Cheers!
     
  15. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,203
    He's just cautioning against trying to reinvent the wheel when Unity is designed to be used in a specific way as it can lead to extremely difficult to find bugs.

    Correct, and you can find it stated as much in the docs entry for that method.

    https://docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html
     
    wideeyenow_unity likes this.
  16. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,004
    Well, it's also mentioned in the documentation, only active gameobjects get their Awake method called. When you design a prefab that is deactivated by default, when you instantiate such a prefab, Awake would not be called. It would be called the moment you set it active for the first time.

    I haven't read all of your code, but I don't quite get all your static variable business. Static variables make things a lot harder and more difficult to debug. That's why static variables (global state) considered code smell or at least an anti pattern when used extensively. There's always a bit of global state that you can't avoid, however you usually want to keep it at a minimum. In small scale applications that's not that important. However if you work on a bigger project and potentially with other people it gets more and more important to stick to some design principles which actually makes your lives easier.

    I'm also not sure why you think that holding your own static list of objects would be magically faster than just using GetComponent on the actual reference you already have. Have you actually used the profiler? Your approach doesn't really scale well. When you have 200+ objects you would iterate through your whole list every time. GetComponent does also just iterate through the list of components on the gameobject. Bookkeeping with such long lists is also quite taxing when you add / remove many objects quite frequently.


    @Kurt-Dekker What exactly do you mean by that? Do you talk about GetComponent(s)? Actually the order is guaranteed since we got the AudioFilters years ago AFAIK. Several systems in Unity now depend on the order in the inspector. Since then we actually have the sibling index and we can explicitly set it. Newly added components at runtime are always added at the end by default.
     
  17. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Edit: I use static lists of classes/instances, not gameObjects*

    I use profiler, and System.Diagnostics.Stopwatch, that's why I'm confident in my structure. It scales perfectly fine, as in the beginning I used
    Code (CSharp):
    1. gameObject.GetComponent<itScript>()
    and ran 100-200 free-thinking objects(peasants) that would manage their own time/choices within towns, and trying to hold a steady 60fps while in the editor was a joke. This led me down the rabbit hole of becoming a performance nut. After that conundrum, I named each peon their index# in the list they were in, which string parsing was a little heavy, but was twice as fast as GetComponent() was(now 200-300peons @30-45fps). Then eventually came to discover singletons are awesome, then mutating that into a static list of classes(instances), and iterating through their list and checking against the gameObject for correlation(now 400peons @ 60fps).

    And also the map is 100x100 tiles, so 10,000 instances, and iterating through them at runtime to modify mesh or material is a breeze.

    I had made other performance modifications, as noted in https://docs.unity3d.com/2019.3/Doc...imizationPracticalScriptingOptimizations.html, and cached things like
    Code (CSharp):
    1. Transform trans = transform;
    . As you will notice in the copy/pasted code for GetFromList() actually shows
    Code (CSharp):
    1. list[i].trans.position = position;
    , which I forgot to edit, lol. As that page mentions:
    That could be slow, if there are enough of them running at the same time. Little known fact: all of the component accessors in MonoBehaviour, things like transform, renderer, and audio, are equivalent to their GetComponent(Transform) counterparts, and they are actually a bit slow. GameObject.FindWithTag has been optimized, but in some cases, for example, in inner loops, or on scripts that run on a lot of instances, this script might be a bit slow.
    So it is well known, using
    Code (CSharp):
    1. GetComponent()
    at runtime is very performance heavy, to which I mentioned it falls flat on it's face.

    And I haven't had any issues debugging, to which I am a singular dev, any new function or method I experiment with get's rigorously tested, even in worst case scenarios. And if I ever feel/think something could mess up somewhere, I simply put
    Code (CSharp):
    1. print($"object{index} not found in CallAllToHouse()");
    or something of the sort.

    But I rarely get any errors, since the structure is very easy to understand, and way less chances at typos. Most of my functions are general in a sense, so if an error does happen, without even reading it I know it was the last thing I did.

    But as Kurt replied the way he did, I noticed I may have come off as haughty, or a know it all. This was purely not my intent, I was trying to share my knowledge of something I know works very well. If I happened to shame anyone with my words, I will delete those posts and personally apologize.
     
  18. PaulEmbleton

    PaulEmbleton

    Joined:
    Aug 3, 2021
    Posts:
    3
    Awake is called when the script instance is being loaded, which happens when the game object is instantiated and regardless of whether the game object is active. I think you mean Start, which gets called when the game object is first made active.

    Awake is important when lots of game objects are instantiated at same time, like at start of game, where the engine calls all Awakes before calling any Starts.
     
  19. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,331
    Nope.
     
Thread Status:
Not open for further replies.