Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Problems with small generic script

Discussion in 'Scripting' started by uCringe, Apr 20, 2021.

  1. uCringe

    uCringe

    Joined:
    Mar 24, 2021
    Posts:
    33
    Hi,

    I was trying to write a small script that I can attach to a generic trigger, so it can be used anywhere to change an int value in whatever script referenced in the inspector of the trigger gameobject. I'm new to Unity but have worked with other games and being able to effortlessly set some status value on an object's attached script was central to my workflow. With a generic script and trigger I would not have to write new ones every time.

    So I'm trying to turn the use of "Scriptname.variable = value;" into public inspector fields.


    Code (CSharp):
    1. public class TriggerSetInt : MonoBehaviour
    2. {
    3.     public string scriptName;
    4.     public string intName;
    5.     public int value;
    6.  
    7.     private void OnTriggerEnter2D(Collider2D collision)
    8.     {
    9.         if(collision.gameObject.tag == "Player")
    10.         {
    11.             Component pScript = collision.gameObject.GetComponent(scriptName) as Component;
    12.             if(pScript != null)
    13.             {
    14.                 pScript.intName = value;
    15.             }
    16.         }
    17.     }
    18. }
    But in the last line intName returns an error - I suppose because neither the script nor the int name exist yet. They'll be dragged into the inspector slots when needed. Can this be fixed?

    Cheers.
     
  2. sasuchi

    sasuchi

    Joined:
    Jun 18, 2019
    Posts:
    13
    You declare the variable pScript as a Component.
    In line 14 you say, get me the variable intName of Component.
    The Component Class does not have a variable/field called intName.

    You probably could achieve somewhat you goal using reflection to get the fieldname at runtime... but i would suggest simply a interface with a property. let each necessary gameobject implement that interface and ontrigger access that property...

    Something along those lines:

    Code (CSharp):
    1. public class TriggerSetInt : MonoBehaviour
    2. {
    3.     public int value;
    4.  
    5.     private void OnTriggerEnter2D(Collider2D collision)
    6.     {
    7.         if (collision.gameObject.tag == "Player")
    8.         {
    9.             ISetInit pScript = collision.gameObject.GetComponent<ISetInit>();
    10.             if (pScript != null)
    11.             {
    12.                 pScript.MyIntToSet = value;
    13.             }
    14.         }
    15.     }
    16. }
    17.  
    18. public interface ISetInit
    19. {
    20.     int MyIntToSet{ get; set; }
    21. }
    22.  
    23. public class TestClass : MonoBehaviour, ISetInit
    24. {
    25.     public int MyIntToSet { get; set; }
    26. }
     
    Last edited: Apr 20, 2021
    uCringe likes this.
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,741
    In order to do this, you'd have to use Reflection to find the property with the name of whatever intName says. Right now, the compiler is expecting that the Component class has a property literally called "intName" that you want to be modifying. If you want to go this route, there is some reflection sample code here.

    That said, you probably shouldn't. There are many better ways to make something that is generic and reusable, and Reflection is generally a means of last resort, and usually not appropriate for runtime code due to performance and reliability. (that is, if you make a typo in either your class or variable name, you won't get any errors when you build and would have to actually playtest your whole game to find these errors. That's a HUGE issue as your project grows - not ideal for a generic, reusable script.

    There are better options, like an interface.

    Here's an example:
    Code (csharp):
    1. public interface IValueSetter {
    2.     void SetInt(int i);
    3. }
    4.  
    5.  
    6. public class TriggerSetInt : MonoBehaviour
    7.  {
    8.     public int value;
    9.     private void OnTriggerEnter2D(Collider2D collision)
    10.     {
    11.         if(collision.gameObject.tag == "Player")
    12.         {
    13.             IValueSetter setter = collision.gameObject.GetComponent<IValueSetter>();
    14.             if(setter != null)
    15.             {
    16.                 setter.SetInt(value);
    17.             }
    18.         }
    19.     }
    20. // on your "receiving" script
    21. public class PlayerScript : MonoBehaviour, IValueSetter
    22. {
    23.     public void SetInt(int i) {
    24.         life = i;
    25.     }
    26. }
     
    stain2319 and uCringe like this.
  4. uCringe

    uCringe

    Joined:
    Mar 24, 2021
    Posts:
    33
    Thanks to both of you, I will try them both.
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I would approach this by creating 1 script which is attached to any object which you care about entering the trigger. Then you'd just GetComponent that 1 script. Any other script which needs the value of the fields on that script just references it.
     
    uCringe, stain2319 and Kurt-Dekker like this.