Creating a event callback system between JS and C#

Hi,

I have a central C# script that handles many different functions and I’d like it to be able to ping various different script as needed (mostly Javascript, although another C# script, too.)

So basically, I’d like this (pseudo-code)

In the Central c# function (let’s call it CentralScript)

public void RegisterCallBackForEvent( function passedFunction ) {
     ArrayWithEventFunctions[xx] = passedFunction;
}


public void EventThatNeedsCommunicating() {
     for all passedFunction in ArrayWithEventFunctions {
          Invoke(passedFunction);
     }
}



In the other JS / C# scripts

function HandleEvent() {
     ... do stuff ...
}

Function Start() {
     CentralScript.RegisterCallBackForEvent( HandleEvent() );
}

Is this something that can be done, and if so, any easy to follow examples out there?

Thanks!

So the question is essentially can UnityScript functions be accepted as delegates by C#, I take it ?

All I can tell you is that I’ve avoided doing it because I’m terrified of the disconnect between UnityScript and C#, particularly when it comes to differing typing strengths.

I might give it a go tho. I take it you’re running into some brick wall or another tho? Does that mean you’ve tried this and it doesn’t work?

Here’s my very real world example.

I’m in the middle of implementing Game Center into my iphone app. Now Game Center authentication can be available or not depending on a bunch of different conditions (user logs out explicitly via the Center while your app is in the background, user’s app loses network signal, etc.)

I have some UI elements that are only applicable to GC. Apple’s guidelines (rightly so) say that if GC isn’t available for whatever reason those UI and other elements that depend on GC shouldn’t be shown and/or available to the user.

So for me, I have a c# script where I handle all of my GC related communications (posting scores, authenticating, etc.) What I’d like to do is create a event that other scripts to subscribe to, to let them know if the authentication status changes. Those scripts, once receiving the event would then be able to toggle between their appropriate states.

So far I’ve tried a few different C# delegate method examples I’ve found online, but none of them seem to work. Furthermore, I still don’t know what the proper syntax is to just pass a method along, let alone store it in a array of some form and then invoke it later when the event is needed.

So, I have no idea what Game Center is. That said:

The last time I did any kind of event marshaling I did it in UnityScript only or C# only. I can show you what I did for the UScript version here:

public static var TARGET_ADDED: String = "targetAdded";
public static var TARGET_DESTROYED: String = "targetDestroyed";
public static var GAME_PAUSED: String = "gamePaused";
public static var GAME_UNPAUSED: String = "gameUnPaused";
public static var GAME_NEXT_LEVEL: String = "gameNextLevel";

//singletons
private static var eventListeners: Hashtable;
private static var instantiated: boolean = false;

public static function RegisterEventListener( eventString: String, listener ) {
	if( !instantiated ) {
		eventListeners = new Hashtable();
		instantiated = true;
	}

	if( !eventListeners.Contains( eventString ) ) {
		eventListeners.Add( eventString, new Array() );
	}
	
	eventListeners[eventString].Add( listener );
}

public static function TriggerEvent( eventString: String, eventData: Object ) {
	if( !instantiated ) {
		Debug.LogError( "EventManager received an event trigger for an unregistered event." ); 
		return;
	}
	
	if( eventListeners[eventString].length <= 0 ) {
		Debug.LogError( "EventManager received an event trigger for a registered event, but there are no listeners." );
	}
	
	for( var ii: int = 0; ii < eventListeners[eventString].length; ii++ ) {
		eventListeners[eventString][ii]( eventString, eventData );
	}
}

//this is unused, but Unity appears to complain when it's not here
function Update() {
}

I’m not sure if this helps you. I disliked this code for two reasons:
I couldn’t seem to throw exceptions in JS, and the public const pattern was not available, so I ended up using public vars. I was the sole writer, so I felt I could get away with it. If I were to do it again, I would do it in C#, no problem.

For C#, I don’t have an example that I know works and is ready to go, but you would want to do something like this:

public delegate eventListener( string eventString, SomeType eventData );

public class MyEventClass
{
   //events:
   public const string EVENT_TYPE_A = "eventTypeA";
   public const string EVENT_TYPE_B = "eventTypeB";
       //etc

   //event listener map
   private Dictionary<string, List<eventListener>>eventMap = new Dictionary<string, List<eventListener>>();

   public void registerEventListener( string eventType, eventListener functionThatListens )
   {
        //add stuff to dictionary stuff, instantiate new list if eventstring not in dictionary, etc.
   }

   public void triggerEvent( string eventType, SomeType eventData )
   {
       //validate data before this line, then do:
       for( int i = 0; i < eventMap[eventType].Length; i++ )
            (eventMap[eventType])[i]( eventType, eventData );
   }

   public void clearListenersForEvent( string eventType ) { ... }

   public void declareThatCakeIsDelicious{ Deb.LogError( "Let it not stand in doubt that cake is delicious." ); }

    //etc
}

Now, if you’re talking about passing a UScript function to a C# script that tries to accept it as a delegate, I really have no clue if that works or not. And frankly I’m not even sure I’m answering your question, but if you’re having trouble getting delegates to work at all, that should get you started.

Thanks, TM! I’ll have to spend some time digesting this and trying to incorporate the ideas in here. I’ll let you know if it works out (and how.)

I’m definitely interested to hear your results. I may try some stuff these weekend focusing particularly on the crossover between Uscript and C#. I’d be interested to compare notes.

Hi Redd,

I played with it a little bit but it took me until today to get to it, sorry about that. Anyway, here are my results:

It looks like you can definitely do this. Now I haven’t pushed it too hard, but I got it working in the following way:

C# class:

public delegate void EventListener( string eventName, Object eventData );

public class EventManager
{
   public const string EVENT_ONE = "eventOne";

   private static Dictionary<string, List<EventListener>>() eventMap = new Dictionary<string, List<EventListener>>();
   public static void RegisterEventListener( string eventName, EventListener listener )
   {
      //add listener to dictionary, create list if not exists, etc
   }

   public static void RaiseEvent( string eventName, Object eventData )
   {
      //data sanity check, and then:
		foreach( EventListener listener in eventMap[eventName] )
			listener( eventName, eventData );
   }
}

event raising class invokes:

EventManager.RaiseEvent( EventManager.EVENT_ONE, null );

event registering script (UnityScript):

function Awake()
{
  	EventManager.RegisterEventListener( EventManager.EVENT_ONE, eventListeningFunction );
}

private function eventListeningFunction( eventName : String, tankAffected : Object ) : void
{
    Debug.Log( "this works!" );
}

When the invoking class triggers that RaiseEvent call, we get the expected result:
“this works!”

So, it looks like you’re in the clear. Which is good news for me, because I’ve just run into something that desperately needs good events and talking between UnityScript and C#, so I was a little worried there for a bit.

Edit: I did want to draw your attention to the explicit typing in the eventListeningFunction declaration. I was worried C# might kill itself if it couldn’t resolve my delegate type declaration, so this is what I tried first. I don’t know if it works without explicit typing in UnityScript. It might, but I don’t know.

I have encountered a strangeness on the iPod regarding this approach whereby if you have a JavaScript function called when an event is raised by the C# EventManager, then you cannot do compound dereferences in the JavaScript function.

For instance:

//somewhere:
EventManager.RegisterEventListener( EventManager.EVENT_ONE, eEventListeningFunction );

//EventListeningFunction:

private function EventListeningFunction( eventName : String, tankAffected : SomeObject ) : void
{
    //crashes the program on the ipod:
    tankAffected.GetSomeObject().DoSomethingWithObject();
    //works fine on the ipod
    var tmp_obj : SomeObject = tankAffected.GetSomeObject();
    tmp_obj.DoSomethingWithObject();
}

Both run just fine in the editor. Gonna file a bug report, but thought you might like to know if you happen to be building for iOS.

Redd, here’s a proven callback driven system in Unity. Check it out! :sunglasses:

You can download the Visual Studio C# example project here. The whole repository: here.

Here’s a video about it: http://www.youtube.com/watch?v=KytO48w7fOI

Enjoy! :wink:

Tags: Unity3d callback event driven CallbackQueue