Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Interacting w/ GameObjects not using Monobehaviour[SOLVED]

Discussion in 'Scripting' started by BrodiMAN, Aug 18, 2017.

  1. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    Hello All,

    I am attempting to make a Master Class for all of the enemy ships in my game. Since the ships share basic characteristics, it'd make more sense to have them inherit those functions instead of rewriting them each time. So I've created a basic script. However I have 2 problems.

    1) Since C#/Unity does not support multiple inheritance, if I want to extend a class, I can't extend Monobehaviour. How then do I access a gameObject the script is attached to?

    Here is an example script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyCollisionDetection : EnemyCollisionBehavior
    6. {  
    7.     private int value;
    8.  
    9.     private void Awake()
    10.     {
    11.         value = 1;
    12.     }
    13.  
    14.     void OnCollisionEnter(Collision collision)
    15.     {
    16.         CollisionDetected(gameObject, collision, value);
    17.     }  
    18. }
    As you can see, I want to pass the gameObject, the thing that collided with it, and its value to an inherited function. But, Unity won't let me call "gameObject" without Monobehaviour. Is there a way around this?

    2) I want the inherited class to instantiate a prefab whenever one of the other scripts calls it. However, this class does not have Monobehaviour because it won't be attached to an object. Since it doesn't have Monobehaviour, I do not have the option to drag-and-drop a prefab onto it to be instantiated during play.

    Code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyCollisionBehavior
    6. {
    7.     public GameObject explosionAnimation;
    8.     private static GameObject sExplosionAnimation;
    9.  
    10.     private void Awake()
    11.     {
    12.         sExplosionAnimation = explosionAnimation;
    13.     }
    14.  
    15.     public static void CollisionDetected(GameObject callingObject, Collision collidingObject, int value)
    16.     {
    17.         if (collidingObject.gameObject.tag == "Laser" || collidingObject.gameObject.tag == "DoubleLaser" || collidingObject.gameObject.tag == "TripleLaser"
    18.             || collidingObject.gameObject.tag == "Rocket" || collidingObject.gameObject.tag == "Player")
    19.         {
    20.             AudioController.Explosion();
    21.             Object.Instantiate(sExplosionAnimation, callingObject.transform.position, callingObject.transform.rotation);
    22.             GameController.IncreaseEnemyKillCount();
    23.             GameController.DecreaseEnemyCount();
    24.             GameController.IncreasePlayerScore(value);
    25.             Object.Destroy(callingObject.gameObject);
    26.         }
    27.  
    28.         else if (collidingObject.gameObject.tag != "Boundary")
    29.             Physics.IgnoreCollision(collidingObject.collider, callingObject.GetComponent<Collider>());
    30.     }
    31. }
    How do I solve this?

    -------------------------------------------------------------------------------------------------------
    Clearly, I don't understand Monobehaviour. Any advice is greatly appreciated.

    Thank you!
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    So, why can't EnemyCollisionBehavior inherit from monobehavior?
     
    LiterallyJeff likes this.
  3. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    I was under the impression that for a script to exist during runtime without being attached to an object it'd have to not extend Monobehaviour.

    Is that an incorrect assumption? I know nothing of Monobehaviour.
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    That is correct, but you do seem to need it to be attached to an object because you're using object lifecycle functions like Awake and other MonoBehaviour functions for gameobjects, detecting collisions etc.

    Make EnemyCollisionBehavior inherit from MonoBehaviour, then add the EnemyCollisionDetection component to the object detecting the collisions.
     
    BrodiMAN likes this.
  5. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    Well, the thing is, you have an Awake in that script, which if you want that to run, you have to inherit from Monobehavior anyways. I may be confused on what you are trying to do with these two scripts. I mean, I get the basic idea that you want a base script for your ships and then a class to inherit from them, but what is attached to your ships?

    @jeffreyschoch beat me to part of my point. But I also wonder if you could have a different script setup.
     
    BrodiMAN likes this.
  6. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    Good points, both of you.

    For clarification, EnemyCollisionDetection is attached the the enemy ship object. EnemyCollisionBehavior is what I want as the "Master Class" that all enemy ships will inherit.

    Imagine that EnemyCollisionBehavior does not have the Awake() function. And EnemyCollisionDetection remains the same. Like so:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyCollisionBehavior
    6. {
    7.     public GameObject explosionAnimation;
    8.  
    9.     public void CollisionDetected(GameObject callingObject, Collision collidingObject, int value)
    10.     {
    11.         if (collidingObject.gameObject.tag == "Laser" || collidingObject.gameObject.tag == "DoubleLaser" || collidingObject.gameObject.tag == "TripleLaser"
    12.             || collidingObject.gameObject.tag == "Rocket" || collidingObject.gameObject.tag == "Player")
    13.         {
    14.             AudioController.Explosion();
    15.             Object.Instantiate(explosionAnimation, callingObject.transform.position, callingObject.transform.rotation);
    16.             GameController.IncreaseEnemyKillCount();
    17.             GameController.DecreaseEnemyCount();
    18.             GameController.IncreasePlayerScore(value);
    19.             Object.Destroy(callingObject.gameObject);
    20.         }
    21.  
    22.         else if (collidingObject.gameObject.tag != "Boundary")
    23.             Physics.IgnoreCollision(collidingObject.collider, callingObject.GetComponent<Collider>());
    24.     }
    25. }
    How then can I access "gameObject" in EnemyCollisionDetection without Monobehaviour? And how can I add the explosionAnimation to EnemyCollisionBehavior without Monobehaviour?

    Is that not possible? Or am I thinking incorrectly on what Monobehaviour does? Or is this just a bad setup all together?
     
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    It sounds like you want EnemyCollisionDetection to inherit from Monobehavior and your EnemyCollsionBehavior to be more of a manager type class. You can't attach a script to a gameobject without it (or a base class it inherits from) inheriting from monobehavior.

    So either you have a base class that inherits from monobehavior that your other classes inherit from, or you have a manager class that handles things for you (which can inherit from monobehavior) and interact with your scene while your ships just have their own script that accessing the manager. (these also inherit from monobehavior).

    I think the other part of my confusion is what you are trying to use inheritance for. Do your child classes have something that makes them different from each other? I mean, other then values like health, damage, etc which you can set values for, but something that actually makes a need for the different scripts per ship?
     
    BrodiMAN likes this.
  8. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Yes :)

    What are you trying to gain with this type of structure? If a ship needs to exhibit a certain type of behavior based on collisions then that behavior should be defined as a MonoBehaviour attached to it. You would only need to define the unique sets of behavior and then attach the appropriate one to each ship. Common behaviour could also be abstracted into a common base class.
     
    BrodiMAN likes this.
  9. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    Yes, this is what I'm trying to achieve. However, if the managing class (EnemyCollsionBehavior) inherits Monobehaviour, it has to be attached to an object. Correct?

    That's what I'm trying to avoid, as I don't want a bunch of classes attached to an empty object. That just seems like bad design to me. Or am I wrong on that?

    Yes.

    Aside from differing health points and values, they have different movement patterns, weapon fire rates, behaviors when encountering different triggers in the game world, etc, etc, etc.

    My thought process was basically "If the ships have any similar characteristics, just put that all in one class instead of re-writing it for however many ships I may end up creating". I was hoping to have a managing class to handle all shared characteristics to save time and allow for future growth in the game as I create many random ships for whatever reasons I may have.
     
  10. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    See my answer to Brathnann above.

    I was hoping to have a class handle all the shared behaviors between the ships that I could simply extend to them. Removing the need to re-write code for however many ships I would end up having.

    My concept of "Monobehaviour" is very very limited. Could you please explain what you mean by "defined as a MonoBehaviour"?

    Does Monobehaviour remove the need for inheriting classes in Unity? What's the best way to use inheritance in Unity that doesn't require me creating an empty object to store all of my managing classes in?
     
  11. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Not necessarily. If it's just meant to contain common logic then it can be abstract and your specific behaviours can inherit from it. Those specific behaviours are what get attached to each ship.

    I'm still not sure why you think you need to do this?
     
    BrodiMAN likes this.
  12. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    That's just it. If you want a class with shared stuff, it should follow the pattern of

    baseClass : Monobehavior
    anotherClass : baseClass
    evenAnotherClass : baseClass

    That's the way you should do it and then you should be able to attach anotherClass and evenAnotherClass to your ships.

    There isn't anything wrong with manager classes attached to an empty gameobject. It's pretty much the point. You may do that for several reasons, including having it hold references to objects in the scene that you can access from other scripts.

    Also to add, you may just need an interface for your ship scripts instead of inheritance.
     
    BrodiMAN likes this.
  13. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    Isn't that the idea of inheritance?

    I've always coded with that concept in mind in every project I've been involved in. I try to reduce the amount of repeat code as much as possible. IMO, it reduces complexity, saves time, and simply looks cleaner.

    If I want to make classes called Apple Pie and Blueberry Pie, I don't want to have to define Pie in both of them. Just make a Pie class and extend it to them.

    Again, if you haven't guessed it by my confusing questions above, I'm new to Unity and C#. I welcome any knowledge of design patterns and concepts you are willing to throw at me. I greatly appreciate the learning experience. :D
     
  14. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    Ok, that makes sense. I will give that a try and see what comes of it.

    Question though, is Monobehaviour a strict necessity in any lineage of code in Unity? Is there any effective way to not use Monobehaviour (inherited or otherwise) and still be able to interact with gameObjects?
     
  15. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    You only need to inherit from MonoBehaviour if you want that class, or any subclass to be able to be added as a component, and have access to the wide array of functionality MonoBehaviour brings.

    An example of not using MonoBehaviour is to make a static class with utility functions. As long as you have "using UnityEngine" at the top, you can interface with GameObjects passed in as parameters, find GameObjects in the scene, Instantiate objects, etc. using static class functions like GameObject.Find (...dont use this), and Object.Instantiate, etc.

    Personally almost every single class I write derives from MonoBehaviour at some point up the chain. Non-MonoBehaviours are the minority by far, but include scriptable objects, editor scripts, extension methods, plugins. Honestly MonoBehaviours just make life a lot easier inside Unity. It just sucks they spelled Behavior wrong ;)

    Any class can interact with a GameObject type object. MonoBehaviour has a built-in "gameObject" variable that references the GameObject that this component instance is attached to.
     
    Last edited: Aug 18, 2017
    BrodiMAN likes this.
  16. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    That's correct - it just sounded like you thought you were going to have to do the opposite of what you just said. :)
     
  17. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    My apologies for being terrible at explaining simple things :D
     
  18. BrodiMAN

    BrodiMAN

    Joined:
    Feb 2, 2016
    Posts:
    82
    This thread has been a wealth of information/knowledge. Thank you guys for answering my questions.

    I just tried Brathnann's suggestion. Extended Monobehaviour to my managing class, then extending that class to the classes attached to the ships. Works just fine.

    I still need to bone up on Monobehaviour and it's functions, but I'll do that in my own time.

    Again, thank you dudes for the help!
     
    LiterallyJeff likes this.
  19. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    I never even gave this a look. So I had to check into it. Behavior is preferred by the US, but behaviour is apparently preferred everywhere else.

    @BrodiMAN Glad I could be of help!