Hello, everyone!
I was looking around for possible JavaScript/UnityScript solutions for events that felt similar to C# events and delegates. Not finding much outside of a few mentions of frameworks (I didn’t look hard), I decided to try to make something simple myself as a personal challenge. The following class is the result of that and I was just wanting to see what people thought of it, even if other options already exist. First you’ll see the class, and then examples to how it compares to using C# events.
#pragma strict
/*
* A simple JavaScript/UnityScript class that provides C#-style event functionality
* using reflection.
*/
public class SimpleJsEvent extends Object {
//A class (struct) to hold the information needed to register and fire an event
private class SimpleJsEventInfo extends System.ValueType {
var ObjectInstance:Object;
var Method:String;
var ObjectMethodInfo:System.Reflection.MethodInfo;
}
//The max number of arguments the registered function requires
private var maxNumberArguments:int = -1;
public function GetMaxNumberArguments() { return maxNumberArguments; }
//Storage for the registered functions
private var events:Array = new Array();
/*
* Registers the function you wish to call when this event is fired. This will also
* ensure that the number of parameters for all functions registered match in length.
* The number of parameters is set when the first function is registered.
* PARAM: object (System.Object) - The object that contains the fuction you want to be called
* PARAM: method (String) - The name of themethod you want to be called
*/
public function RegisterEvent(object:Object, method:String) {
//Get the method
var methodInfo:System.Reflection.MethodInfo = object.GetType().GetMethod(method);
//Get the number of parameters for the method.
var numOfArgs = methodInfo.GetParameters().length;
//Check to see if this is our first function registered. If so, assign the max number of params
//to the number of parameters of the passed in method. If not and the numbers don't match,
//throw and error.
if(maxNumberArguments == -1) {
maxNumberArguments = numOfArgs;
} else {
if(numOfArgs != maxNumberArguments) {
throw "The number of parameters for " + object.GetType() + "." + method + " is invalid. Found " + numOfArgs + " where a number of " + maxNumberArguments + " is required.";
}
}
//Create the info to be stored for later calling.
var eventInfo:SimpleJsEventInfo;
eventInfo.ObjectInstance = object;
eventInfo.Method = method;
eventInfo.ObjectMethodInfo = methodInfo;
events.Add(eventInfo);
}
/*
* Unegisters the function you wish to no longer be called when this event is fired.
* PARAM: object (System.Object) - The object that contains the fuction you want to be unregistered
* PARAM: method (String) - The name of the method you want to be removed
*/
public function UnregisterEvent(object:Object, method:String) {
var eventArray:SimpleJsEventInfo[] = events.ToBuiltin(SimpleJsEventInfo) as SimpleJsEventInfo[];
for(var i=0; i < eventArray.length; ++i) {
//If the current registered function's object and method are the same as the one's passed in,
//remove it from the array.
if(eventArray[i].ObjectInstance == object eventArray[i].Method == method) {
events.RemoveAt(i);
break;
}
}
}
/*
* Fires the event if it requires no parameters.
*/
public function FireEvent() {
this.fireEvent(null);
}
/*
* Takes in a JavaScript/UnityScript array and fire's the event, passing the
* parameters to the called function.
* PARAM: params (Array) - the parameters to be passed to each registered event
*/
public function FireEvent(params:Array) {
this.fireEvent(params.ToBuiltin(Object) as Object[]);
}
/*
* Actually performs the event firing. Goes through all registered functions and
* invokes the method.
* PARAM: params (System.object[]) - The functions, as a "built in" array, to pass to Invoke
*/
private function fireEvent(params:Object[]) {
var eventArray:SimpleJsEventInfo[] = events.ToBuiltin(SimpleJsEventInfo) as SimpleJsEventInfo[];
for(var i=0; i < eventArray.length; ++i) {
eventArray[i].ObjectMethodInfo.Invoke(eventArray[i].ObjectInstance, params);
}
}
}
EXAMPLES
C# using built-in events
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
public delegate void ExampleDelegate();
public event ExampleDelegate OnExample;
public delegate void ExampleDelegateWithParams(int param1, string param2);
public event ExampleDelegateWithParams OnExampleWithParams;
void Update () {
//Register the events
if(Input.GetKeyDown(KeyCode.R)) {
OnExample += LogText;
OnExampleWithParams += LogTextWithParams;
Debug.Log("---EVENTS REGISTERED");
}
//Fire the no param event
if(Input.GetKeyDown(KeyCode.Space)) {
if(OnExample != null){
OnExample();
Debug.Log("---OnExample FIRED");
}
}
//Fire the event with params
if(Input.GetKeyDown(KeyCode.F)) {
if(OnExampleWithParams != null){
OnExampleWithParams(42, "weeeeeeee!");
Debug.Log("---OnExampleWithParams FIRED");
}
}
//Unregister the events
if(Input.GetKeyDown(KeyCode.U)) {
OnExample -= LogText;
OnExampleWithParams -= LogTextWithParams;
Debug.Log("---EVENTS UNREGISTERED");
}
}
private void LogText() {
Debug.Log("Here is some text going into the logs!");
}
private void LogTextWithParams(int intParam, string stringParam) {
Debug.Log("Here is some text going into the logs, but with some parameters! param1 = " + intParam + " param2 = " + stringParam);
}
}
JS/US using SimpleJsEvent
#pragma strict
var OnExample:SimpleJsEvent = new SimpleJsEvent();
var OnExampleWithParams:SimpleJsEvent = new SimpleJsEvent();
function Update () {
//Fire the no param event
if(Input.GetKeyDown(KeyCode.Space)) {
OnExample.FireEvent();
Debug.Log("---OnExample FIRED");
}
//Fire the event with params
if(Input.GetKeyDown(KeyCode.F)) {
OnExampleWithParams.FireEvent(new Array(42, "weeeeeeee!"));
Debug.Log("---OnExampleWithParams FIRED");
}
//Register the events
if(Input.GetKeyDown(KeyCode.R)) {
OnExample.RegisterEvent(this, "LogText");
OnExampleWithParams.RegisterEvent(this, "LogTextWithParams");
Debug.Log("---EVENTS REGISTERED");
}
//Unregister the events
if(Input.GetKeyDown(KeyCode.U)) {
OnExample.UnregisterEvent(this, "LogText");
OnExampleWithParams.UnregisterEvent(this, "LogTextWithParams");
Debug.Log("---EVENTS UNREGISTERED");
}
}
function LogText() {
Debug.Log("Here is some text going into the logs!");
}
function LogTextWithParams(intParam, stringParam) {
Debug.Log("Here is some text going into the logs, but with some parameters! param1 = " + intParam + " param2 = " + stringParam);
}