Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call VRInput Class Events (OnClick, OnDoubleClick, ...) from another Class?

I'm adding a replaying feature to my game. Having this feature, I can capture user's input to the game and feed them back to Unity later on for replaying the game. But the design of VRStandardAssets.Utils.VRInput class prevents me to mimic user inputs.

This class does not provide any public method to make it possible to trigger its events (e.g. OnClick or OnDoubleClick events) programmatically. So I decided to create a derived class from it and write my own public methods to trigger the events. This strategy failed because VRInput's methods are private meaning that I cannot invoke them from a derived class.

It is recommended for this type of classes to provide a protected virtual void On[eventName](subclassOfEventArgs e) method to provide a way for a derived class to handle the event using an override but this class does not have it (why so restrictive?). I guess it's a poor design from Unity. This poor design also makes it hard to write unit/integration tests.

Am I missing something here? Can I still do something to trick other classes to think they are dealing with VRInput class while replaying the game?

like image 769
Kamran Bigdely Avatar asked Apr 16 '18 19:04

Kamran Bigdely


1 Answers

In fact you can trigger theses events (OnClick, OnDoubleClick or any other events) from another class and without using reflection using this clever hack (Inspired by this article):

C# is not really type safe so you can share the same memory location. First declare a class with two fields that share the same memory space:

[StructLayout(LayoutKind.Explicit)]
public class OverlapEvents
{
    [FieldOffset(0)]
    public VRInput Source;

    [FieldOffset(0)]
    public EventCapture Target;
}

Then you can declare a new class that will intercept and call the other event:

public class EventCapture
{
    public event Action OnClick;

    public void SimulateClick()
    {
        InvokeClicked();
    }

    // This method will call the event from VRInput!
    private void InvokeClicked()
    {
        var handler = OnClick;
        if (handler != null)
            handler();
    }
}

Then finally register it and call it:

public static void Main()
{
    input = GetComponent<VRInput>();

    // Overlap the event
    var o = new OverlapEvents { Source = input };

    // You can now call the event! (Note how Target should be null but is of type VRInput)
    o.Target.SimulateClick();
}

Here is a simple dotNetFiddle that show it working (at least outside of unity)

like image 182
Ludovic Feltz Avatar answered Oct 22 '22 05:10

Ludovic Feltz