Skip to content

Commit 49b9bf3

Browse files
Merge pull request #1101 from Zod-/UI-Raycasts
Fix random focus change on layered canvas ui elements
2 parents e32e1e1 + 41f6051 commit 49b9bf3

19 files changed

+3431
-65
lines changed

Assets/HoloToolkit-Examples/Input/Scenes/CanvasUiRaycasts.unity

Lines changed: 2908 additions & 0 deletions
Large diffs are not rendered by default.

Assets/HoloToolkit-Examples/Input/Scenes/CanvasUiRaycasts.unity.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using UnityEngine;
5+
6+
namespace HoloToolkit.Unity.Tests
7+
{
8+
public class RotateMinMax : MonoBehaviour
9+
{
10+
[SerializeField] private float _minAngle;
11+
[SerializeField] private float _maxAngle;
12+
[SerializeField] private float _step;
13+
14+
private void Update()
15+
{
16+
transform.Rotate(Vector3.up, _step);
17+
if (transform.localRotation.eulerAngles.y < _minAngle || transform.localRotation.eulerAngles.y > _maxAngle)
18+
{
19+
_step *= -1;
20+
}
21+
}
22+
}
23+
}

Assets/HoloToolkit-Examples/Input/Scripts/RotateMinMax.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
namespace HoloToolkit.Unity.Tests
5+
{
6+
public class EnumerableExtensionsTests
7+
{
8+
[Test]
9+
public void TestMaxOrDefault()
10+
{
11+
Assert.Throws<ArgumentNullException>(() => ((int[])null).MaxOrDefault());
12+
}
13+
14+
[Test]
15+
public void TestMaxOrDefaultEmpty()
16+
{
17+
var items = new int[0];
18+
19+
Assert.That(items.MaxOrDefault(), Is.Zero);
20+
}
21+
22+
[Test]
23+
public void TestMaxOrDefaultUnordered()
24+
{
25+
var items = new[]
26+
{
27+
-5, -20, 100, 5
28+
};
29+
30+
Assert.That(items.MaxOrDefault(), Is.EqualTo(100));
31+
}
32+
33+
[Test]
34+
public void TestMaxOrDefaultOrdered()
35+
{
36+
var items = new[]
37+
{
38+
-20, -5, 5, 100
39+
};
40+
41+
Assert.That(items.MaxOrDefault(), Is.EqualTo(100));
42+
}
43+
}
44+
}

Assets/HoloToolkit-UnitTests/Editor/Utilities/Extensions/EnumerableExtensionsTests.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using NUnit.Framework;
5+
using UnityEngine;
6+
7+
namespace HoloToolkit.Unity.Tests
8+
{
9+
public class LayerExtensionsTests
10+
{
11+
[Test]
12+
public void TestIsLayerInLayerMask()
13+
{
14+
var waterLayer = LayerMask.NameToLayer("Water");
15+
var mask = Physics.AllLayers;
16+
Assert.That(waterLayer.IsInLayerMask(mask), Is.True);
17+
}
18+
19+
[Test]
20+
public void TestIsLayerNotInLayerMask()
21+
{
22+
var waterLayer = LayerMask.NameToLayer("Water");
23+
var mask = LayerMask.GetMask("Default");
24+
Assert.That(waterLayer.IsInLayerMask(mask), Is.False);
25+
}
26+
27+
[Test]
28+
public void TestFindLayerListIndexFirst()
29+
{
30+
var waterLayer = LayerMask.NameToLayer("Water");
31+
var masks = new LayerMask[]
32+
{
33+
LayerMask.GetMask("Water"),
34+
LayerMask.GetMask("Default"),
35+
LayerMask.GetMask("UI"),
36+
};
37+
Assert.That(waterLayer.FindLayerListIndex(masks), Is.EqualTo(0));
38+
}
39+
40+
[Test]
41+
public void TestFindLayerListIndexLast()
42+
{
43+
var waterLayer = LayerMask.NameToLayer("Water");
44+
var masks = new LayerMask[]
45+
{
46+
LayerMask.GetMask("Default"),
47+
LayerMask.GetMask("UI"),
48+
LayerMask.GetMask("Water")
49+
};
50+
Assert.That(waterLayer.FindLayerListIndex(masks), Is.EqualTo(masks.Length - 1));
51+
}
52+
53+
[Test]
54+
public void TestFindLayerListIndexMiddle()
55+
{
56+
var waterLayer = LayerMask.NameToLayer("Water");
57+
var masks = new LayerMask[]
58+
{
59+
LayerMask.GetMask("Default"),
60+
LayerMask.GetMask("Water"),
61+
LayerMask.GetMask("UI")
62+
};
63+
Assert.That(waterLayer.FindLayerListIndex(masks), Is.EqualTo(1));
64+
}
65+
66+
[Test]
67+
public void TestFindLayerListIndexNone()
68+
{
69+
var waterLayer = LayerMask.NameToLayer("Water");
70+
var masks = new LayerMask[]
71+
{
72+
LayerMask.GetMask("Default"),
73+
LayerMask.GetMask("UI")
74+
};
75+
Assert.That(waterLayer.FindLayerListIndex(masks), Is.EqualTo(-1));
76+
}
77+
78+
[Test]
79+
public void TestFindLayerListIndexEmpty()
80+
{
81+
var waterLayer = LayerMask.NameToLayer("Water");
82+
var masks = new LayerMask[] { };
83+
Assert.That(waterLayer.FindLayerListIndex(masks), Is.EqualTo(-1));
84+
}
85+
86+
[Test]
87+
public void TestCombineLayerMasksMultiple()
88+
{
89+
var masks = new LayerMask[]
90+
{
91+
LayerMask.GetMask("Ignore Raycast"),
92+
LayerMask.GetMask("TransparentFX"),
93+
LayerMask.GetMask("UI")
94+
};
95+
var combinedMask = LayerMask.GetMask("Ignore Raycast", "TransparentFX", "UI");
96+
Assert.That(masks.Combine(), Is.EqualTo(combinedMask));
97+
}
98+
99+
[Test]
100+
public void TestCombineLayerMasksOne()
101+
{
102+
var masks = new LayerMask[]
103+
{
104+
LayerMask.GetMask("UI")
105+
};
106+
var combinedMask = LayerMask.GetMask("UI");
107+
Assert.That(masks.Combine(), Is.EqualTo(combinedMask));
108+
}
109+
110+
[Test]
111+
public void TestCombineLayerMasksEmpty()
112+
{
113+
var masks = new LayerMask[] { };
114+
var combinedMask = LayerMask.GetMask();
115+
Assert.That(masks.Combine(), Is.EqualTo(combinedMask));
116+
}
117+
}
118+
}

Assets/HoloToolkit-UnitTests/Editor/Utilities/Extensions/LayerExtensionsTests.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/HoloToolkit-UnitTests/Editor/Utilities/Extensions/TransformExtensionsTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,5 +295,44 @@ public void BoundsColliderExcludeOrigin()
295295
var sut = cube.transform.GetColliderBounds();
296296
Assert.That(sut, Is.EqualTo(new Bounds(Vector3.one * 2, Vector3.one)));
297297
}
298+
299+
[Test]
300+
public void TestUnrelatedParentChild()
301+
{
302+
var transform1 = new GameObject().transform;
303+
var transform2 = new GameObject().transform;
304+
305+
Assert.That(transform1.IsParentOrChildOf(transform2), Is.False);
306+
Assert.That(transform2.IsParentOrChildOf(transform1), Is.False);
307+
}
308+
309+
[Test]
310+
public void TestRelatedParentChild()
311+
{
312+
var transform1 = new GameObject().transform;
313+
var transform2 = Object.Instantiate(empty, transform1).transform;
314+
315+
Assert.That(transform1.IsParentOrChildOf(transform2), Is.True);
316+
Assert.That(transform2.IsParentOrChildOf(transform1), Is.True);
317+
}
318+
319+
[Test]
320+
public void TestNestedRelatedParentChild()
321+
{
322+
var root = new GameObject().transform;
323+
var transform2 = Object.Instantiate(empty, root).transform;
324+
var transform3 = Object.Instantiate(empty, transform2).transform;
325+
326+
Assert.That(transform2.IsParentOrChildOf(transform3), Is.True);
327+
Assert.That(transform3.IsParentOrChildOf(transform2), Is.True);
328+
}
329+
330+
[Test]
331+
public void TestEqualParentChild()
332+
{
333+
var root = new GameObject().transform;
334+
335+
Assert.That(root.IsParentOrChildOf(root), Is.True);
336+
}
298337
}
299338
}

Assets/HoloToolkit/Input/Scripts/Gaze/GazeManager.cs

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,6 @@ public class GazeManager : Singleton<GazeManager>
9292
/// </summary>
9393
public PointerEventData UnityUIPointerEvent { get; private set; }
9494

95-
/// <summary>
96-
/// Cached results of raycast results.
97-
/// </summary>
98-
private List<RaycastResult> raycastResultList = new List<RaycastResult>();
99-
10095
protected override void Awake()
10196
{
10297
base.Awake();
@@ -225,9 +220,7 @@ private void RaycastUnityUI()
225220
UnityUIPointerEvent.position = cursorScreenPos;
226221

227222
// Graphics raycast
228-
raycastResultList.Clear();
229-
EventSystem.current.RaycastAll(UnityUIPointerEvent, raycastResultList);
230-
RaycastResult uiRaycastResult = FindClosestRaycastHitInLayerMasks(raycastResultList, RaycastLayerMasks);
223+
RaycastResult uiRaycastResult = EventSystem.current.Raycast(UnityUIPointerEvent, RaycastLayerMasks);
231224
UnityUIPointerEvent.pointerCurrentRaycast = uiRaycastResult;
232225

233226
// If we have a raycast result, check if we need to overwrite the 3D raycast info
@@ -240,8 +233,8 @@ private void RaycastUnityUI()
240233
if (RaycastLayerMasks.Length > 1)
241234
{
242235
// Get the index in the prioritized layer masks
243-
int uiLayerIndex = FindLayerListIndex(uiRaycastResult.gameObject.layer, RaycastLayerMasks);
244-
int threeDLayerIndex = FindLayerListIndex(hitInfo.collider.gameObject.layer, RaycastLayerMasks);
236+
int uiLayerIndex = uiRaycastResult.gameObject.layer.FindLayerListIndex(RaycastLayerMasks);
237+
int threeDLayerIndex = hitInfo.collider.gameObject.layer.FindLayerListIndex(RaycastLayerMasks);
245238

246239
if (threeDLayerIndex > uiLayerIndex)
247240
{
@@ -285,60 +278,6 @@ private void RaycastUnityUI()
285278

286279
#region Helpers
287280

288-
/// <summary>
289-
/// Find the closest raycast hit in the list of RaycastResults that is also included in the LayerMask list.
290-
/// </summary>
291-
/// <param name="candidates">List of RaycastResults from a Unity UI raycast</param>
292-
/// <param name="layerMaskList">List of layers to support</param>
293-
/// <returns>RaycastResult if hit, or an empty RaycastResult if nothing was hit</returns>
294-
private RaycastResult FindClosestRaycastHitInLayerMasks(List<RaycastResult> candidates, LayerMask[] layerMaskList)
295-
{
296-
int combinedLayerMask = 0;
297-
for (int i = 0; i < layerMaskList.Length; i++)
298-
{
299-
combinedLayerMask = combinedLayerMask | layerMaskList[i].value;
300-
}
301-
302-
RaycastResult? minHit = null;
303-
for (var i = 0; i < candidates.Count; ++i)
304-
{
305-
if (candidates[i].gameObject == null || !IsLayerInLayerMask(candidates[i].gameObject.layer, combinedLayerMask))
306-
{
307-
continue;
308-
}
309-
if (minHit == null || candidates[i].distance < minHit.Value.distance)
310-
{
311-
minHit = candidates[i];
312-
}
313-
}
314-
315-
return minHit ?? new RaycastResult();
316-
}
317-
318-
/// <summary>
319-
/// Look through the layerMaskList and find the index in that list for which the supplied layer is part of
320-
/// </summary>
321-
/// <param name="layer">Layer to search for</param>
322-
/// <param name="layerMaskList">List of LayerMasks to search</param>
323-
/// <returns>LayerMaskList index, or -1 for not found</returns>
324-
private int FindLayerListIndex(int layer, LayerMask[] layerMaskList)
325-
{
326-
for (int i = 0; i < layerMaskList.Length; i++)
327-
{
328-
if (IsLayerInLayerMask(layer, layerMaskList[i].value))
329-
{
330-
return i;
331-
}
332-
}
333-
334-
return -1;
335-
}
336-
337-
private bool IsLayerInLayerMask(int layer, int layerMask)
338-
{
339-
return ((1 << layer) & layerMask) != 0;
340-
}
341-
342281
private RaycastHit? PrioritizeHits(RaycastHit[] hits)
343282
{
344283
if (hits.Length == 0)
@@ -355,7 +294,7 @@ private bool IsLayerInLayerMask(int layer, int layerMask)
355294
for (int hitIdx = 0; hitIdx < hits.Length; hitIdx++)
356295
{
357296
RaycastHit hit = hits[hitIdx];
358-
if (IsLayerInLayerMask(hit.transform.gameObject.layer, RaycastLayerMasks[layerMaskIdx]) &&
297+
if (hit.transform.gameObject.layer.IsInLayerMask(RaycastLayerMasks[layerMaskIdx]) &&
359298
(minHit == null || hit.distance < minHit.Value.distance))
360299
{
361300
minHit = hit;

0 commit comments

Comments
 (0)