5

Ok, I'm confused a bit. I studied UnityEvent and Messaging System by this tutorial. But I have one question, that I cannot understand. How can I pass arguments to Invoking function?

For example(I have EventManager like in tutorial):

void OnEnable() {
        EventManager.StartListening ("OnPlayerTeleport", TeleportPlayer);
    }

    void OnDisable() {
        EventManager.StopListening ("OnPlayerTeleport", TeleportPlayer);
    }

    void TeleportPlayer () {
        float yPos = transform.position.y;
        yPos += 20.0f;
        transform.position = new Vector3 (transform.position.x, yPos, transform.position.z);
    }

And I have trigger:

void Update () {
    if (Input.GetButtonDown ("Teleport")) {
        EventManager.TriggerEvent ("OnPlayerTeleport");
    }
}

But, what if I want to pass 'height' value to function 'TeleportPlayer':

void TeleportPlayer (float h) {
    float yPos = transform.position.y;
    yPos += h;
    transform.position = new Vector3 (transform.position.x, yPos, transform.position.z);
}

How can I do this?

Programmer
  • 121,791
  • 22
  • 236
  • 328
Alexandr Köln
  • 144
  • 2
  • 2
  • 8

2 Answers2

2

Use C# delegate/Action instead of Unity's UnityEvent. It is faster than Unity's event. I ported it few days ago. You just need to modify that a little bit to get what you are looking for.

1.Make Action take float by changing all Action declaration to Action<float> which means that it will allow functions with float parameter.

2.Now, make the TriggerEvent function take a float parameter. by changing

public static void TriggerEvent(string eventName)

to

public static void TriggerEvent(string eventName, float h)

Here is the new EventManager script.

using UnityEngine;
using System.Collections.Generic;
using System;

public class EventManager : MonoBehaviour
{

    private Dictionary<string, Action<float>> eventDictionary;

    private static EventManager eventManager;

    public static EventManager instance
    {
        get
        {
            if (!eventManager)
            {
                eventManager = FindObjectOfType(typeof(EventManager)) as EventManager;

                if (!eventManager)
                {
                    Debug.LogError("There needs to be one active EventManger script on a GameObject in your scene.");
                }
                else
                {
                    eventManager.Init();
                }
            }

            return eventManager;
        }
    }

    void Init()
    {
        if (eventDictionary == null)
        {
            eventDictionary = new Dictionary<string, Action<float>>();
        }
    }

    public static void StartListening(string eventName, Action<float> listener)
    {

        Action<float> thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent += listener;
        }
        else
        {
            thisEvent += listener;
            instance.eventDictionary.Add(eventName, thisEvent);
        }
    }

    public static void StopListening(string eventName, Action<float> listener)
    {
        if (eventManager == null) return;
        Action<float> thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent -= listener;
        }
    }

    public static void TriggerEvent(string eventName, float h)
    {
        Action<float> thisEvent = null;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent.Invoke(h);
        }
    }
}

Test:

public class Test : MonoBehaviour
{

    void OnEnable()
    {
        EventManager.StartListening("OnPlayerTeleport", TeleportPlayer);
    }

    void OnDisable()
    {
        EventManager.StopListening("OnPlayerTeleport", TeleportPlayer);
    }

    void Update()
    {
        if (Input.GetButtonDown("Teleport"))
        {
            EventManager.TriggerEvent("OnPlayerTeleport", 5);
        }
    }

    void TeleportPlayer(float h)
    {
        float yPos = transform.position.y;
        yPos += h;
        transform.position = new Vector3(transform.position.x, yPos, transform.position.z);
    }
}
Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 1
    Ok, thank you. But can I ask one more question? What if I have many function and some of them have `float` arguments, some have `string`, and some go without argument? – Alexandr Köln Feb 12 '17 at 08:27
  • @AlexandrKöln It get's complicated from there but can be done. You can create a class called AllData then have all the variables in it. Maybe array strings or floats. Then, change the float parameter to that AllData class type. Now you can pass a class that contains all your information in it. – Programmer Feb 12 '17 at 10:43
  • 1
    Another way is to use the `params` keyword to pass unlimited variables. Instead of making the type `float or `string`, make it `object` type. You can then cast the object to or from `float` or `string` My advice to you is to only pass in what you need. Don't make an EventSystem that can pass every data type in its parameter. This will slow down your game. – Programmer Feb 12 '17 at 10:45
  • Ok. And what if I will sstore all variables in my GameManager class (I use singleton), and every function that I need will use variable from it instance? For example: `float height = 15.0f;` in my GameManager, and in function I will just use `GameManager.instance.height`? I mean, this will be faster and more optimal for code? – Alexandr Köln Feb 12 '17 at 10:54
  • You can use any class you want but singleton does not make code fast in any way. It is simply used to have one instance of a class. I suggest you read more about singleton in C#. – Programmer Feb 12 '17 at 11:00
  • I mean, your phrase: "Don't make an EventSystem that can pass every data type in its parameter. This will slow down your game." What will faster: passing parameters or get this parameter from the instance of other class? P.S. I read about singleton and about C# `params` keyword. And I liked `params` solution. It looks like what I needю – Alexandr Köln Feb 12 '17 at 15:31
  • "get this parameter from the instance of other class" is faster in your case. Avoid [params](http://www.somasim.com/blog/2015/04/csharp-memory-and-performance-tips-for-unity/) for the performance reason I talked about. – Programmer Feb 13 '17 at 04:42
  • 1
    The question is specifically about UnityEvent, not finding alternatives. – John Stock Jun 11 '19 at 13:58
0

Create a typed subclass of unityevent to take a parameter that is passed through to listeners and invoke that instead.

Documentation with an example using integer data is here https://docs.unity3d.com/ScriptReference/Events.UnityEvent_1.html

Andrew
  • 547
  • 4
  • 13