Skip to content

Commit 2889df9

Browse files
author
Rene Damm
authored
FIX: Messages from PlayerInput not received when actions contain spaces (case 1214519, #1101).
1 parent f9f4f5d commit 2889df9

File tree

4 files changed

+101
-42
lines changed

4 files changed

+101
-42
lines changed

Assets/Tests/InputSystem/Plugins/PlayerInputTests.cs

Lines changed: 97 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
using UnityEngine.InputSystem.Users;
1313
using UnityEngine.InputSystem.Processors;
1414
using UnityEngine.InputSystem.XInput;
15+
using UnityEngine.Profiling;
16+
using UnityEngine.TestTools.Constraints;
1517
using Object = UnityEngine.Object;
1618
using Gyroscope = UnityEngine.InputSystem.Gyroscope;
19+
using Is = UnityEngine.TestTools.Constraints.Is;
1720

1821
/// <summary>
1922
/// Tests for <see cref="PlayerInput"/> and <see cref="PlayerInputManager"/>.
@@ -826,6 +829,25 @@ public void PlayerInput_ByDefaultChoosesMostSpecificControlSchemeAvailable()
826829
Assert.That(playerInput.currentControlScheme, Is.EqualTo("PS4Gamepad"));
827830
}
828831

832+
// https://fogbugz.unity3d.com/f/cases/1214519/
833+
[Test]
834+
[Category("PlayerInput")]
835+
public void PlayerInput_CanHaveSpacesAndSpecialCharactersInActionNames()
836+
{
837+
InputSystem.AddDevice<Gamepad>();
838+
839+
var go = new GameObject();
840+
var playerInput = go.AddComponent<PlayerInput>();
841+
playerInput.defaultActionMap = "gameplay";
842+
playerInput.notificationBehavior = PlayerNotifications.SendMessages;
843+
playerInput.actions = InputActionAsset.FromJson(kActions);
844+
var listener = go.AddComponent<MessageListener>();
845+
846+
Press((ButtonControl)playerInput.actions["Action With Spaces!!"].controls[0]);
847+
848+
Assert.That(listener.messages, Has.Exactly(1).With.Property("name").EqualTo("OnActionWithSpaces"));
849+
}
850+
829851
// Test setup where two players both use the keyboard but with two different control
830852
// schemes.
831853
[Test]
@@ -1133,6 +1155,42 @@ public void PlayerInput_CanReceiveMessageWhenValueActionIsCanceled()
11331155
}));
11341156
}
11351157

1158+
[Test]
1159+
[Category("PlayerInput")]
1160+
[TestCase(PlayerNotifications.SendMessages, typeof(MessageListener))]
1161+
[TestCase(PlayerNotifications.BroadcastMessages, typeof(MessageListener))]
1162+
[TestCase(PlayerNotifications.InvokeUnityEvents, typeof(PlayerInputEventListener))]
1163+
[TestCase(PlayerNotifications.InvokeCSharpEvents, typeof(PlayerInputCSharpEventListener))]
1164+
[Retry(2)] // Warm up JIT.
1165+
public void PlayerInput_TriggeringAction_DoesNotAllocateGCMemory(PlayerNotifications notificationBehavior, Type listenerType)
1166+
{
1167+
var gamepad = InputSystem.AddDevice<Gamepad>();
1168+
1169+
var go = new GameObject();
1170+
var playerInput = go.AddComponent<PlayerInput>();
1171+
playerInput.notificationBehavior = notificationBehavior;
1172+
playerInput.defaultActionMap = "gameplay";
1173+
playerInput.actions = InputActionAsset.FromJson(kActions);
1174+
1175+
var listener = (IListener)go.AddComponent(listenerType);
1176+
// We don't want the listener to actually record messages. They have an object field which will
1177+
// box values and thus allocate GC garbage. We *do* want the listener to actually read values
1178+
// to make sure that doesn't allocate anything.
1179+
listener.messages = null;
1180+
1181+
// First message is allowed to perform initialization work and thus allocate.
1182+
PressAndRelease(gamepad.buttonSouth);
1183+
1184+
var kProfilerRegion = "PlayerInput_TriggeringAction_DoesNotAllocateGCMemory";
1185+
1186+
Assert.That(() =>
1187+
{
1188+
Profiler.BeginSample(kProfilerRegion);
1189+
PressAndRelease(gamepad.buttonSouth);
1190+
Profiler.EndSample();
1191+
}, Is.Not.AllocatingGCMemory());
1192+
}
1193+
11361194
[Test]
11371195
[Category("PlayerInput")]
11381196
[TestCase(PlayerNotifications.SendMessages, typeof(MessageListener))]
@@ -1791,14 +1849,6 @@ public void TODO_PlayerInput_CanSetUpSplitScreen_AndManuallyAllocatePlayersToScr
17911849
Assert.Fail();
17921850
}
17931851

1794-
[Test]
1795-
[Category("PlayerInput")]
1796-
[Ignore("TODO")]
1797-
public void TODO_PlayerInput_TriggeringAction_DoesNotAllocate()
1798-
{
1799-
Assert.Fail();
1800-
}
1801-
18021852
// An action is either
18031853
// (a) button-like, or
18041854
// (b) axis-like, or
@@ -1823,7 +1873,8 @@ public void TODO_PlayerInput_TriggeringAction_DoesNotAllocate()
18231873
""actions"" : [
18241874
{ ""name"" : ""Fire"", ""type"" : ""button"" },
18251875
{ ""name"" : ""Look"", ""type"" : ""value"" },
1826-
{ ""name"" : ""Move"", ""type"" : ""value"" }
1876+
{ ""name"" : ""Move"", ""type"" : ""value"" },
1877+
{ ""name"" : ""Action With Spaces!!"", ""type"" : ""value"" }
18271878
],
18281879
""bindings"" : [
18291880
{ ""path"" : ""<Gamepad>/buttonSouth"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
@@ -1836,7 +1887,8 @@ public void TODO_PlayerInput_TriggeringAction_DoesNotAllocate()
18361887
{ ""path"" : ""<Keyboard>/w"", ""name"" : ""up"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
18371888
{ ""path"" : ""<Keyboard>/s"", ""name"" : ""down"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
18381889
{ ""path"" : ""<Mouse>/delta"", ""action"" : ""look"", ""groups"" : ""Keyboard&Mouse"" },
1839-
{ ""path"" : ""<Mouse>/leftButton"", ""action"" : ""fire"", ""groups"" : ""Keyboard&Mouse"" }
1890+
{ ""path"" : ""<Mouse>/leftButton"", ""action"" : ""fire"", ""groups"" : ""Keyboard&Mouse"" },
1891+
{ ""path"" : ""<Gamepad>/buttonNorth"", ""action"" : ""Action With Spaces!!"", ""groups"" : ""Gamepad"" }
18401892
]
18411893
},
18421894
{
@@ -1884,8 +1936,8 @@ public void TODO_PlayerInput_TriggeringAction_DoesNotAllocate()
18841936

18851937
private struct Message : IEquatable<Message>
18861938
{
1887-
public string name;
1888-
public object value;
1939+
public string name { get; set; }
1940+
public object value { get; set; }
18891941

18901942
public Message(string name, object value = null)
18911943
{
@@ -1923,65 +1975,71 @@ public override int GetHashCode()
19231975

19241976
private interface IListener
19251977
{
1926-
List<Message> messages { get; }
1978+
List<Message> messages { get; set; }
19271979
}
19281980

19291981
private class MessageListener : MonoBehaviour, IListener
19301982
{
1931-
public List<Message> messages { get; } = new List<Message>();
1983+
public List<Message> messages { get; set; } = new List<Message>();
19321984

19331985
// ReSharper disable once UnusedMember.Local
19341986
public void OnFire(InputValue value)
19351987
{
1936-
messages.Add(new Message { name = "OnFire", value = value.Get<float>() });
1988+
messages?.Add(new Message { name = "OnFire", value = value.Get<float>() });
19371989
}
19381990

19391991
// ReSharper disable once UnusedMember.Local
19401992
public void OnLook(InputValue value)
19411993
{
1942-
messages.Add(new Message { name = "OnLook", value = value.Get<Vector2>() });
1994+
messages?.Add(new Message { name = "OnLook", value = value.Get<Vector2>() });
19431995
}
19441996

19451997
// ReSharper disable once UnusedMember.Local
19461998
public void OnMove(InputValue value)
19471999
{
1948-
messages.Add(new Message { name = "OnMove", value = value.Get<Vector2>() });
2000+
messages?.Add(new Message { name = "OnMove", value = value.Get<Vector2>() });
19492001
}
19502002

19512003
// ReSharper disable once UnusedMember.Local
19522004
public void OnOtherAction(InputValue value)
19532005
{
1954-
messages.Add(new Message { name = "OnOtherAction", value = value.Get<float>() });
2006+
messages?.Add(new Message { name = "OnOtherAction", value = value.Get<float>() });
2007+
}
2008+
2009+
// ReSharper disable once UnusedMember.Local
2010+
public void OnActionWithSpaces(InputValue value)
2011+
{
2012+
messages?.Add(new Message { name = "OnActionWithSpaces", value = value.Get<float>() });
19552013
}
19562014

19572015
// ReSharper disable once UnusedMember.Local
19582016
public void OnDeviceLost(PlayerInput player)
19592017
{
1960-
messages.Add(new Message { name = "OnDeviceLost", value = player});
2018+
messages?.Add(new Message { name = "OnDeviceLost", value = player});
19612019
}
19622020

19632021
// ReSharper disable once UnusedMember.Local
19642022
public void OnDeviceRegained(PlayerInput player)
19652023
{
1966-
messages.Add(new Message { name = "OnDeviceRegained", value = player});
2024+
messages?.Add(new Message { name = "OnDeviceRegained", value = player});
19672025
}
19682026

19692027
// ReSharper disable once UnusedMember.Local
19702028
public void OnControlsChanged(PlayerInput player)
19712029
{
1972-
messages.Add(new Message { name = "OnControlsChanged", value = player});
2030+
messages?.Add(new Message { name = "OnControlsChanged", value = player});
19732031
}
19742032

19752033
// ReSharper disable once UnusedMember.Local
19762034
public void OnPlayerJoined(PlayerInput player)
19772035
{
1978-
messages.Add(new Message { name = "OnPlayerJoined", value = player});
2036+
messages?.Add(new Message { name = "OnPlayerJoined", value = player});
19792037
}
19802038

19812039
// ReSharper disable once UnusedMember.Local
19822040
public void OnPlayerLeft(PlayerInput player)
19832041
{
1984-
messages.Add(new Message { name = "OnPlayerLeft", value = player});
2042+
messages?.Add(new Message { name = "OnPlayerLeft", value = player});
19852043
}
19862044
}
19872045

@@ -1990,7 +2048,7 @@ public void OnPlayerLeft(PlayerInput player)
19902048
[Preserve]
19912049
private class PlayerInputEventListener : MonoBehaviour, IListener
19922050
{
1993-
public List<Message> messages { get; } = new List<Message>();
2051+
public List<Message> messages { get; set; } = new List<Message>();
19942052

19952053
public void OnEnable()
19962054
{
@@ -2031,40 +2089,40 @@ private void SetUpActionEvents(PlayerInput player)
20312089

20322090
private void OnFireEvent(InputAction.CallbackContext context)
20332091
{
2034-
messages.Add(new Message($"Fire {context.phase}", context.ReadValueAsObject()));
2092+
messages?.Add(new Message($"Fire {context.phase}", context.ReadValueAsObject()));
20352093
}
20362094

20372095
private void OnLookEvent(InputAction.CallbackContext context)
20382096
{
2039-
messages.Add(new Message($"Look {context.phase}", context.ReadValueAsObject()));
2097+
messages?.Add(new Message($"Look {context.phase}", context.ReadValueAsObject()));
20402098
}
20412099

20422100
private void OnMoveEvent(InputAction.CallbackContext context)
20432101
{
2044-
messages.Add(new Message($"Move {context.phase}", context.ReadValueAsObject()));
2102+
messages?.Add(new Message($"Move {context.phase}", context.ReadValueAsObject()));
20452103
}
20462104

20472105
private void OnDeviceLost(PlayerInput player)
20482106
{
2049-
messages.Add(new Message("OnDeviceLost", player));
2107+
messages?.Add(new Message("OnDeviceLost", player));
20502108
}
20512109

20522110
private void OnDeviceRegained(PlayerInput player)
20532111
{
2054-
messages.Add(new Message("OnDeviceRegained", player));
2112+
messages?.Add(new Message("OnDeviceRegained", player));
20552113
}
20562114

20572115
private void OnControlsChanged(PlayerInput player)
20582116
{
2059-
messages.Add(new Message("OnControlsChanged", player));
2117+
messages?.Add(new Message("OnControlsChanged", player));
20602118
}
20612119
}
20622120

20632121
// il2cpp internally crashes if this gets stripped. Need to investigate, but for now we preserve it.
20642122
[Preserve]
20652123
private class PlayerInputCSharpEventListener : MonoBehaviour, IListener
20662124
{
2067-
public List<Message> messages { get; } = new List<Message>();
2125+
public List<Message> messages { get; set; } = new List<Message>();
20682126

20692127
public void OnEnable()
20702128
{
@@ -2079,30 +2137,30 @@ public void OnEnable()
20792137

20802138
private void OnAction(InputAction.CallbackContext context)
20812139
{
2082-
messages.Add(new Message($"{context.action.name} {context.phase}", context.ReadValueAsObject()));
2140+
messages?.Add(new Message($"{context.action.name} {context.phase}", context.ReadValueAsObject()));
20832141
}
20842142

20852143
private void OnDeviceLost(PlayerInput player)
20862144
{
2087-
messages.Add(new Message("OnDeviceLost", player));
2145+
messages?.Add(new Message("OnDeviceLost", player));
20882146
}
20892147

20902148
private void OnDeviceRegained(PlayerInput player)
20912149
{
2092-
messages.Add(new Message("OnDeviceRegained", player));
2150+
messages?.Add(new Message("OnDeviceRegained", player));
20932151
}
20942152

20952153
private void OnControlsChanged(PlayerInput player)
20962154
{
2097-
messages.Add(new Message("OnControlsChanged", player));
2155+
messages?.Add(new Message("OnControlsChanged", player));
20982156
}
20992157
}
21002158

21012159
// il2cpp internally crashes if this gets stripped. Need to investigate, but for now we preserve it.
21022160
[Preserve]
21032161
private class PlayerManagerEventListener : MonoBehaviour, IListener
21042162
{
2105-
public List<Message> messages { get; } = new List<Message>();
2163+
public List<Message> messages { get; set; } = new List<Message>();
21062164

21072165
public void OnEnable()
21082166
{
@@ -2128,7 +2186,7 @@ private void OnPlayerLeft(PlayerInput player)
21282186
[Preserve]
21292187
private class PlayerManagerCSharpEventListener : MonoBehaviour, IListener
21302188
{
2131-
public List<Message> messages { get; } = new List<Message>();
2189+
public List<Message> messages { get; set; } = new List<Message>();
21322190

21332191
public void OnEnable()
21342192
{
@@ -2141,12 +2199,12 @@ public void OnEnable()
21412199

21422200
private void OnPlayerJoined(PlayerInput player)
21432201
{
2144-
messages.Add(new Message("OnPlayerJoined", player));
2202+
messages?.Add(new Message("OnPlayerJoined", player));
21452203
}
21462204

21472205
private void OnPlayerLeft(PlayerInput player)
21482206
{
2149-
messages.Add(new Message("OnPlayerLeft", player));
2207+
messages?.Add(new Message("OnPlayerLeft", player));
21502208
}
21512209
}
21522210
}

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ however, it has to be formatted properly to pass verification tests.
2121
#### Actions
2222

2323
- Controls are now re-resolved after adding or removing bindings from actions ([case 1218544](https://issuetracker.unity3d.com/issues/input-system-package-does-not-re-resolve-bindings-when-adding-a-new-binding-to-a-map-that-has-already-generated-its-state)).
24+
- Can now have spaces and special characters in action names when using `PlayerInput` with the `SendMessages` or `BroadcastMessages` behavior. Previously, an incorrect method name was generated (fix contributed by [BHSPitMonkey](https://github.com/BHSPitMonkey) in [#1022](https://github.com/Unity-Technologies/InputSystem/pull/1022); [case 1214519](https://issuetracker.unity3d.com/issues/player-input-send-messages-wont-trigger-when-input-action-name-contains-spaces)).
2425
- Adding a new action now sets `expectedControlType` to `Button` as expected ([case 1221015](https://issuetracker.unity3d.com/issues/input-system-default-value-of-expectedcontroltype-is-not-being-set-when-creating-a-new-action)).
2526
- Player joins with `PlayerInputManager` from button presses no longer fail if there are multiple devices of the same type present and the join was not on the first gamepad ([case 226920](https://fogbugz.unity3d.com/f/cases/1226920/)).
2627
- `PlayerInputEditor` no longer leads to the player's `InputActionAsset` mistakenly getting replaced with a clone when the inspector is open on a `PlayerInput` component ([case 1228636](https://issuetracker.unity3d.com/issues/action-map-gets-lost-on-play-when-prefab-is-highlighted-in-inspector)).

Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,9 +1298,7 @@ private void CacheMessageNames()
12981298
{
12991299
action.MakeSureIdIsInPlace();
13001300

1301-
var name = action.name;
1302-
if (char.IsLower(name[0]))
1303-
name = char.ToUpper(name[0]) + name.Substring(1);
1301+
var name = CSharpCodeHelpers.MakeTypeName(action.name);
13041302
m_ActionMessageNames[action.m_Id] = "On" + name;
13051303
}
13061304
}

Packages/com.unity.inputsystem/InputSystem/Utilities/CSharpCodeHelpers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Linq;
33
using System.Text;
44

5+
////REVIEW: this seems like it should be #if UNITY_EDITOR
6+
57
namespace UnityEngine.InputSystem.Utilities
68
{
79
internal static class CSharpCodeHelpers

0 commit comments

Comments
 (0)