Search Unity

Get a Particular script on an object?

Discussion in 'Editor & General Support' started by MosquitoByte, Dec 13, 2019.

  1. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    The plan is to create a turn-based RPG battle system, with enemies randomly instantiated from a list. I'm gonna set that up later, right now, i am building the battle systems basics. The plan is to have different enemies have different behavior patterns, such as wolves being more likely to attack, bats more likely to run, etc. In my head i have the idea of each enemy having a script named Unit, which defines basics like the enemy name, stats, etc. like in this video:
    And another script, named (Enemy name)Behavior, such as WolfBehavior. During the EnemyTurn() function, it would reference this behavior script for what to do. I'm trying to think of a way to set it up so that it gets the reference of this behavior script without me having to hardcode a reference, this way it can get the script from any enemy type, such as WolfBehavior, BatBehavior, SlimeBehavior etc. How would i go about doing this?
     
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Like in your other thread, use GetComponent.

    Usually goes something like this:

    Code (csharp):
    1. MyMonoBehaviourClass instanceOfMyMonoBehaviourClass = myGameObject.GetComponent<MyMonoBehaviourClass>();
    2. if (instanceOfMyMonoBehaviourClass != null)
    3. {
    4.     instanceOfMyMonoBehaviourClass.CallSomeMethod(someParameter);
    5. }
     
  3. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    im very confused about the example, what exactly is being done here?
     
  4. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    So in my fake example, I have a component MonoBehaviour script called MyMonoBehaviourClass attached to a GameObject. The variable with the reference to that GameObject is just called myGameObject.

    So I create a variable of type MyMonoBehaviourClass called instanceOfMyMonoBehaviourClass (I tried to make the made up names somewhat self explanitory, but you wouldn't normally name your stuff like this), and in the same line I call GetComponent for type MyMonoBehaviourClass on myGameObject.

    That should result in instanceOfMyMonoBehaviourClass now being a reference to the MyMonoBehaviourClass script attached to myGameObject. Because GetComponent will return null if there actually isn't a component of the correct type attached to that GameObject, I then check if it is null or not. If it is not null (meaning GetComponent was successful, as in you actually do have a component of the correct type attached to the object), then I call the CallSomeMethod method which is part of the MyMonoBehaviourClass.

    You can imagine that the MyMonoBehaviourClass would look something along these lines, if that helps:
    Code (csharp):
    1. public class MyMonoBehaviourClass : MonoBehaviour
    2. {
    3.     void Start()
    4.     {
    5.         Debug.Log("I started! yeah!");
    6.     }
    7.  
    8.     public void CallSomeMethod(bool imABool)
    9.     {
    10.         if (imABool)
    11.         {
    12.             Debug.Log("Yes I'm true!");
    13.         }
    14.         else
    15.         {
    16.             Debug.Log("The truth hurts, because I'm false!  I hate being false!");
    17.         }
    18.     }
    19. }
    Take a look at the scripting reference, specifically the second instance "generic version" which is how GetComponent is typically used.
    https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html
     
  5. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Okay, i think i understand, but wouldnt this mean i'd have to hardcode it to search for every potential "Behavior" script? IE getcomponent<WolfBehavior>, if null then getcomponent<BatBehavior>, etc.?
     
  6. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    You can create a common base class for all your different behaviours.

    Code (CSharp):
    1. // base class for all Unit Behaviours
    2. public abstract class UnitBehaviour : MonoBehaviour
    3. {
    4.  
    5. }
    6.  
    7. public class WolfBehavior : UnitBehaviour
    8. {
    9.  
    10. }
    Then you can use GetComponent with the base type, and it works for getting any of the extending classes.

    Code (CSharp):
    1. var behaviour = GetComponent<UnitBehaviour>();

    Edit
    : added missing class keyword
     
    Last edited: Dec 15, 2019
  7. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    hm, i didnt know you could use var in c#, also, what is an abstract? never seen it used before.
     
  8. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    Abstract basically means that the class is not yet complete and as such can't be used directly. For example you can't add a class that has the abstract keyword to a GameObject as a component. You need to extend the abstract class to complete it and create a usable class. It's good practice to add the abstract keyword to all classes that are just base classes for other classes.
     
  9. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    You can use var in C# when the type can be implicitly inferred from the declaration.

    This is not valid, because there's no way to figure out the type of the variable:

    Code (CSharp):
    1. var myString;
    But this is perfectly valid:

    Code (CSharp):
    1. var myString = "hello!";
    This is exactly the same as explicitly writing the type:

    Code (CSharp):
    1. string myString = "hello!";
    But it's quite handy when dealing with long type names.

    Code (CSharp):
    1. Dictionary<int, List<string>> myDictionary = new Dictionary<int, List<string>>();
    2.  
    3. var myDictionary = new Dictionary<int, List<string>>();
     
  10. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Thank you very much, i learned something new today.
     
    SisusCo likes this.
  11. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
  12. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    Oops, I forgot to add the class keyword in my example. It should be:

    Code (CSharp):
    1. public abstract class UnitBehaviour : MonoBehaviour
     
  13. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    one last thing, in this part:
    Code (CSharp):
    1.     var behaviour = GetComponent<UnitBehaviour>();
    2.  
    Since this line would be intended to get the behaviour, regardless of the name of the object, would it work to have this line read as:

    Code (CSharp):
    1.     var behaviour = GetComponent<(GameObject.name) + Behaviour>();
    2.  
     
  14. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    Yeah, you could also use the variant of GetComponent that takes the type name in string form.

    Code (CSharp):
    1. string behaviourTypeName =  name + "Behaviour";
    2. var behaviour = gameObject.GetComponent(behaviourTypeName) as UnitBehaviour;
    It returns a Component type result. You can cast it to UnitBehaviour to gain access to all the class members shared by all UnitBehaviours (if there are any).

    I don't see what the benefit using this method would have though, unless it is possible for a single GameObject to contain multiple UnitBehaviour components. By the way, if you don't want to allow multiple UnitBehaviours per GameObject, you can add the DisallowMultipleComponent attribute to the UnitBehaviour class.

    Code (CSharp):
    1. [DisallowMultipleComponent]
    2. public abstract class UnitBehaviour : MonoBehaviour
    3. {
    4. ...
     
  15. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Hm, interesting, placing the UnitBehavior script on the enemy isnt allowed. Gives the error: "Can't add script behaviour UnitBehavior. The script class can't be abstract!"
     
  16. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,326
    Yep, that is exactly what the abstract keyword is supposed to do. You can't add the abstract base class UnitBehaviour to any GameObjects, you can only add the classes that extend it (WolfBehavior, BatBehavior, SlimeBehavior...).

    Code (CSharp):
    1. // Extends UnitBehaviour.
    2. // This component can be added to GameObjects because it does not have the abstract keyword.
    3. public class WolfBehavior : UnitBehaviour
    4. {
    5.      ...
    6. }
    If you want to be able to add the base class UnitBehaviour to GameObjects, you can remove the abstract keyword from it.
     
  17. MosquitoByte

    MosquitoByte

    Joined:
    Sep 17, 2018
    Posts:
    213
    Ah, i see. thank you :)