Skip to content

Commit 9150693

Browse files
author
Rene Damm
authored
FIX: PlayerInput.Instantiate not handling control schemes correctly (#796).
1 parent 8d26e0a commit 9150693

File tree

3 files changed

+115
-21
lines changed

3 files changed

+115
-21
lines changed

Assets/Tests/InputSystem/Plugins/PlayerInputTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,91 @@ public void PlayerInput_CanAutoSwitchControlSchemesInSinglePlayer()
530530
Assert.That(listener.messages, Is.EquivalentTo(new[] {new Message("OnFire", 1f)}));
531531
}
532532

533+
// Test setup where two players both use the keyboard but with two different control
534+
// schemes.
535+
[Test]
536+
[Category("PlayerInput")]
537+
public void PlayerInput_CanSetUpSplitKeyboardPlay()
538+
{
539+
var keyboard = InputSystem.AddDevice<Keyboard>();
540+
541+
// We add a gamepad device and scheme just to add noise and make sure
542+
// this isn't throwing the thing off the rails.
543+
InputSystem.AddDevice<Gamepad>();
544+
545+
const string kActions = @"
546+
{
547+
""maps"" : [
548+
{
549+
""name"" : ""gameplay"",
550+
""actions"" : [
551+
{ ""name"" : ""fire"", ""type"" : ""button"" }
552+
],
553+
""bindings"" : [
554+
{ ""path"" : ""<Gamepad>/buttonSouth"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
555+
{ ""path"" : ""<Keyboard>/leftCtrl"", ""action"" : ""fire"", ""groups"" : ""KeyboardWASD"" },
556+
{ ""path"" : ""<Keyboard>/rightCtrl"", ""action"" : ""fire"", ""groups"" : ""KeyboardArrows"" }
557+
]
558+
}
559+
],
560+
""controlSchemes"" : [
561+
{
562+
""name"" : ""Gamepad"",
563+
""bindingGroup"" : ""Gamepad"",
564+
""devices"" : [
565+
{ ""devicePath"" : ""<Gamepad>"" }
566+
]
567+
},
568+
{
569+
""name"" : ""Keyboard WASD"",
570+
""bindingGroup"" : ""KeyboardWASD"",
571+
""devices"" : [
572+
{ ""devicePath"" : ""<Keyboard>"" }
573+
]
574+
},
575+
{
576+
""name"" : ""Keyboard Arrows"",
577+
""bindingGroup"" : ""KeyboardArrows"",
578+
""devices"" : [
579+
{ ""devicePath"" : ""<Keyboard>"" }
580+
]
581+
}
582+
]
583+
}";
584+
585+
var prefab = new GameObject();
586+
prefab.SetActive(false);
587+
prefab.AddComponent<MessageListener>();
588+
prefab.AddComponent<PlayerInput>();
589+
prefab.GetComponent<PlayerInput>().actions = InputActionAsset.FromJson(kActions);
590+
prefab.GetComponent<PlayerInput>().defaultActionMap = "gameplay";
591+
592+
var player1 = PlayerInput.Instantiate(prefab, controlScheme: "Keyboard WASD", pairWithDevice: keyboard);
593+
var player2 = PlayerInput.Instantiate(prefab, controlScheme: "Keyboard Arrows", pairWithDevice: keyboard);
594+
595+
Assert.That(player1.devices, Is.EquivalentTo(new[] { keyboard }));
596+
Assert.That(player2.devices, Is.EquivalentTo(new[] { keyboard }));
597+
Assert.That(player1.controlScheme, Is.EqualTo("Keyboard WASD"));
598+
Assert.That(player2.controlScheme, Is.EqualTo("Keyboard Arrows"));
599+
Assert.That(player1.actions["fire"].controls, Is.EquivalentTo(new[] { keyboard.leftCtrlKey }));
600+
Assert.That(player2.actions["fire"].controls, Is.EquivalentTo(new[] { keyboard.rightCtrlKey }));
601+
602+
Press(keyboard.leftCtrlKey);
603+
604+
Assert.That(player1.GetComponent<MessageListener>().messages,
605+
Is.EquivalentTo(new[] {new Message { name = "OnFire", value = 1f }}));
606+
Assert.That(player2.GetComponent<MessageListener>().messages, Is.Empty);
607+
608+
Release(keyboard.leftCtrlKey);
609+
player1.GetComponent<MessageListener>().messages.Clear();
610+
611+
Press(keyboard.rightCtrlKey);
612+
613+
Assert.That(player1.GetComponent<MessageListener>().messages, Is.Empty);
614+
Assert.That(player2.GetComponent<MessageListener>().messages,
615+
Is.EquivalentTo(new[] {new Message { name = "OnFire", value = 1f }}));
616+
}
617+
533618
[Test]
534619
[Category("PlayerInput")]
535620
public void PlayerInput_CanAutoSwitchControlSchemesInSinglePlayer_WithDevicePluggedInAfterStart()

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ however, it has to be formatted properly to pass verification tests.
1515

1616
#### Actions
1717

18+
- `PlayerInput.Instantiate` now correctly sets up a given control scheme, if specified.
19+
* When passing a `controlScheme:` argument, the result used to be a correctly assigned control scheme at the `InputUser` level but no restrictions being actually applied to the bindings, i.e. every single binding was active regardless of the specified control scheme.
20+
1821
### Changed
1922

2023
- `InputUser.onUnpairedDeviceUsed` now receives a 2nd argument which is the event that triggered the callback.

Packages/com.unity.inputsystem/InputSystem/Plugins/Users/InputUser.cs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ public void AssociateActionsWithUser(IInputActionCollection actions)
469469
{
470470
actions.devices = pairedDevices;
471471
if (s_AllUserData[userIndex].controlScheme != null)
472-
ActivateControlScheme(s_AllUserData[userIndex].controlScheme.Value);
472+
ActivateControlSchemeInternal(userIndex, s_AllUserData[userIndex].controlScheme.Value);
473473
}
474474
}
475475

@@ -508,34 +508,40 @@ public ControlSchemeChangeSyntax ActivateControlScheme(string schemeName)
508508
public ControlSchemeChangeSyntax ActivateControlScheme(InputControlScheme scheme)
509509
{
510510
var userIndex = index; // Throws if user is invalid.
511+
512+
if (s_AllUserData[userIndex].controlScheme != scheme ||
513+
(scheme == default && s_AllUserData[userIndex].controlScheme != null))
514+
{
515+
ActivateControlSchemeInternal(userIndex, scheme);
516+
Notify(userIndex, InputUserChange.ControlSchemeChanged, null);
517+
}
518+
519+
return new ControlSchemeChangeSyntax {m_UserIndex = userIndex};
520+
}
521+
522+
private void ActivateControlSchemeInternal(int userIndex, InputControlScheme scheme)
523+
{
511524
var isEmpty = scheme == default;
512525

513-
if (s_AllUserData[userIndex].controlScheme != scheme || (isEmpty && s_AllUserData[userIndex].controlScheme != null))
526+
if (isEmpty)
527+
s_AllUserData[userIndex].controlScheme = null;
528+
else
529+
s_AllUserData[userIndex].controlScheme = scheme;
530+
531+
if (s_AllUserData[userIndex].actions != null)
514532
{
515533
if (isEmpty)
516-
s_AllUserData[userIndex].controlScheme = null;
534+
{
535+
s_AllUserData[userIndex].actions.bindingMask = null;
536+
s_AllUserData[userIndex].controlSchemeMatch.Dispose();
537+
s_AllUserData[userIndex].controlSchemeMatch = new InputControlScheme.MatchResult();
538+
}
517539
else
518-
s_AllUserData[userIndex].controlScheme = scheme;
519-
520-
if (s_AllUserData[userIndex].actions != null)
521540
{
522-
if (isEmpty)
523-
{
524-
s_AllUserData[userIndex].actions.bindingMask = null;
525-
s_AllUserData[userIndex].controlSchemeMatch.Dispose();
526-
s_AllUserData[userIndex].controlSchemeMatch = new InputControlScheme.MatchResult();
527-
}
528-
else
529-
{
530-
s_AllUserData[userIndex].actions.bindingMask = new InputBinding {groups = scheme.bindingGroup};
531-
UpdateControlSchemeMatch(userIndex);
532-
}
541+
s_AllUserData[userIndex].actions.bindingMask = new InputBinding {groups = scheme.bindingGroup};
542+
UpdateControlSchemeMatch(userIndex);
533543
}
534-
535-
Notify(userIndex, InputUserChange.ControlSchemeChanged, null);
536544
}
537-
538-
return new ControlSchemeChangeSyntax {m_UserIndex = userIndex};
539545
}
540546

541547
public void PauseHaptics()

0 commit comments

Comments
 (0)