- Home /
Attach a non Monobehavior class to a GameObject
This is my issue:
I want to assign a script that inherits from an abstract class to a GameObject. I used to create MonoBehaviour scripts like "BirdScript" or "CarScript" but I want use proper classes and be able to control the gameobject the class is attached to.
Let me show you an example:
I have to following classes
 public abstract class Shape : MonoBehaviour {
    private int weight;
    private string color;
    protected int Weight
    {
        get { return weight; }
        set { weight = value; }
    }
    protected string Color
    {
        get { return color; }
        set { color = value; }
    }
    //Example abstract method
    public abstract void calculateVolume();
 }
 public class Cube : Shape {
    public void Move()
    {
        Vector3 pos = transform.position;
        pos.x = 2;
        transform.position = pos;
    }
    public override void calculateVolume()
    {
       //Just print a string
       MonoBehaviour.print("Cube volume");
    }
 }
I also have a prefab GameObject called MyCube which has the Cube class attached
Now I want to create an instance of MyCube from another GameObject which has the following script attached
 public class ShapeManager : MonoBehaviour
 {
    [SerializeField]
    public GameObject cube;
    // Use this for initialization
    void Start()
    {
        GameObject c = Instantiate(cube);
        c.GetComponent<Cube>().calculateVolume();
        c.GetComponent<Cube>().Move();
    }
    // Update is called once per frame
    void Update() {}
 }
The GameObject cube has the MyCube prefab attached using the Inspector.
My questions are:
What if Cube wouldn't inherit a Monobehavior class ?
Is this approach correct ?
Does it violate OOP ?
The way ShapeManager controls the cube is right ?
 GameObject c = Instantiate(cube);
 c.GetComponent<Cube>().calculateVolume();
 c.GetComponent<Cube>().Move();
My apologies for this long post :)
You can only attach $$anonymous$$onoBehaviour to a GameObject.
 You don't need put [SerializeField] in public fields.
 The way Shape$$anonymous$$anager controls the cube is correct but I recommend check if you have get the cube. You can use Shape class in Shape$$anonymous$$anager. 
GameObject c = Instantiate(cube);
Shape s=c.GetComponent<Shape>();
if(s){// chack if Shape exists
    s.calculateVolume();
    if(s is Cube){
        s.$$anonymous$$ove();
    }
}
Only $$anonymous$$onoBehaviour can be added to a object because internally a GameObject have a array/collection of Component, and $$anonymous$$onoBehaviour extend Component class.
You need extend $$anonymous$$onoBehaviour and can't extend Component directly because C# code in unity is treated as $$anonymous$$onoScript and the C++ part of unity will only understand a $$anonymous$$onoScript with a $$anonymous$$onoBehaviour or a ScriptableObject.
thank you for your answer @Sergio7888
Why should I get Shape and check if I get Cube ?
Wouldn't this be a one line solution ? Cube c = Instantiate(cube).GetComponent();
this way you can use any other Shape class derived object Like a Circle or Triangle or other shape you created in the same method and only call move if its is a cube because only cube has $$anonymous$$ove method.
What if Cube wouldn't inherit a $$anonymous$$onobehavior class ?
Then the script would not work because only $$anonymous$$onoBehaviour derived classes are allowed able to exist on a GameObject.
Is this approach correct ?
No, cause it does not work :)
Does it violate OOP ?
I would say it's legal to have an abstract class based on $$anonymous$$onoBehaviour. It's rather violating the "composition over inheritance" principle Unity's whole concept is based on 
thank you for your answer @hexagonius
I have this code working, why do you say it doesn't work ? $$anonymous$$aybe I did not explain the full issue.
If shape wasn't an abstract class, would it still be an ilegal implementation ?
Answer by Glurth · Oct 27, 2016 at 10:08 PM
If neither shape nor cube actually use any of the features of a MonoBehavior, like say... defining Update(), serializing data to be saved/loaded, or just accessing the gameObject member, then I would say you can certainly choose to NOT inherit from monobehavior.
However, the cube's move function, DOES access "transform", a property of the monobehavior.
Still, if not actually overriding Monobehavior functions, it's always possible to store a reference to a game object in the class itself (specified as a parameter to the constructor of the class), rather than derive from Monobehvior.
Is it correct? Well, as long as it works, it's just a mater of opinion. Using a reference to an object, rather than deriving from an unnecessary class- does not violate OOP.
I'm not quite sure what shape manager is supposed to do, but it looks like it will create a new object, and if that object has a cube compoent (that is derived from monobehavior), it will call move on it. If the object does not have a cube component it will generate a null exception. If cube is not derived from monobehavior, use of GetComponent will fail, or possibly not even compile.
Update/Edit: Here is an example of how I would do it using both classes derived from monobehavior, and a class NOT derived from monobehavior.
 abstract public class Candy : MonoBehaviour
 {
     public int pointValue;
     public float speed;
 
     abstract public void Move();
     abstract public bool MoveCondition();
 
     void Start() { }
     void Update() { if (MoveCondition()) Move(); }
 }
 public class LemonDrop : Candy
 {
     override public void Move() { /* manipulate member variable "transform" to drop down candy*/ }
     override public bool MoveCondition() { /* use gameObject to check for certain collisions */ }
 }
 public class CottonCandy : Candy
 {
     override public void Move() { /* manipulate member variable "transform" to float around*/ }
     override public bool MoveCondition() { return true; }
 }
 
 //usage
 
 static class CandyManager
 {
     public static List<Candy> allCandy=null; //note that this is "effectively" a list of gameobjects, that all have a candy component
 
     //dont let the <Tparam> throw you off, just think of it as a parameter that takes a "type"
     static void CreateCandy<Tparam>(Vector3 position) where Tparam : Candy
     {
         //create gameobject & add component (or load prefab with component)
         GameObject g = new GameObject("candy");
         g.transform.position = position;
         Candy c=null;
         c=g.AddComponent<Tparam>();
 
         //add it to allCandy List
         if (allCandy == null)
             allCandy = new List<Candy>();
         allCandy.Add(c);
     }
 
     static void InitLevel()
     {
         CreateCandy<LemonDrop>(Vector3.up);
         CreateCandy<LemonDrop>(Vector3.up+ Vector3.right);
         CreateCandy<CottonCandy>(Vector3.right);
         CreateCandy<CottonCandy>(Vector3.forward);
     }
 }
thank you for your answer @Glurth
$$anonymous$$aybe the Shape/Cube was not the best example I should have use for my question.
Suppose I have these classes
 public class Animal {
 }
 
 public class Dog : Animal {
     public void Walk(){}
 }
I want to attach the Dog class to a Dog GameObject.
If Animal does not inherit from $$anonymous$$onoBehaviour it wouldn't be possible to attach the Dog class to the Dog GameObject.
In the Shape/Cube example, this is the reason why Shape inherits from $$anonymous$$onoBehaviour.
As I said you cant attach non $$anonymous$$onoBehaviour classes, you still can use these classes as field in a $$anonymous$$onoBehaviour class if these classes are [Serializable], and if are not serializable still can be used in your code but can't be sarialized(saved) by the engine.
Lets start by defining what exactly you mean by attach: If you mean "attach" in the same way that a component is attached, then yes, you will need to derive from monobehavior.
However, if the only reason you are attaching to the object, is to get a reference to the object, I would suggest that you do not need to actually "attach" to the object. Rather, one simply stores a reference to the game object in ones class. Edit: while the does not violate Object oriented progra$$anonymous$$g, it might be considered as violating the "component based model" used so heavily by unity,
Here is an uncomplied example, pretty sure I messed up the exact syntax a bit...but should give you the idea.
 public class Animal
 {
   public GameObject referencedSceneObject;
   public Animal(GameObject refObj)
    {
       referencedSceneObject=refObj;
    }
 }
 class Dog:Animal
 {
    public void walk()
    {
       referencedSceneObject.transform.x+=1;
    }
 
 }
usage...
 GameObject c=Instantiate(Cube);
 Dog d= new Dog(c);
@Glurth You said: "However, if the only reason you are attaching to the object, is to get a reference to the object", but get a reference is not the only reason, save the values of the object is a reason, in you example you used a Animal with a GameObject reference, but the other possible field values will not be serialized(saved) without a custom code to serialize it or without putting the animal as a field in a $$anonymous$$onoBehaviour or ScriptableObject.
 For a question of praticity if you need keep the value data you have as option:
- use a $$anonymous$$onoBehaviour and put it in a object. 
- use a ScriptableObject and create a asset (readonly at runtime). 
- use a [Serializable] and use a custom code to serialize. 
- use a [Serializable] and use in a field of a $$anonymous$$onoBehaviour or ScriptableObject. 
Your answer
 
 
              koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                