Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Invoke Method Script

Discussion in 'Scripting' started by AirborneFluff, Jan 7, 2022.

  1. AirborneFluff

    AirborneFluff

    Joined:
    Oct 9, 2021
    Posts:
    5
    I have a simple script which takes a reference to a script and method name. When it is triggered (say, OnTriggerEnter) it Invokes the method within the GameObjects code like so:
    Code (CSharp):
    1. ScriptToInvoke.Invoke(MethodName, InvokeDelay);
    I now want to be able to invoke methods which have additional arguments, such as:
    Code (CSharp):
    1. methodInfo = ScriptToInvoke.GetType().GetMethod(MethodName);
    2.             methodInfo.Invoke(ScriptToInvoke, Arguments);
    (Where Arguments is an object array)

    Now, I have been playing around with the inspector editor, but I cannot get the editor to store the values I give it. Everytime I re-compile the game, or reload unity the values disapear. This is because unity will not serialize type object[].

    Any idea of how I can store an array of objects (string, float, bool, vector3 ect...) and have them be used within my code? Cheers
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,369
    Probably best to stop using Invoke and start using something like interfaces.

    Using Interfaces in Unity3D:

    https://forum.unity.com/threads/how...rereceiver-error-message.920801/#post-6028457

    https://forum.unity.com/threads/manager-classes.965609/#post-6286820

    Check Youtube for other tutorials about interfaces and working in Unity3D. It's a pretty powerful combination. With that you can construct target calls that take any number of arguments.

    However, once you get above 2 or 3 arguments, it's probably best to put all the data into a public class to hold it as a single passed object, otherwise code gets hairy.

    As for doing something later, I always use my CallAfterDelay class for delayed action.

    https://gist.github.com/kurtdekker/0da9a9721c15bd3af1d2ced0a367e24e

    See usage notes at bottom below gist code.
     
    Bunny83, Vryken and StarManta like this.
  3. AirborneFluff

    AirborneFluff

    Joined:
    Oct 9, 2021
    Posts:
    5
    Thanks for the reply! I have used interfaces before, but as good as they are, they don't have the simplicity I want. Give you an example. I have a Door class. In here, I've got "OpenDoor", "CloseDoor", "SlamDoor". It would be lovely if I can just attach a script to a gameobject which has a trigger collider. This script would require just simply the door gameobject the method name (and any arguments).
    This would be very flexible. I could invoke any method from any gameobject with a simple UI.
    Here's my version of the idea at the moment, and it works amazingly. It's just, I want a way to add arguments
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,369
  5. AirborneFluff

    AirborneFluff

    Joined:
    Oct 9, 2021
    Posts:
    5
    That is... Perfect...! Thanks so much. I will get back to you if this works out! :D
     
  6. AirborneFluff

    AirborneFluff

    Joined:
    Oct 9, 2021
    Posts:
    5
    Ahh, I'm back to the same issue. So that method SendMessage takes an 'object' as a parameter. The problem is that you cannot serialize an object. The inspector doesn't show that field
     
  7. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    Maybe it's also worth looking into UnityEvents for a way to assign callback methods on scripts from the inspector.

    As an example, the UI Button component has "OnClick" section in its inspector where you assign callback methods that are invoked when the button is clicked - that is a UnityEvent.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,369
    This is just not correct. Are you decorating the thing properly? Remember
    object
    just means "typeless," a thing that can point at anything.

    If you make a serializable class like so:

    Code (csharp):
    1. [System.Serializable]
    2. public class MyArgs
    3. {
    4.   public int arg1;
    5.   public string arg2;
    6.   public bool arg3;
    7. }
    Then in your MonoBehaviour you can make one of those:

    Code (csharp):
    1. public MyArgs TheArgs;
    And fill it all out in the inspector.

    Screen Shot 2022-01-07 at 12.01.20 PM.png

    Then just pass
    TheArgs
    into SendMessage()

    SendMessage would need to cast it back to a MyArgs (either explicitly or prospectively) and use it that way.

    Still though, I stand by my original statement: use interfaces.

    Why? Because they are first class compiler-checked item. If you misspell a function, you instantly know about it.
     
  9. AirborneFluff

    AirborneFluff

    Joined:
    Oct 9, 2021
    Posts:
    5
    That's great, but I wanted to supply the method with either a Float, bool or vector3 depending on the method. I can't just have Arg1, Arg2, Arg3... It has to be of type object, this way it can be anything.
    Code (CSharp):
    1.  
    2.     [Header("Object to invoke")]
    3.     [SerializeField] private MonoBehaviour ScriptToInvoke;
    4.     [SerializeField] public string MethodName;
    5.     [SerializeField] public object Argument;
    6.     [SerializeField] private float InvokeDelay = 0;
    In my inspector, the object Argument does not show.
    Using
    Code (CSharp):
    1. Argument = serializedObject.FindProperty("Argument");
    In my custom editor gives an error of 'Object reference not set to instance of object'.
    This is, of course, because Unity states that it cannot serialize the type object
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,369
    If you are hell-bent on missing out on compile-time-checking, just make the argument a string, pass the string in and let the target method parse it into what it expects.
     
  11. enoripocrampirone

    enoripocrampirone

    Joined:
    Oct 2, 2022
    Posts:
    2
    What you have to do is use an invoke like this:
    Code (CSharp):
    1. Invoke("MethodName", InvokeDelay);