Skip to content

An UdonSharp package for VRChat that extends support for executing custom methods using SendCustomEvent and its variants with support for arguments, return values and overloaded methods.

License

Notifications You must be signed in to change notification settings

NGenesis/UdonCustomEventArgs

Repository files navigation

Udon Custom Event Args

Description

This package extends support in U# for executing custom methods using SendCustomEvent and its variants with support for arguments, return values and overloaded methods.

Why use this?

  • Adds support for calling custom events with arguments, return values and overloaded methods.
  • Supports calling custom methods on third party behaviours without modifying their source code.
    • No inheriting from custom base classes, adding custom attributes to methods, etc.
  • Can be used to create flexible event handlers that use existing methods of different behaviours where creating standardized methods and variables to pass data around would be infeasible.

Installation & Usage

VPM Package Version

  1. Install the VCC package through VRChat Creator Companion, no additional setup is required.
  2. In your code, call one of the following with your method/event name followed by the arguments that the method/event requires:
// Generic method
this.SendCustomEvent("eventname", arg0, ..., argN);

// Non-generic method
this.SendCustomEventArgs("eventname", new object[] { arg0, ..., argN });

Supported APIs

SendCustomEvent / SendCustomEventArgs

void SendCustomEvent<T0, ..., TN>(string eventName, T0 arg0, ..., TN argN)
Executes a public method on a behaviour with the specified event name and arguments.
void SendCustomEventArgs(string eventName, object[] args)

Non-generic version of SendCustomEvent that executes a public method on a behaviour with the specified event name and a list of arguments.

SendCustomEventDelayedSeconds / SendCustomEventArgsDelayedSeconds

void SendCustomEventDelayedSeconds<T0, ..., TN>(string eventName, float delaySeconds, EventTiming eventTiming, T0 arg0, ..., TN argN)
Executes a public method on a behaviour with the specified event name and arguments after a time delay, measured in seconds.
void SendCustomEventArgsDelayedSeconds(string eventName, float delaySeconds, EventTiming eventTiming, object[] args)

Non-generic version of SendCustomEventDelayedSeconds that executes a public method on a behaviour with the specified event name and a list of arguments after a time delay, measured in seconds.

SendCustomEventDelayedFrames / SendCustomEventArgsDelayedFrames

void SendCustomEventDelayedFrames<T0, ..., TN>(string eventName, int delayFrames, EventTiming eventTiming, T0 arg0, ..., TN argN)
Executes a public method on a behaviour with the specified event name and arguments after a frame delay.
void SendCustomEventArgsDelayedFrames(string eventName, int delayFrames, EventTiming eventTiming, object[] args)

Non-generic version of SendCustomEventDelayedFrames that executes a public method on a behaviour with the specified event name and a list of arguments after a frame delay.

TryExecuteCustomEvent / TryExecuteCustomEventArgs

bool TryExecuteCustomEvent<TResult, T0, ..., TN>(string eventName, out TResult returnValue, T0 arg0, ..., TN argN)
Executes a public method on a behaviour with the specified event name and arguments and outputs the return value of the method to the specified variable. If the method was successfully executed, this method will return `true`, otherwise `false`.
bool TryExecuteCustomEventArgs<TResult>(string eventName, out TResult returnValue, object[] args)

Non-generic version of TryExecuteCustomEventArgs that executes a public method on a behaviour with the specified event name and arguments and outputs the return value of the method to the specified variable. If the method was successfully executed, this method will return true, otherwise false.

Examples

using UdonSharp;
using UnityEngine;
using VRC.Udon;

// Some behaviour in a third party package that you wouldn't be able to modify the source code of
public class SomeThirdPartyBehaviour : UdonSharpBehaviour
{
    public void DoThing(int intValue)
    {
        Debug.Log($"DoThing_A {intValue}");
    }
}

public class MyInteractEventHandler
{
    public UdonSharpBehaviour EventTarget;
    public string EventName;
    public object[] EventArgs;
}

public class MyPlayerTriggerEventHandler
{
    public UdonSharpBehaviour EventTarget;
    public string EventName;
}

// A behaviour you created
public class MyBehaviour : UdonSharpBehaviour
{
    public SomeThirdPartyBehaviour AnotherBehaviour;
    public List<MyInteractEventHandler> InteractEventHandlers = new List<MyInteractEventHandler>();
    public List<MyPlayerTriggerEventHandler> PlayerTriggerEventHandlers = new List<MyPlayerTriggerEventHandler>();

    void Start()
    {
        // Calling a method on the current behaviour
        this.SendCustomEvent(nameof(Test), 1.0f, 2, "hello", true, this); // Displays "Test_A 1.0 2 hello true ThisBehaviour"
        this.SendCustomEvent(nameof(Test), 3.0f, "world", 4, false, AnotherBehaviour); // Displays "Test_B 3.0 world 4 false AnotherBehaviour"
        this.SendCustomEvent(nameof(Test), 5); // Displays "Test_C 5"
        this.SendCustomEvent(nameof(Test)); // Displays "Test_D"

        // Calling a method from another instance of a behaviour
        AnotherBehaviour.SendCustomEvent(nameof(SomeThirdPartyBehaviour.DoThing), 6); // Displays "DoThing_A 6"

        // An alternative form of calling a method which can accept an object array containing each method argument
        this.SendCustomEventArgs(nameof(Test), new object[] { 3.0f, "world", 4, false, AnotherBehaviour }); // Displays "Test_B 3.0 world 4 false AnotherBehaviour"

        // Calling methods with arguments which cannot be bound to a specific overload will fall back to the default behaviour of calling the event without arguments
        this.SendCustomEvent(nameof(Test), "these", "args", "are", "ignored"); // Displays "Test_D"

        // Store some event handlers to call later when Interact is triggered
        AddInteractEventHandler(this, nameof(Test), new object[] { 123.0f, "interact", 6, false, AnotherBehaviour });
        AddInteractEventHandler(AnotherBehaviour, nameof(SomeThirdPartyBehaviour.DoThing), new object[] { 42 });

        // Store some event handlers to call later when OnPlayerTriggerEnter is triggered
        AddPlayerTriggerEventHandler(this, nameof(Test));
        AddPlayerTriggerEventHandler(AnotherBehaviour, nameof(SomeThirdPartyBehaviour.DoThing));

        // Calling methods and getting their return value
        if(this.TryExecuteCustomEvent(nameof(GetSum), out int sum, 2, 3)) Debug.Log($"2 + 3 = {sum}"); // Displays "2 + 3 = 5"

        // Calling methods with a delay
        this.SendCustomEventDelayedSeconds(nameof(DelayedTest), 5f, EventTiming.Update, "This message appears later!"); // Displays "This message appears later!" after 5 seconds
        this.SendCustomEventDelayedFrames(nameof(DelayedTest), 1, EventTiming.LateUpdate, "This message appears on the next frame!"); // Displays "This message appears on the next frame!" in the next frame
    }

    public override void Interact()
    {
        // Displays "Test_B 123.0 interact 6 false AnotherBehaviour" and "DoThing_A 42"
        foreach(var handler in InteractEventHandlers) handler.EventTarget.SendCustomEventArgs(handler.EventName, handler.EventArgs);
    }

    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
    {
        // Displays "Test_C XXXX" and "DoThing_A XXXX" where XXXX is the playerId that was passed in
        foreach(var handler in PlayerTriggerEventHandlers) handler.EventTarget.SendCustomEvent(handler.EventName, player.playerId);
    }

    public void Test(float floatValue, int intValue, string stringValue, bool boolValue, UdonSharpBehaviour behaviourValue)
    {
        Debug.Log($"Test_A {floatValue} {intValue} {stringValue} {boolValue} {behaviourValue.name}");
    }

    public void Test(float floatValue, string stringValue, int intValue, bool boolValue, UdonBehaviour behaviourValue)
    {
        Debug.Log($"Test_B {floatValue} {stringValue} {intValue} {boolValue} {behaviourValue.name}");
    }

    public void Test(int intValue)
    {
        Debug.Log($"Test_C {intValue}");
    }

    public void Test()
    {
        Debug.Log($"Test_D");
    }

    public int GetSum(int a, int b)
    {
        return a + b;
    }

    public void DelayedTest(string message)
    {
        Debug.Log(message);
    }

    public void AddInteractEventHandler(UdonSharpBehaviour target, string eventName, object[] eventArgs)
    {
        var handler = new MyInteractEventHandler();
        handler.EventTarget = target;
        handler.EventName = eventName;
        handler.EventArgs = eventArgs;
        InteractEventHandlers.Add(handler);
    }

    public void AddPlayerTriggerEventHandler(UdonSharpBehaviour target, string eventName)
    {
        var handler = new MyPlayerTriggerEventHandler();
        handler.EventTarget = target;
        handler.EventName = eventName;
        PlayerTriggerEventHandlers.Add(handler);
    }
}

Note: List and non-UdonSharpBehaviour classes are supported in U# 1.2 beta or higher.

Notes & Caveats

  • Calling methods with arguments which cannot be bound to a specific overload will fall back to the default behaviour of calling the event without arguments.
  • Calling methods with arguments using SendCustomNetworkEvent is not currently supported.
  • Calling methods marked with the RecursiveMethod attribute may not work correctly.

About

An UdonSharp package for VRChat that extends support for executing custom methods using SendCustomEvent and its variants with support for arguments, return values and overloaded methods.

Resources

License

Stars

Watchers

Forks

Packages

No packages published