Don't 'get' ExecuteEvents.Execute(...)

I am trying to follow the above example on how to use the IEventSystemHandler

I have created my interface:

using UnityEngine.EventSystems;

public interface RegisteredBingoMessages : IEventSystemHandler
{
    void ChangeAutoSpin(bool bAutospin);
}

I have inherrited this interface into my recipient class and implemented the function:

public class BingoManager : MonoBehaviour, RegisteredBingoMessages
.
.
.
.
.
    public void ChangeAutoSpin(bool bAutospin)
    {
        Debug.Log ("InBingoManager.ChangeAutoSpin(bool)");
        m_bAutospin = bAutospin;
        m_drawbuttonmanagerScript.Enable(!m_bAutospin);
        if (m_bAutospin)
            m_fTimer = 0.0f;
    }

But how do I call this function from my message sender?

    public override void DoPress()
    {
        base.DoPress();
        m_gameobjOptions.SetActive(false);
        m_gameobjBingo.SetActive(true);

        //m_bingomanagerScript.DoChangeAutospin(m_bAutospin);
        ExecuteEvents.Execute<RegisteredBingoMessages>(m_gameobjBingo, m_bAutospin, m_gameobjBingo.ChangeAutoSpin(m_bAutospin));
       
        m_patternmanagerScript.DoChangePattern (m_enPattern);
        //m_gameobjPatternManager.SendMessage("DoChangePattern", (int)m_enPattern);
    }

ExecuteEvents.Execute(m_gameobjBingo, m_bAutospin, m_gameobjBingo.ChangeAutoSpin(m_bAutospin));

m_gameobjBingo is the game object with the receiving script attached.
m_bAutospin will end up as the message function parameter I assume.
But I don’t get the third parameter of ExecuteEvents.Execute(…)
How do I tell it that I want to call the ‘ChangeAutoSpin’ message?

Something like this. I may have dropped a symbol or two, lambda syntax is hard to get right without a compiler in front of me.

ExecuteEvents.Execute<RegisteredBingoMessages>(m_gameobjBingo, null, (x,y) => {x.ChangeAutoSpin(m_bAutospin);});

Yeah but I don’t get the (x,y)=>
(x,y) looks like screen or world coordinates or soemthing. What does it have to do with Sending a message?

1 Like

Here is another way that the method could have been written

ExecuteEvents.Execute<RegisteredBingoMessages>(m_gameobjBingo, null, HandleEvent);

private void HandleEvent(RegisteredBingoMessages target, BaseEventData eventData)
{
   target.ChangeAutoSpin( ... );
}

The lambda shorthand tells the compiler to do the same thing for you automatically. It’ll also handle creating fields and classes for method variables that need to be lifted to a higher scope. Lambdas and the like are an advanced topic that have a lot of nuance.

1 Like

Lambda is a way of producing anonymous methods. It doesn’t have to be x and y, you could name the parameters whatever you like.

Or you can write it out in full as in @kru 's post.

I came up with my own Visual Studio / MFC like message map system below that allows you to pass the receiver any number of parameters of any primitive data type.

So all of your script classes need to inherit the new base class rather than MonoBehavior.
public class PatternManager : McCormickMonoBehaviourBase

You define your messages like this:

public enumMessage IDEnum
{
MSGID_CHANGE_PATTERN,
MSGID_CHANGE_AUTO_SPIN,
MSGID_SAVE_CHANGE_PATTERN,
MSGID_SAVE_CHANGE_AUTO_SPIN,
}

You register you script class for particular messages like this:

public BingoManager()
{
   RegisterMessageReceiver("BingoManager",MessageIDEnum.MSGID_SAVE_CHANGE_AUTO_SPIN);
   //DumpMessageMap();
}

It has done in class constructors so that the entire message map is built up front, regardless of inactive gameobjects.

Then you override this base class function in order to receive any message you have registered the class for.

protected override void DoReceiveMessage(MessageIDEnumen MsgID, MessageParams1 mp)
{
    if (enMsgID == MessageIDEnum.MSGID_SAVE_CHANGE_AUTO_SPIN)
     {
         SaveChangeAutoSpin(mp.m_bParam1);
     }
     base.DoReceiveMessage(enMsgID,mp);
}

The call to base.DoReceiveMessage(enMsgID,mp); is necessary because the parameters are placed on a stack and the base class function cleans up the stack.

using UnityEngine;
using System.Collections;

public class MessageParams1
{
    public int m_nParam1 = 0;
    public string m_strParam1;
    public bool m_bParam1 = false;
    public float m_fParam1 = 0.0f;

    public MessageParams1()
    {
    }

    public MessageParams1(MessageParams1 mp)
    {
        m_nParam1 = mp.m_nParam1;
        m_strParam1 = mp.m_strParam1;
        m_bParam1 = mp.m_bParam1;
        m_fParam1 = mp.m_fParam1;
    }
}

public class MessageParams2 : MessageParams1
{
    public int m_nParam2 = 0;
    public string m_strParam2;
    public bool m_bParam2 = false;
    public float m_fParam2 = 0.0f;

    public MessageParams2()
    {
    }

    public MessageParams2(MessageParams2 mp)
    {
        new MessageParams1(mp);
        m_nParam2 = mp.m_nParam2;
        m_strParam2 = mp.m_strParam2;
        m_bParam2 = mp.m_bParam2;
        m_fParam2 = mp.m_fParam2;
    }
}

public class MessageParams3 : MessageParams2
{
    public int m_nParam3 = 0;
    public string m_strParam3;
    public bool m_bParam3 = false;
    public float m_fParam3 = 0.0f;

    public MessageParams3()
    {
    }

    public MessageParams3(MessageParams3 mp)
    {
        new MessageParams2(mp);
        m_nParam3 = mp.m_nParam3;
        m_strParam3 = mp.m_strParam3;
        m_bParam3 = mp.m_bParam3;
        m_fParam3 = mp.m_fParam3;
    }
}

public class MessageEntry
{
    private MessageIDEnum m_enMessageID;
    private ArrayList m_arrayNameReceivers = new ArrayList();

    public MessageEntry()
    {
    }

    public MessageEntry(MessageIDEnum enMessageID, string strReceiverName)
    {
        m_enMessageID = enMessageID;
        m_arrayNameReceivers.Add(strReceiverName);
    }

    public MessageIDEnum GetMessageID()
    {
        return m_enMessageID;
    }

    private int FindReceiverName(string strReceiverName)
    {
        int nI = -1;

        for (nI = 0; nI < m_arrayNameReceivers.Count; nI++)
        {
            if (((string)m_arrayNameReceivers[nI]) == strReceiverName)
                break;
        }
        if (nI == m_arrayNameReceivers.Count)
            nI = -1;

        return nI;
    }

    public void AddReceiver(string strReceiverName)
    {
        int nI = FindReceiverName(strReceiverName);

        if (nI == -1)
            m_arrayNameReceivers.Add(strReceiverName);
    }

    public void PostMessage()
    {
        GameObject gameobject = null;
          
        for (int nI = 0; nI < m_arrayNameReceivers.Count; nI++)
        {
            gameobject = GameObject.Find((string)m_arrayNameReceivers[nI]);
            gameobject.SendMessage("DispatchMessage", m_enMessageID);
        }
    }

    public void Dump()
    {
        Debug.Log(m_enMessageID);
        for (int nI = 0; nI < m_arrayNameReceivers.Count; nI++)
        {
            Debug.Log(m_arrayNameReceivers[nI]);
        }
        Debug.Log("--------------------------------------");
    }

}

public class McCormickMonoBehaviourBase : MonoBehaviour
{
    private static ArrayList m_arrayRegisteredMessages = new ArrayList();
    private static ArrayList m_arrayMessageDetails = new ArrayList();

    private static int FindMessageID(MessageIDEnum enMessageID)
    {
        int nI = -1;

        for (nI = 0; nI < m_arrayRegisteredMessages.Count; nI++)
        {
            if (((MessageEntry)m_arrayRegisteredMessages[nI]).GetMessageID() == enMessageID)
                break;
        }
        if (nI == m_arrayRegisteredMessages.Count)
            nI = -1;

        return nI;
    }

    public static void RegisterMessageReceiver(string strReceiverName, MessageIDEnum enMessageID)
    {
        //int nI = FindFuncName(strFuncName);
        int nI = FindMessageID(enMessageID);

        if (nI >= 0)
        {
            ((MessageEntry)m_arrayRegisteredMessages[nI]).AddReceiver(strReceiverName);
        }
        else
        {
            MessageEntry messageentry = new MessageEntry(enMessageID, strReceiverName);
            m_arrayRegisteredMessages.Add(messageentry);
        }
    }

    public static void PostMessage(MessageIDEnum enMessageID, MessageParams1 mp)
    {
        int nI = FindMessageID(enMessageID);

        if (nI > -1)
        {
            m_arrayMessageDetails.Add(mp);
            ((MessageEntry)m_arrayRegisteredMessages[nI]).PostMessage();
        }
    }

    public static void DumpMessageMap()
    {
        for (int nI = 0; nI < m_arrayRegisteredMessages.Count; nI++)
        {
            ((MessageEntry)m_arrayRegisteredMessages[nI]).Dump();
        }
        Debug.Log("XXXXXXXXXXXXXXXXXXXXXXXXX");
        Debug.Log("XXXXXXXXXXXXXXXXXXXXXXXXX");
    }

    protected virtual void DoUpdate()
    {
    }

    protected virtual void DoStart()
    {
    }

    protected virtual void DoAwake()
    {
    }

    protected virtual void DoReceiveMessage(MessageIDEnum enMsgID, MessageParams1 mp)
    {
        if (m_arrayMessageDetails.Count > 0)
            m_arrayMessageDetails.RemoveAt(m_arrayMessageDetails.Count - 1);
    }

    void Start()
    {
        DoStart();
    }

    void Update()
    {
        DoUpdate();
    }

    void Awake()
    {
        DoAwake();
    }

    public void DispatchMessage(MessageIDEnum enMsgID)
    {
        DoReceiveMessage(enMsgID, (MessageParams1)m_arrayMessageDetails[m_arrayMessageDetails.Count - 1]);
    }
}

Is there such a thing as ‘union’ (as opposed to struct) in C#?

‘class MessageParams1’ would be better as ‘union MessageParams1’ if there is such a thing.

That’s an incredibly long way to go to avoid using a delegate. :wink:

C# doesn’t support Unions. Unions are very anti the nice, safe environment that C# is designed to create.

Bummer - they have their uses never the less.

I wonder how much trouble it would be to simulate a union in C# with ‘new long()’, or what ever, and with int, bool and char pointers to that same bit of memory.

By the way there are some other aspects of C# that are very unclear to this C++ programmer.

GameObject gm = new GameObject()

Is this the same in C++ as GameObject *pgm = new GameObject() or GameObject gm;
I.E. Is gm a local variable or a pointer to a bit of memory in the heap?

function DoSomething(GameObject gm)

Is the object you pass in as gm copied and placed on the stack or is parameter gm a reference the GameObject outside the function?

If the latter then what is the difference between the above and ‘function DoSomething(ref GameObject gm)’?

Your C++ is just as confusing to me. :wink:

A big part of C# is just letting go of control and trusting that the runtime knows what its doing. This is even more true using a game engine like Unity.

I’ll try and explain. It might also be worth pulling in some of the more powerful gurus. @lordofduct , @eisenpony .

You don’t get to use pointers in C# (not unless you start writing unsafe blocks). You use references, which are similar. But references point to a specific data type, not to any random memory location.

There are a couple of ways you could duplicate union functionality. One would be to use an array of bytes. You could then expose a bunch of different methods that could read that data as the various different types permitted by the union. I believe this is pretty much how streams work.

C# has two fundamentally different types of behavior.

structs are passed by value. These are normally allocated on the stack. When you pass a struct around you are creating a duplicate each time. structs are normally used for small, disposable things. Numbers, Vectors and the like. It also means that each time you copy a struct you can’t modify the original without using ref or out.

classes are passed by reference. The actual class data is placed on the heap. When you pass a class you are passing around a reference to the original. Any changes you make will be reflected in the original.

The reason why I would be keen to try and duplicate it is that, with the following as a means of passing multiple parameters to a receiver, all those variables are allocated to memory regardless of whether I use them or not. So it is a source of inefficiency when, with Android Apps, efficiency is king.

 publicclassMessageParams1
{
publicintm_nParam1=0;
publicstringm_strParam1;
publicboolm_bParam1=false;
publicfloatm_fParam1=0.0f;

publicMessageParams1()
{
}

publicMessageParams1(MessageParams1mp)
{
m_nParam1=mp.m_nParam1;
m_strParam1=mp.m_strParam1;
m_bParam1=mp.m_bParam1;
m_fParam1=mp.m_fParam1;
}
}

Well jeeeeez…I am glad you explained that.

I might have come up against some mighty strange run-time behavior with no clue as to what was causing it…because I was not expecting my class object to be altered by a function I was passing it to.

So then that would mean there is little point in using ref with a class object.

The only useful purpose for ref would be when passing structs to a function.

I don’t get it!

What is ‘myMultiDelegate += PowerUp;’ doing when PowerUp is a void function?

And how does that allow you to send multiple parameters to a receiver?

public class MulticastScript : MonoBehaviour
{
    delegate void MultiDelegate();
    MultiDelegate myMultiDelegate;
   

    void Start ()
    {
        myMultiDelegate += PowerUp;
        myMultiDelegate += TurnRed;
       
        if(myMultiDelegate != null)
        {
            myMultiDelegate();
        }
    }
   
    void PowerUp()
    {
        print ("Orb is powering up!");
    }
   
    void TurnRed()
    {
        renderer.material.color = Color.red;
    }
}

C# is NOT C++ in any way shape or form.

C# is more inspired by Java than C++, and it bars you from memory management in many ways (though there are small commands to give you some lower level access).

And there’s a reason for this. Pointers don’t exactly exist in C#, or .Net for that matter, because all the memory is managed for you. This managed memory is called the ‘heap’ and it’s a large swath of memory that objects are placed on. Problem is, if memory starts getting filled up, the memory manager can reorganize the objects on the heap to basically ‘defrag’ it so it can clear up memory from the gaps. Because of this, the actual ‘pointer’ to the object can move sporadically without you knowing. So instead of using pointers in C#, you use managed references.

Managed references are inherit to a datatype. All classes are reference types, structs are not.

Structs too have their own weird pointer, that’s not a pointer, and that is with the ‘ref’ and ‘out’ parameter modifiers which act as pointers to the stack, because the memory location they’re pointing at is always deeper in the stack from the local scope. The difference between them being ref is just a general pointer to the value in memory, out forces the called method to set that value to something.

ref and out CAN be used for classes as well, if you plan to return a reference to an object. Take for example a ‘Dictionary.TryGetValue’ and ‘Dictionary.Item’.

When attempting to get a TValue based on a TKey, if you attempted this with the normal accessor and the TKey doesn’t exist it throws an exception:

But with TryGetValue you include an out parameter. If the TKey exists, the out parameter is set, and it returns true. If it doesn’t exist, the out parameter is set to default(TValue) and false is returned:

Anyways,

Like Java, C# is intended to be object-oriented by design. Unlike C++ which is C with classes tacked on to offer object-oriented design. Of course as time has progressed, C# has added some extra bells and whistles that Java designers probably wouldn’t consider ‘pure OO’… but no matter what, at the end of the day, C# is rooted in the concept of everything being an ‘object’.

Which leads to:

Really the compiler interprets this as:

myMultiDelegate = Delegate.Combine(myMultiDelegate, new MultiDelegate(addressof PowerUp));

A delegate is supposed to replace functionality like that of the ‘void pointer’ in C++. A void pointer just allows storing the memory address of anything. Problem is void pointers can lead to a lot of unsafe nonsense.

Furthermore, we need to remember how design principles in C# are based on the idea that pointers aren’t used, but rather references, because memory moves around. Now of course, function locations don’t really move (well, in theory… remember that types can be compiled at runtime in the JIT, and as C# matured other features were implemented that toss this on its head). So design wise, C# approached it where instead of having a pointer to a function, you have an object representation of a function.

This is the delegate.

A delegate has 2 parts.

Definition:

public delegate void Foo();

We give the type a name, and a shape (the shape of this has a void return, and no params).

Objects:

Foo d = new Foo(this.SomeFunctionWithShapeFoo);
//shorthanded as
Foo d = this.SomeFunctionWithShapeFoo;
//or
d += this.SomeOtherFunctionWithShapeFoo;

Now we have object identity, and interface definition, for a function pointer.

Note! the lack of () at the end. The parens state you’re calling the function, no parens state you’re getting the address of the funciton.

Furthermore, C# builds in the idea that a delegate can store multiple references to functions. It’s sort of an array of delegates, which is shaped as a delegate. This is more or less to support the ‘event’ functionality, which I’m not going to get into.

Now,

With everything being an object, including functions, you can get more paradigms. Such as some functional paradigm like stuff. Which is where lamdas/anonymous functions come in.

lamdas are just a syntax for anonymous functions to make them cleaner.

Now an anonymous function actually just compiles up into its own data type for you. It’s sort of ‘syntax sugar’. That records the current state when created.

What I mean is…

void Foo()
{
    Random rand = new Random();
    Action[] arr = new Action[10]; //action is the delegate System.Action with shape 'void Action()'

    for (int i = 0; i < 10; i++)
    {
        double d = rand.NextDouble();
        //this lamda is going to generate a type that contains 'd' as a field member
        //that state information will be held and used when 'f' is called in the future
        arr[i] = () => { Console.WriteLine(d); };
    }

    foreach(Action f in arr)
    {
        //here the various random values are printed out in succession
        //note how the state from earlier was preserved
        f();
    }

}

These are often known as “closures” in languages like python or lisp (hence the lamda notation, which is borrowed from lisp). They close around the current state at the time of creation.

This allows you to write code that is called “functional” because it resembles “functions” in the mathematical sense.

f(x) = 3x + 2
g(x) = x^2 + 1
f(g(x)) = 3(x^2 + 1) + 2

SO, with the ExecuteEvents.Execute, if you want to call a function on the targets with some parameters you’d use this closure to carry through the methods.

ExecuteEvents.Execute<RegisteredBingoMessages>(m_gameobjBingo, null, (c,e) => { c.ChangeAutoSpin(m_bAutospin) });

Now, Execute is given a closure that has the current state of the calling code (it has m_bAutoSpin included). The closure has the shape of the delegate EventFunction which is like:

(T c, BaseEventData e)

The method has 2 params, first is of type T (RegisteredBingoMessages), and the other of type BaseEventData (because all EventSystem is used primarily for the EventSystem which is used primarily for mouse/touch input, which includes event info along with it). Note that I passed in null for the BaseEventData, because we don’t need it. You may wonder why the types aren’t declared in code, that’s just cause the compiler infers them. You could declare them explicitly:

ExecuteEvents.Execute<RegisteredBingoMessages>(m_gameobjBingo, null, (T c,BaseEventData e) => { c.ChangeAutoSpin(m_bAutospin) });

Execute can now call the passed in closure and you can use m_bAutoSpin on the ‘c’ that is called. This can be several of them, because the GameObject might have multiple components that implement RegisteredBingoMessages.[/code]

3 Likes

OK that makes slightly more sense to me although it is still a rather alien concept having come from C++.

I am still far from clear why I would need it but I guess that will come with time and practice.

Another question…

How do you create assignment operator functions for a class?

Along these lines…

public Parameter operator =(bool bVal)
    {
        Array.Copy(bVal, m_arrayBytesParam, 1);
        return this;
    }

Unity is unhappy with the = symbol.

As far as I’m aware you can’t override the assignment operator in C#.

Why would you want to? It doesn’t make much sense, and would lead to some very unusual behavior.

http://stackoverflow.com/questions/599367/why-can-not-be-overloaded-in-c

You can’t override the assignment behaviour.

Assignment switches out the data stored in the address of the variable. It does not modify the data. If it was a class (a ref type), modifying the state of the class is counter to the concept of object identity (critical to OO). If it’s a struct (a value type), you can just use a conversion rather than an assignment overload.

It may be worth pointing out properties. While these don’t let you override assignment behavior in general, it does let you override it for a specific reference.

I ain’t going to lie… you pull some of the weirdest C++ concepts out of left field.

Assignment overloads?

I don’t mean to offend or anything… but it seems you like to ‘hack’ C++ more than design with C++.

This may be the cause of your confusion about C#. C# (like Java) is intended to STOP you from hacking it up like that. C# was designed for enterprise grade, well designed, rapid application development. It isn’t intended for saving that extra clock cycle, or magic trick memory manipulation. It’s supposed to be memory invisible (within reason).

That way… your developers can spend more time thinking about how their program WORKS, not how the memory will be cleaned up.

Which is nice for a game engine that is intended for use by novices and experts. The memory critical stuff is all inside the engine (and written in C++), where as you’re more concerned with the actual scripting of the game.

IF you need some tight C++ control for some section of code that is slow in C#, you can write a native C++ plugin, and load it into your game.

2 Likes

Well from my C++ point of view, I would regard Parameter p = 3; p = false nicer than p.set(false); p.Set(3).

From you C# point of view you might regard simulating a C++ union has ‘hacking’ but you have to concede that a C++ union is more efficient on android memory for my purpose.

As long as I am careful not to overrun the array bounds (not remotely difficult if you set the array length for the largest possible data type) then I really don’t see what all the fuss is about!

Ummm… if p is a int or a bool, you DO just say ‘p = 3’ or ‘p = false’.

You don’t need to overload the assignment operator for that.

I honestly don’t know why anyone would write ‘p.Set(3)’, what is getting set to 3??? p? What is p that it can be set to 3 but isn’t a number?

1 Like