Dynamic test for class

I am writing some code to be fairly generic and can be used in different games. One of the things I am doing is providing a “rumbling sound” where you can specify an audio clip and motor frequency vales and I will do the vibration while the sound is playing. I will do this using the new InputSystem’s Gamepad object.

But I am trying to do a dynamic check for it that fails:

Type typeGamepad = Type.GetType(“UnityEngine.InputSystem.Gamepad”);

is returning null, even though I can check for it in the immediate window:

? UnityEngine.InputSystem.Gamepad
UnityEngine.InputSystem.Gamepad
base: UnityEngine.InputSystem.InputDevice
all: {UnityEngine.InputSystem.Utilities.ReadOnlyArray<UnityEngine.InputSystem.Gamepad>}
current: null
s_GamepadCount: 0
s_Gamepads: null

This is in a project that has the new input sytem installed and active input set to both. On another project with the same settings, I was able to vibrate the controller just going directly to Gamepad.current in code.

The call is on line 42 below. If you know the answer to this question, you might know whether what I am trying to do will work in Unity. If there is a reason it won’t, please let me know.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace MusicAndSFX
    {
    // class Rumble is for vibrating gamepads
    //  note that it only has static calls
    public class Rumble
        {
        // object for gamepad, if there is one
        private static object oController = null;
        // method to invoke on object
        private static MethodInfo methodSetMotorSpeeds = null;
        // make sure we have called Init;
        private static bool bInit = false;
        // boolean to note we found InputSystem, defaults to true as we only want to set false once and check for that
        private static bool bInputSystem = true;
        // time we last checked for a gamepad. Start with -2 seconds so we will check right away
        private static float timeLastCheckedForGamePad = -2.0f;

        // InitRumble function instead of static constructor we only bother with it if SetMotorSpeeds is ever called
        private static void InitRumble()
            {
            // if we already checked for InputSystem and didn't find it, that won't change
            if (!bInputSystem)
                return;
            // check to see if we have initialized
            if (bInit && oController != null)
                return;
            // note that InitRumble has been called
            bInit = true;
            // I nearly always use try/catch around Reflection
            //   in this case, the processing is optional anyway
            try
                {
                // Basically a test to see if InputSystem is being used in this game
                Type typeGamepad = Type.GetType("UnityEngine.InputSystem.Gamepad");
                // if it is used, we will attempt to get the controller and the method for setting mtor speeds

                bInputSystem = (typeGamepad != null);
                if (bInputSystem)
                    {
                    // note that we found InputSystem - if we don't find a current controller, we still want to check at intervals
                    bInputSystem = true;
                    // note time we are testing
                    timeLastCheckedForGamePad = Time.time;
                    // try to get current gamepad
                    oController = typeGamepad.GetProperty("current", BindingFlags.Static | BindingFlags.Public).GetValue(null, null);
                    if (oController != null && methodSetMotorSpeeds == null)
                        {
                        // not really dependent on havingfound a controller, but pointless to do it if not
                        Type[] parameterTypes = new Type[] { typeof(float), typeof(float) };
                        // let's get ready to rumble! get the method call for setting motor speeds
                        methodSetMotorSpeeds = typeGamepad.GetMethod("SetMotorSpeeds", parameterTypes);
                        }
                    }
                }
            catch(Exception ex)
                {
                Debug.LogError("Exception in Rumble.Init " + ex.Message);
                }
            }
        public static void SetMotorSpeeds(float lowFrequency, float highFrequency)
            {
            // make sure we have initialized; will short circuit if no gamepad
            InitRumble();
            try
                {
                // see if we have a controller and the method to call
                if (oController != null && methodSetMotorSpeeds != null)
                    {
                    // call the gamepad's motor speed function
                    object[] parameters = new object[] { lowFrequency, highFrequency };
                    methodSetMotorSpeeds.Invoke(oController, parameters);
                    }
                }
            catch (Exception ex)
                {
                Debug.LogError("Exception in Rumble.SetMotorSpeeds " + ex.Message);
                }
            }
        }
    }

To use Type.GetType(string) you need the AssemblyQualifiedName of the type. In other words, you need not just the full name of the type with the namespace, but the Assembly info that it comes from as well:

Assembly qualified names look something like this:
System.Array, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

See:

1 Like

and their examples seem to go the other way - getting the assembly info if you have the type. Hmm…
More digging to do. Not positive Unity is going to let me do this…

OK, hella inefficient, but it short circuits on later calls after checking once. I ask every assembly in the current AppDomain if it can create the type, and if InputSystem is there, it returns the Type.

I expected to move on to debugging the dynamic method call next, but when I hit continue, a controller started rattling on my desk. :slight_smile: It works!

Code below, in case anyone else has need to do something like this. Standard disclaimer - this works for me, I hope it works for you. No other warranty is expressed or implied…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace MusicAndSFX
    {
    // class Rumble is for vibrating gamepads
    //  note that it only has static calls
    public class Rumble
        {
        // object for gamepad, if there is one
        private static object oController = null;
        // method to invoke on object
        private static MethodInfo methodSetMotorSpeeds = null;
        // make sure we have called Init;
        private static bool bInit = false;
        // boolean to note we found InputSystem, defaults to true as we only want to set false once and check for that
        private static bool bInputSystem = true;
        // time we last checked for a gamepad. Start with -2 seconds so we will check right away
        private static float timeLastCheckedForGamePad = -2.0f;

        // InitRumble function instead of static constructor we only bother with it if SetMotorSpeeds is ever called
        private static void InitRumble()
            {
            // if we already checked for InputSystem and didn't find it, that won't change
            if (!bInputSystem)
                return;
            // check to see if we have initialized
            if (bInit && oController != null)
                return;
            // note that InitRumble has been called
            bInit = true;
            // I nearly always use try/catch around Reflection
            //   in this case, the processing is optional anyway
            try
                {
                // Basically a test to see if InputSystem is being used in this game
                Type typeGamepad = null;
                // if it is used, we will attempt to get the controller and the method for setting mtor speeds

                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                foreach(Assembly assembly in assemblies)
                    {
                    typeGamepad = assembly.GetType("UnityEngine.InputSystem.Gamepad");
                    if (typeGamepad != null)
                        break;
                    }
                bInputSystem = (typeGamepad != null);
                if (bInputSystem)
                    {
                    // note that we found InputSystem - if we don't find a current controller, we still want to check at intervals
                    bInputSystem = true;
                    // note time we are testing
                    timeLastCheckedForGamePad = Time.time;
                    // try to get current gamepad
                    oController = typeGamepad.GetProperty("current", BindingFlags.Static | BindingFlags.Public).GetValue(null, null);
                    if (oController != null && methodSetMotorSpeeds == null)
                        {
                        // not really dependent on havingfound a controller, but pointless to do it if not
                        Type[] parameterTypes = new Type[] { typeof(float), typeof(float) };
                        // let's get ready to rumble! get the method call for setting motor speeds
                        methodSetMotorSpeeds = typeGamepad.GetMethod("SetMotorSpeeds", parameterTypes);
                        }
                    }
                }
            catch(Exception ex)
                {
                Debug.LogError("Exception in Rumble.Init " + ex.Message);
                }
            }
        public static void SetMotorSpeeds(float lowFrequency, float highFrequency)
            {
            // make sure we have initialized; will short circuit if no gamepad
            InitRumble();
            try
                {
                // see if we have a controller and the method to call
                if (oController != null && methodSetMotorSpeeds != null)
                    {
                    // call the gamepad's motor speed function
                    object[] parameters = new object[] { lowFrequency, highFrequency };
                    methodSetMotorSpeeds.Invoke(oController, parameters);
                    }
                }
            catch (Exception ex)
                {
                Debug.LogError("Exception in Rumble.SetMotorSpeeds " + ex.Message);
                }
            }
        }
    }
1 Like