Joystick Runtime Plug/Unplug Detection
Hello, I am using Unity 5.2.2 and checking at each frame if :
Input.GetJoystickNames().Length > 0
the issue is that this thing works one-way only. If I start the game without a Joystick, it detects whether a Joystick gets plugged in. If at this point I unplug the Joystick, that array doesn't change and its length is still 1. I'd like to know if any of you sirs know how to do correct runtime plug/unplug detection of joystick. Unity makes a warning appear on the console when joystick is unplugged, so I'm pretty sure there must be a way to catch that event, but I don't seem to find it!
The Joystick I am using is a XBOX 360 controller.
Upvoting this question - would be great to get a multi-platform solution for this without having to resort to XInput or related .DLLs specific to windows only :-) I saw in other forums the actual name strings in the GetJoystickNames() array are being set to blank "" when joysticks are disconnected, hence no reduction in .Length, but that some legit gamepad names are detected as "" even when connected, so using a blank "" string as comparator is not reliable as a means to check connect/disconnect. Given those events appear in the editor console, it should be detectable, surely? The Length of the Game Controllers list in the Windows OS Control Panel is being dynamically updated up and down as controllers are connected/disconnected, just not in the UnityEngine Input class.
Answer by Alakanu · Jan 08, 2016 at 04:36 PM
I used system APIs to solve this problem. I leave my code here hoping it will help someone in the future. This works with xbox joystick, I haven't tested with other gamepads since I don't own any.
PlayerIndex singlePlayer = (PlayerIndex)0;
GamePadState testState = GamePad.GetState(singlePlayer);
if (testState.IsConnected)
{
//joystick is connected
}
else
{
//joystick is not connected
}
The API Alakanu mentioned is XINPUT and it's a must have for any game with gamepad support. Whether it's detecting runtime joysticks or adding rumble, it does it all. Here's a link to the github. https://github.com/speps/XInputDotNet
Answer by AG_Alex · Mar 30, 2017 at 09:37 AM
For people who stumble upon this same problem, there is a way to do it within Unity.
As correctly stated by @Alakanu that the size of the string array does not reduce by one when disconnecting a controller, Unity does set the specific controller entry back to an empty string. So by simply checking if the entry is empty or not, you are able to determine when a controller gets connected / disconnected.
Example Code:
//Get Joystick Names
string[] temp = Input.GetJoystickNames();
//Check whether array contains anything
if(temp.Length > 0)
{
//Iterate over every element
for(int i =0; i < temp.Length; ++i)
{
//Check if the string is empty or not
if(!string.IsNullOrEmpty(temp[i]))
{
//Not empty, controller temp[i] is connected
Debug.Log("Controller " + i + " is connected using: " + temp[i]);
}
else
{
//If it is empty, controller i is disconnected
//where i indicates the controller number
Debug.Log("Controller: " + i + " is disconnected.");
}
}
}
I recommend you do not run this code every frame, but at a set interval (eg every 2 seconds) instead to save on performance.
Hope it helps!
Kind Regards, Alex
Yes, this is another solution I found. It works but I hate it because it looks like a makeshift solution and I cannot accept something like that from Unity. They said they are reworking the Input management, let's hope for the best.
Yes, this works great. I did it like this. This is to switch between keyboard mouse and gamepad/joystick.
if (Input.GetJoystickNames().Length > 0) // Auto switch if gamepad is activated.
{
if (Input.GetJoystickNames()[0].Length > 0)
{
if (!IsInDirect$$anonymous$$ode)
{
IsInDirect$$anonymous$$ode = true; // toggle mode
CurrentClickTarget = transform.position; // clear the click target
}
}
else
{
if (IsInDirect$$anonymous$$ode)
{
IsInDirect$$anonymous$$ode = false;
CurrentClickTarget = transform.position;
}
}
}
if (IsInDirect$$anonymous$$ode)
{
ProcessDirect$$anonymous$$ovement();
}
else
{
Process$$anonymous$$ouse$$anonymous$$ovement();
}
Answer by Sosara · Mar 18, 2018 at 08:32 AM
There is a little trick (not so tricky) that I've done though, i'm not sure if it'll work for every console. But hey, it works on mine. Here's the code.
void Start() {
StartCoroutine(delayCheck());
}
IEnumerator delayCheck() {
yield return new WaitForSecondsRealtime(2f);
for(int i = 0; i < Input.GetJoystickNames().Length; i++) {
if (!string.IsNullOrEmpty(Input.GetJoystickNames()[i])) {
Debug.Log("Joystick Connected");
i = Input.GetJoystickNames().Length;
joystickDialogue = true;
} else {
Debug.Log("Joystick Disconnected");
i = Input.GetJoystickNames().Length;
joystickDialogue = false;
}
}
StopAllCoroutines();
StartCoroutine(delayCheck());
}
You might be in shock why I created my own version of "return". Well, I don't know why but, Unity returns both connected and disconnected without it.
I Just wanted to $$anonymous$$ention, ins$$anonymous$$d of Calling the IEnumeration again and again, because you should avoid to Call The Same Function inside the Same Function.
The simple Solution is to put all the Code into a While Loop
IEnumerator delayCheck()
{
while (true)
{
yield return new WaitForSecondsRealtime(2f);
for (int i = 0; i < Input.GetJoystickNames().Length; i++)
{
if (!string.IsNullOrEmpty(Input.GetJoystickNames()[i]))
{
Debug.Log("Joystick Connected");
i = Input.GetJoystickNames().Length;
joystickDialogue = true;
}
else
{
Debug.Log("Joystick Disconnected");
i = Input.GetJoystickNames().Length;
joystickDialogue = false;
}
}
}
}
Answer by petersvp · Aug 26, 2016 at 09:30 PM
In my case I ended up implementing my own InputManager using Win32's RawInput API in C++. This way you can detect disconnections and handle them yourself. You can even differentiate between multiple mice this way - And the multiple mice was the motivation to do so....
Here is sample implementation but for mice. Alter is to suit your needs. It properly detects XBOX Gamepads. There is the code needed to register for gamepads, but then you should read on since this example was intended to be used only for mice.
C++ DLL: http://pastebin.com/0Szi8ga6 C# script: http://pastebin.com/4h3CqpYy
Note that in Editor, Unity steals RawInput devices very feequently. I did not had any issues in a build, however.
Answer by ericmg123 · Apr 04, 2017 at 08:25 PM
I fashioned something similar to the accepted answer last year but just polished it to deal with controllers connecting and disconnecting. This is tested and working when one type of controller is connected. I'm sure some wonkiness will happen if you have multiple different controllers plugged in.
Declare some variables:
public bool playstationController, xboxController, keyboard;
public string[] currentControllers;
public float controllerCheckTimer = 2;
public float controllerCheckTimerOG = 2;
Then here's a function I call when I want to check what's currently connected:
public void ControllerCheck()
{
System.Array.Clear(currentControllers, 0, currentControllers.Length);
System.Array.Resize<string>(ref currentControllers, Input.GetJoystickNames().Length);
int numberOfControllers = 0;
for (int i = 0; i < Input.GetJoystickNames().Length; i++)
{
currentControllers[i] = Input.GetJoystickNames()[i].ToLower();
if ((currentControllers[i] == "controller (xbox 360 for windows)" || currentControllers[i] == "controller (xbox 360 wireless receiver for windows)" || currentControllers[i] == "controller (xbox one for windows)"))
{
xboxController = true;
keyboard = false;
playstationController = false;
}
else if (currentControllers[i] == "wireless controller")
{
playstationController = true; //not sure if wireless controller is just super generic but that's what DS4 comes up as.
keyboard = false;
xboxController = false;
}
else if (currentControllers[i] == "")
{
numberOfControllers++;
}
}
if (numberOfControllers == Input.GetJoystickNames().Length)
{
keyboard = true;
xboxController = false;
playstationController = false;
}
}
As @AG_Alex mentioned, it's not a good idea to do this in Update. I setup a timer so that if there's no input, a timer ticks down and when it hits zero, it calls the ControllerCheck function.
if (!Input.GetButtonDown("SingleFlap") && !Input.GetButton("MultiFlap"))
{
controllerCheckTimer -= Time.deltaTime;
if (controllerCheckTimer <= 0)
{
ControllerCheck();
controllerCheckTimer = controllerCheckTimerOG;
}
}
else
{
controllerCheckTimer = controllerCheckTimerOG;
}
The issue with the code snippets I've seen when searching for a solution is that Unity jumbles the order of connected devices. For example, if the 0th joystick is empty (keyboard), the 1st joystick is detected as being XBox, and the 2nd joystick is empty, at the end of the for loop you may not get what you wanted (i.e. an XBox controller is connected, but the last Joystick Unity detected is empty), so you may be SoL. The basic idea with this function is to increment a temporary number each time the JoystickName comes back empty (keyboard or mouse or whatever else Unity decides is empty). Keyboard is only set to true if all of the connected Joysticks are empty. Otherwise, one of the other ones is set to true. I think this is a pretty good fix until Unity sorts out its joystick detection. Cheers, Eric G.
Your answer
Follow this Question
Related Questions
How do you determine the x,y co-ordinates of a sprite's boundaries? 1 Answer
How can detect moving in both directions one Input axis at the same time? 0 Answers
Raycast detecting colliders issue w/ image depicting problem 0 Answers
Help (reloading a level) 0 Answers
Resources.Load() .fbx file with multiple mesh children. 1 Answer