Skip to content

Commit 26bdc29

Browse files
author
Rene Damm
authored
FIX: InvokeCSharpEvents on PlayerInput not triggering onActionTriggered (#904, #854).
1 parent c1aea16 commit 26bdc29

File tree

7 files changed

+210
-101
lines changed

7 files changed

+210
-101
lines changed

Assets/Tests/InputSystem/Plugins/PlayerInputTests.cs

Lines changed: 101 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,64 @@ public void PlayerInput_PlayerIndex_DoesNotChange()
807807
Assert.That(playerInput2.playerIndex, Is.EqualTo(1));
808808
}
809809

810+
[Test]
811+
[Category("PlayerInput")]
812+
[TestCase(PlayerNotifications.SendMessages, typeof(MessageListener))]
813+
[TestCase(PlayerNotifications.BroadcastMessages, typeof(MessageListener))]
814+
[TestCase(PlayerNotifications.InvokeUnityEvents, typeof(PlayerInputEventListener), true)]
815+
[TestCase(PlayerNotifications.InvokeCSharpEvents, typeof(PlayerInputCSharpEventListener), true)]
816+
public void PlayerInput_CanReceiveNotificationWhenActionIsTriggered(PlayerNotifications notificationBehavior, Type listenerType, bool receivesAllPhases = false)
817+
{
818+
var gamepad = InputSystem.AddDevice<Gamepad>();
819+
820+
var go = new GameObject();
821+
go.SetActive(false);
822+
IListener listener;
823+
if (notificationBehavior == PlayerNotifications.BroadcastMessages)
824+
{
825+
var child = new GameObject();
826+
child.transform.parent = go.transform;
827+
listener = (IListener)child.AddComponent(listenerType);
828+
}
829+
else
830+
{
831+
listener = (IListener)go.AddComponent(listenerType);
832+
}
833+
var playerInput = go.AddComponent<PlayerInput>();
834+
835+
playerInput.notificationBehavior = notificationBehavior;
836+
playerInput.defaultActionMap = "gameplay";
837+
playerInput.actions = InputActionAsset.FromJson(kActions);
838+
839+
go.SetActive(true);
840+
841+
Press(gamepad.buttonSouth);
842+
843+
if (receivesAllPhases)
844+
{
845+
Assert.That(listener.messages, Is.EquivalentTo(new[] { new Message("Fire Started", 1f), new Message("Fire Performed", 1f) }));
846+
}
847+
else
848+
{
849+
Assert.That(listener.messages, Is.EquivalentTo(new[] {new Message("OnFire", 1f)}));
850+
}
851+
852+
listener.messages.Clear();
853+
854+
Release(gamepad.buttonSouth);
855+
856+
if (receivesAllPhases)
857+
{
858+
Assert.That(listener.messages, Is.EquivalentTo(new[] {new Message("Fire Canceled", 0f)}));
859+
}
860+
else
861+
{
862+
// 'Fire' is a button action. Unlike with value actions, PlayerInput should not
863+
// send a message on button release (i.e. when the action cancels).
864+
Assert.That(listener.messages, Is.Empty);
865+
}
866+
}
867+
810868
[Test]
811869
[Category("PlayerInput")]
812870
public void PlayerInput_CanReceiveMessageWhenActionIsTriggered()
@@ -836,7 +894,7 @@ public void PlayerInput_CanReceiveMessageWhenActionIsTriggered()
836894

837895
[Test]
838896
[Category("PlayerInput")]
839-
public void PlayerInput_CanReceiveMessageWhenContinuousActionIsCanceled()
897+
public void PlayerInput_CanReceiveMessageWhenValueActionIsCanceled()
840898
{
841899
var gamepad = InputSystem.AddDevice<Gamepad>();
842900

@@ -867,26 +925,6 @@ public void PlayerInput_CanReceiveMessageWhenContinuousActionIsCanceled()
867925
}));
868926
}
869927

870-
[Test]
871-
[Category("PlayerInput")]
872-
public void PlayerInput_CanReceiveEventWhenActionIsTriggered()
873-
{
874-
var gamepad = InputSystem.AddDevice<Gamepad>();
875-
876-
var go = new GameObject();
877-
var listener = go.AddComponent<MessageListener>();
878-
var playerInput = go.AddComponent<PlayerInput>();
879-
playerInput.notificationBehavior = PlayerNotifications.InvokeUnityEvents;
880-
playerInput.defaultActionMap = "gameplay";
881-
playerInput.actions = InputActionAsset.FromJson(kActions);
882-
listener.SetUpEvents(playerInput);
883-
884-
Press(gamepad.buttonSouth);
885-
886-
Assert.That(listener.messages,
887-
Is.EquivalentTo(new[] {new Message("gameplay/fire Started", 1f), new Message("gameplay/fire Performed", 1f)}));
888-
}
889-
890928
[Test]
891929
[Category("PlayerInput")]
892930
[TestCase(PlayerNotifications.SendMessages, typeof(MessageListener))]
@@ -1411,9 +1449,9 @@ public void TODO_PlayerInput_TriggeringAction_DoesNotAllocate()
14111449
{
14121450
""name"" : ""gameplay"",
14131451
""actions"" : [
1414-
{ ""name"" : ""fire"", ""type"" : ""button"" },
1415-
{ ""name"" : ""look"", ""type"" : ""value"" },
1416-
{ ""name"" : ""move"", ""type"" : ""value"" }
1452+
{ ""name"" : ""Fire"", ""type"" : ""button"" },
1453+
{ ""name"" : ""Look"", ""type"" : ""value"" },
1454+
{ ""name"" : ""Move"", ""type"" : ""value"" }
14171455
],
14181456
""bindings"" : [
14191457
{ ""path"" : ""<Gamepad>/buttonSouth"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
@@ -1516,43 +1554,6 @@ private class MessageListener : MonoBehaviour, IListener
15161554
{
15171555
public List<Message> messages { get; } = new List<Message>();
15181556

1519-
public void SetUpEvents(PlayerInput player)
1520-
{
1521-
var fireAction = player.actions.FindAction("gameplay/fire");
1522-
var lookAction = player.actions.FindAction("gameplay/look");
1523-
var moveAction = player.actions.FindAction("gameplay/move");
1524-
1525-
var fireEvent = new PlayerInput.ActionEvent(fireAction);
1526-
var lookEvent = new PlayerInput.ActionEvent(lookAction);
1527-
var moveEvent = new PlayerInput.ActionEvent(moveAction);
1528-
1529-
fireEvent.AddListener(OnFireEvent);
1530-
lookEvent.AddListener(OnLookEvent);
1531-
moveEvent.AddListener(OnMoveEvent);
1532-
1533-
player.actionEvents = new[]
1534-
{
1535-
fireEvent,
1536-
lookEvent,
1537-
moveEvent,
1538-
};
1539-
}
1540-
1541-
private void OnFireEvent(InputAction.CallbackContext context)
1542-
{
1543-
messages.Add(new Message { name = "gameplay/fire " + context.phase, value = context.ReadValue<float>() });
1544-
}
1545-
1546-
private void OnLookEvent(InputAction.CallbackContext context)
1547-
{
1548-
messages.Add(new Message { name = "gameplay/look " + context.phase, value = context.ReadValue<Vector2>() });
1549-
}
1550-
1551-
private void OnMoveEvent(InputAction.CallbackContext context)
1552-
{
1553-
messages.Add(new Message { name = "gameplay/move" + context.phase, value = context.ReadValue<Vector2>() });
1554-
}
1555-
15561557
// ReSharper disable once UnusedMember.Local
15571558
public void OnFire(InputValue value)
15581559
{
@@ -1614,16 +1615,50 @@ public void OnEnable()
16141615
var playerInput = GetComponent<PlayerInput>();
16151616
Debug.Assert(playerInput != null, "Must have PlayerInput component");
16161617

1617-
foreach (var item in playerInput.actionEvents)
1618-
item.AddListener(OnAction);
1618+
SetUpActionEvents(playerInput);
16191619

16201620
playerInput.deviceLostEvent.AddListener(OnDeviceLost);
16211621
playerInput.deviceRegainedEvent.AddListener(OnDeviceRegained);
16221622
}
16231623

1624-
private void OnAction(InputAction.CallbackContext context)
1624+
private void SetUpActionEvents(PlayerInput player)
16251625
{
1626-
messages.Add(new Message(context.action.ToString()));
1626+
var fireAction = player.actions.FindAction("gameplay/fire");
1627+
var lookAction = player.actions.FindAction("gameplay/look");
1628+
var moveAction = player.actions.FindAction("gameplay/move");
1629+
1630+
var fireEvent = new PlayerInput.ActionEvent(fireAction);
1631+
var lookEvent = new PlayerInput.ActionEvent(lookAction);
1632+
var moveEvent = new PlayerInput.ActionEvent(moveAction);
1633+
1634+
fireEvent.AddListener(OnFireEvent);
1635+
lookEvent.AddListener(OnLookEvent);
1636+
moveEvent.AddListener(OnMoveEvent);
1637+
1638+
player.actionEvents = new[]
1639+
{
1640+
fireEvent,
1641+
lookEvent,
1642+
moveEvent,
1643+
};
1644+
}
1645+
1646+
// We have separate methods for these rather than one that we reuse for each listener in order to
1647+
// guarantee that PlayerInput is indeed calling the right method.
1648+
1649+
private void OnFireEvent(InputAction.CallbackContext context)
1650+
{
1651+
messages.Add(new Message($"Fire {context.phase}", context.ReadValueAsObject()));
1652+
}
1653+
1654+
private void OnLookEvent(InputAction.CallbackContext context)
1655+
{
1656+
messages.Add(new Message($"Look {context.phase}", context.ReadValueAsObject()));
1657+
}
1658+
1659+
private void OnMoveEvent(InputAction.CallbackContext context)
1660+
{
1661+
messages.Add(new Message($"Move {context.phase}", context.ReadValueAsObject()));
16271662
}
16281663

16291664
private void OnDeviceLost(PlayerInput player)
@@ -1648,15 +1683,14 @@ public void OnEnable()
16481683
var playerInput = GetComponent<PlayerInput>();
16491684
Debug.Assert(playerInput != null, "Must have PlayerInput component");
16501685

1651-
16521686
playerInput.onActionTriggered += OnAction;
16531687
playerInput.onDeviceLost += OnDeviceLost;
16541688
playerInput.onDeviceRegained += OnDeviceRegained;
16551689
}
16561690

16571691
private void OnAction(InputAction.CallbackContext context)
16581692
{
1659-
messages.Add(new Message(context.action.ToString()));
1693+
messages.Add(new Message($"{context.action.name} {context.phase}", context.ReadValueAsObject()));
16601694
}
16611695

16621696
private void OnDeviceLost(PlayerInput player)

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ however, it has to be formatted properly to pass verification tests.
2323
- Fixed `anyKey` not appearing in control picker for `Keyboard`.
2424
- The text on the "Listen" button is no longer clipped off on 2019.3.
2525
- Controls bound to actions through composites no longer show up as duplicates in the input debugger.
26+
- Fixed `Invoke CSharp Events` when selected in `PlayerInput` not triggering `PlayerInput.onActionTriggered`.
2627

2728
### Changed
2829

Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ public InputAction this[string actionNameOrId]
325325
}
326326
}
327327

328+
////REVIEW: inconsistent naming; elsewhere we use "onActionTriggered" (which in turn is inconsistent with InputAction.started etc)
328329
/// <summary>
329330
/// Add or remove a callback that is triggered when an action in the map changes its <see cref="InputActionPhase">
330331
/// phase</see>.

Packages/com.unity.inputsystem/InputSystem/Controls/AnyKeyControl.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using UnityEngine.InputSystem.Layouts;
22
using UnityEngine.InputSystem.LowLevel;
33

4+
////REVIEW: generalize this to AnyButton and add to more devices?
5+
46
namespace UnityEngine.InputSystem.Controls
57
{
68
/// <summary>

0 commit comments

Comments
 (0)