@@ -42,6 +42,7 @@ internal class UITests : CoreTestsFixture
4242 private struct TestObjects
4343 {
4444 public Camera camera ;
45+ public Canvas canvas ;
4546 public InputSystemUIInputModule uiModule ;
4647 public TestEventSystem eventSystem ;
4748 public GameObject parentGameObject ;
@@ -102,7 +103,7 @@ private static TestObjects CreateUIScene()
102103 // Set up a InputSystemUIInputModule with a full roster of actions and inputs
103104 // and then see if we can generate all the various events expected by the UI
104105 // from activity on input devices.
105- private static TestObjects CreateTestUI ( Rect viewport = default , bool noFirstSelected = false , string namePrefix = "" )
106+ private static TestObjects CreateTestUI ( Rect viewport = default , bool noFirstSelected = false , string namePrefix = "" , bool makeSelectable = false )
106107 {
107108 var objects = new TestObjects ( ) ;
108109
@@ -126,6 +127,7 @@ private static TestObjects CreateTestUI(Rect viewport = default, bool noFirstSel
126127 canvasObject . AddComponent < GraphicRaycaster > ( ) ;
127128 canvasObject . AddComponent < TrackedDeviceRaycaster > ( ) ;
128129 canvas . worldCamera = objects . camera ;
130+ objects . canvas = canvas ;
129131
130132 // Set up a GameObject hierarchy that we send events to. In a real setup,
131133 // this would be a hierarchy involving UI components.
@@ -141,13 +143,17 @@ private static TestObjects CreateTestUI(Rect viewport = default, bool noFirstSel
141143 leftChildGameObject . AddComponent < Image > ( ) ;
142144 objects . leftChildReceiver = leftChildGameObject . AddComponent < UICallbackReceiver > ( ) ;
143145 objects . leftGameObject = leftChildGameObject ;
146+ if ( makeSelectable )
147+ leftChildGameObject . AddComponent < Selectable > ( ) ;
144148
145149 var rightChildGameObject = new GameObject ( namePrefix + "Right Child" ) ;
146150 rightChildGameObject . SetActive ( false ) ;
147151 var rightChildTransform = rightChildGameObject . AddComponent < RectTransform > ( ) ;
148152 rightChildGameObject . AddComponent < Image > ( ) ;
149153 objects . rightChildReceiver = rightChildGameObject . AddComponent < UICallbackReceiver > ( ) ;
150154 objects . rightGameObject = rightChildGameObject ;
155+ if ( makeSelectable )
156+ rightChildGameObject . AddComponent < Selectable > ( ) ;
151157
152158 parentTransform . SetParent ( canvasObject . transform , worldPositionStays : false ) ;
153159 leftChildTransform . SetParent ( parentTransform , worldPositionStays : false ) ;
@@ -2451,15 +2457,29 @@ public IEnumerator UI_CanOperateMultiplayerUIGloballyUsingMouse()
24512457
24522458#endif
24532459
2454- [ UnityTest ]
2455- [ Category ( "UI" ) ]
24562460 // Check that two players can have separate UI and control it using separate gamepads, using
24572461 // MultiplayerEventSystem.
2462+ [ UnityTest ]
2463+ [ Category ( "UI" ) ]
24582464 public IEnumerator UI_CanOperateMultiplayerUILocallyUsingGamepads ( )
24592465 {
24602466 // Create devices.
24612467 var gamepads = new [ ] { InputSystem . AddDevice < Gamepad > ( ) , InputSystem . AddDevice < Gamepad > ( ) } ;
2462- var players = new [ ] { CreateTestUI ( new Rect ( 0 , 0 , 0.5f , 1 ) ) , CreateTestUI ( new Rect ( 0.5f , 0 , 0.5f , 1 ) ) } ;
2468+
2469+ // Create scene with side-by-side split-screen.
2470+ var players = new [ ]
2471+ {
2472+ CreateTestUI ( new Rect ( 0 , 0 , 0.5f , 1 ) , namePrefix : "Player1" , makeSelectable : true ) , // Left
2473+ CreateTestUI ( new Rect ( 0.5f , 0 , 0.5f , 1 ) , namePrefix : "Player2" , makeSelectable : true ) // Right
2474+ } ;
2475+
2476+ // Offset player #2's canvas by moving its camera such that the resulting UI will be to the *right*
2477+ // of that of player #1. This is important as it will, by default, make player #2's UI navigatable
2478+ // from player #1 using the navigation logic in Selectable.
2479+ var screenWidthInWorldSpace =
2480+ Mathf . Abs ( players [ 1 ] . camera . ScreenToWorldPoint ( new Vector3 ( 0 , 0 , players [ 1 ] . canvas . planeDistance ) ) . x -
2481+ players [ 1 ] . camera . ScreenToWorldPoint ( new Vector3 ( Screen . width , 0 , players [ 1 ] . canvas . planeDistance ) ) . x ) ;
2482+ players [ 1 ] . camera . transform . Translate ( new Vector3 ( screenWidthInWorldSpace , 0 , 0 ) ) ;
24632483
24642484 for ( var i = 0 ; i < 2 ; i ++ )
24652485 {
@@ -2469,9 +2489,9 @@ public IEnumerator UI_CanOperateMultiplayerUILocallyUsingGamepads()
24692489 // Create actions.
24702490 var map = new InputActionMap ( "map" ) ;
24712491 asset . AddActionMap ( map ) ;
2472- var moveAction = map . AddAction ( "move" , type : InputActionType . PassThrough ) ;
2473- var submitAction = map . AddAction ( "submit" , type : InputActionType . PassThrough ) ;
2474- var cancelAction = map . AddAction ( "cancel" , type : InputActionType . PassThrough ) ;
2492+ var moveAction = map . AddAction ( "move" , type : InputActionType . Value ) ;
2493+ var submitAction = map . AddAction ( "submit" , type : InputActionType . Button ) ;
2494+ var cancelAction = map . AddAction ( "cancel" , type : InputActionType . Button ) ;
24752495
24762496 // Create bindings.
24772497 moveAction . AddBinding ( gamepads [ i ] . leftStick ) ;
@@ -2483,58 +2503,83 @@ public IEnumerator UI_CanOperateMultiplayerUILocallyUsingGamepads()
24832503 players [ i ] . uiModule . submit = InputActionReference . Create ( submitAction ) ;
24842504 players [ i ] . uiModule . cancel = InputActionReference . Create ( cancelAction ) ;
24852505
2486- players [ i ] . leftChildReceiver . moveTo = players [ i ] . rightGameObject ;
2487- players [ i ] . rightChildReceiver . moveTo = players [ i ] . leftGameObject ;
2488-
24892506 // Enable the whole thing.
24902507 map . Enable ( ) ;
24912508 }
24922509
2493- // We need to wait a frame to let the underlying canvas update and properly order the graphics images for raycasting.
24942510 yield return null ;
24952511
24962512 Assert . That ( players [ 0 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 0 ] . leftGameObject ) ) ;
24972513 Assert . That ( players [ 1 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 1 ] . leftGameObject ) ) ;
24982514
2499- // Reset initial selection
25002515 players [ 0 ] . leftChildReceiver . events . Clear ( ) ;
25012516 players [ 1 ] . leftChildReceiver . events . Clear ( ) ;
25022517
2503- // Check Player 0 Move Axes
2504- InputSystem . QueueDeltaStateEvent ( gamepads [ 0 ] . leftStick , new Vector2 ( 1.0f , 0.0f ) ) ;
2505-
2518+ // Move right on player #1's gamepad.
2519+ Set ( gamepads [ 0 ] . leftStick , Vector2 . right ) ;
25062520 yield return null ;
25072521
2522+ // Player #1 should have moved from left to right object.
25082523 Assert . That ( players [ 0 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 0 ] . rightGameObject ) ) ;
25092524 Assert . That ( players [ 1 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 1 ] . leftGameObject ) ) ;
25102525
2511- Assert . That ( players [ 0 ] . leftChildReceiver . events , Has . Count . EqualTo ( 2 ) ) ;
2512- Assert . That ( players [ 0 ] . leftChildReceiver . events [ 0 ] . type , Is . EqualTo ( EventType . Move ) ) ;
2513- Assert . That ( players [ 0 ] . leftChildReceiver . events [ 1 ] . type , Is . EqualTo ( EventType . Deselect ) ) ;
2514- players [ 0 ] . leftChildReceiver . events . Clear ( ) ;
2526+ Assert . That ( players [ 0 ] . leftChildReceiver . events ,
2527+ EventSequence (
2528+ OneEvent ( "type" , EventType . Move ) ,
2529+ OneEvent ( "type" , EventType . Deselect ) ) ) ;
2530+ Assert . That ( players [ 0 ] . rightChildReceiver . events ,
2531+ EventSequence (
2532+ OneEvent ( "type" , EventType . Select ) ) ) ;
25152533
2516- Assert . That ( players [ 0 ] . rightChildReceiver . events , Has . Count . EqualTo ( 1 ) ) ;
2517- Assert . That ( players [ 0 ] . rightChildReceiver . events [ 0 ] . type , Is . EqualTo ( EventType . Select ) ) ;
2534+ players [ 0 ] . leftChildReceiver . events . Clear ( ) ;
25182535 players [ 0 ] . rightChildReceiver . events . Clear ( ) ;
25192536
2537+ // No change for player #2.
2538+ Assert . That ( players [ 1 ] . leftChildReceiver . events , Is . Empty ) ;
2539+ Assert . That ( players [ 1 ] . rightChildReceiver . events , Is . Empty ) ;
2540+
2541+ // https://fogbugz.unity3d.com/f/cases/1306361/
2542+ // Move right on player #1's gamepad AGAIN. This should *not* cross
2543+ // over to player #2's UI but should instead not result in any selection change.
2544+ Set ( gamepads [ 0 ] . leftStick , Vector2 . zero ) ;
25202545 yield return null ;
2546+ Set ( gamepads [ 0 ] . leftStick , Vector2 . right ) ;
2547+ yield return null ;
2548+
2549+ Assert . That ( players [ 0 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 0 ] . rightGameObject ) ) ;
2550+ Assert . That ( players [ 1 ] . eventSystem . currentSelectedGameObject , Is . SameAs ( players [ 1 ] . leftGameObject ) ) ;
2551+
2552+ Assert . That ( players [ 0 ] . leftChildReceiver . events , Is . Empty ) ;
2553+ Assert . That ( players [ 0 ] . rightChildReceiver . events ,
2554+ EventSequence (
2555+ OneEvent ( "type" , EventType . Move ) ) ) ; // OnMove will still get called to *attempt* a move.
2556+
2557+ players [ 0 ] . leftChildReceiver . events . Clear ( ) ;
2558+ players [ 0 ] . rightChildReceiver . events . Clear ( ) ;
2559+
2560+ // No change for player #2.
2561+ Assert . That ( players [ 1 ] . leftChildReceiver . events , Is . Empty ) ;
2562+ Assert . That ( players [ 1 ] . rightChildReceiver . events , Is . Empty ) ;
2563+
2564+ Set ( gamepads [ 0 ] . leftStick , Vector2 . zero ) ;
25212565
25222566 // Check Player 0 Submit
25232567 PressAndRelease ( gamepads [ 0 ] . buttonSouth ) ;
2524-
25252568 yield return null ;
25262569
2527- Assert . That ( players [ 0 ] . rightChildReceiver . events , Has . Count . EqualTo ( 1 ) ) ;
2528- Assert . That ( players [ 0 ] . rightChildReceiver . events [ 0 ] . type , Is . EqualTo ( EventType . Submit ) ) ;
2570+ Assert . That ( players [ 0 ] . rightChildReceiver . events ,
2571+ EventSequence ( OneEvent ( "type" , EventType . Submit ) ) ) ;
2572+ Assert . That ( players [ 1 ] . leftChildReceiver . events , Is . Empty ) ;
2573+
25292574 players [ 0 ] . rightChildReceiver . events . Clear ( ) ;
25302575
25312576 // Check Player 1 Submit
25322577 PressAndRelease ( gamepads [ 1 ] . buttonSouth ) ;
2533-
25342578 yield return null ;
25352579
2536- Assert . That ( players [ 1 ] . leftChildReceiver . events , Has . Count . EqualTo ( 1 ) ) ;
2537- Assert . That ( players [ 1 ] . leftChildReceiver . events [ 0 ] . type , Is . EqualTo ( EventType . Submit ) ) ;
2580+ Assert . That ( players [ 1 ] . leftChildReceiver . events ,
2581+ EventSequence ( OneEvent ( "type" , EventType . Submit ) ) ) ;
2582+ Assert . That ( players [ 0 ] . rightChildReceiver . events , Is . Empty ) ;
25382583 }
25392584
25402585 [ UnityTest ]
@@ -3718,7 +3763,6 @@ public override string ToString()
37183763 }
37193764
37203765 public List < Event > events = new List < Event > ( ) ;
3721- public GameObject moveTo ;
37223766
37233767 public void OnPointerClick ( PointerEventData eventData )
37243768 {
@@ -3757,8 +3801,6 @@ public void OnPointerMove(PointerEventData eventData)
37573801 public void OnMove ( AxisEventData eventData )
37583802 {
37593803 events . Add ( new Event ( EventType . Move , CloneAxisEventData ( eventData ) ) ) ;
3760- if ( moveTo != null )
3761- EventSystem . current . SetSelectedGameObject ( moveTo , eventData ) ;
37623804 }
37633805
37643806 public void OnSubmit ( BaseEventData eventData )
0 commit comments