| using System.Collections.Generic; |
| using NUnit.Framework; |
| using UnityEngine; |
| using Unity.MLAgents.Sensors; |
| using UnityEngine.TestTools; |
|
|
| namespace Unity.MLAgents.Tests |
| { |
| public class RayPerceptionSensorTests |
| { |
| [Test] |
| public void TestGetRayAngles() |
| { |
| var anglesAlternating = RayPerceptionSensorComponentBase.GetRayAnglesAlternating(3, 90f); |
| var expectedAnglesAlternating = new[] { 90f, 60f, 120f, 30f, 150f, 0f, 180f }; |
| Assert.AreEqual(expectedAnglesAlternating.Length, anglesAlternating.Length); |
| for (var i = 0; i < anglesAlternating.Length; i++) |
| { |
| Assert.AreEqual(expectedAnglesAlternating[i], anglesAlternating[i], .01); |
| } |
|
|
| var angles = RayPerceptionSensorComponentBase.GetRayAngles(3, 90f); |
| var expectedAngles = new[] { 0f, 30f, 60f, 90f, 120f, 150f, 180f }; |
| Assert.AreEqual(expectedAngles.Length, angles.Length); |
| for (var i = 0; i < angles.Length; i++) |
| { |
| Assert.AreEqual(expectedAngles[i], angles[i], .01); |
| } |
| } |
| } |
|
|
| public class RayPerception3DTests |
| { |
| [Test] |
| public void TestDefaultLayersAreNegativeFive() |
| { |
| #if MLA_UNITY_PHYSICS_MODULE |
| Assert.IsTrue(Physics.DefaultRaycastLayers == -5); |
| #endif |
| #if MLA_UNITY_PHYSICS2D_MODULE |
| Assert.IsTrue(Physics2D.DefaultRaycastLayers == -5); |
| #endif |
| } |
|
|
| #if MLA_UNITY_PHYSICS_MODULE |
| |
| const string k_CubeTag = "Player"; |
| const string k_SphereTag = "Respawn"; |
|
|
| [TearDown] |
| public void RemoveGameObjects() |
| { |
| #if UNITY_6000_4_OR_NEWER |
| var objects = UnityEngine.Object.FindObjectsByType<GameObject>(FindObjectsInactive.Include); |
| #else |
| var objects = UnityEngine.Object.FindObjectsByType<GameObject>(FindObjectsSortMode.InstanceID); |
| #endif |
| foreach (var o in objects) |
| { |
| UnityEngine.Object.DestroyImmediate(o); |
| } |
| } |
|
|
| void SetupScene() |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
| cube.transform.position = new Vector3(0, 0, 10); |
| cube.tag = k_CubeTag; |
| cube.name = "cube"; |
|
|
| var sphere1 = GameObject.CreatePrimitive(PrimitiveType.Sphere); |
| sphere1.transform.position = new Vector3(-5, 0, 5); |
| sphere1.tag = k_SphereTag; |
| sphere1.name = "sphere1"; |
|
|
| var sphere2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); |
| sphere2.transform.position = new Vector3(5, 0, 5); |
| |
| sphere2.name = "sphere2"; |
|
|
| var sphere3 = GameObject.CreatePrimitive(PrimitiveType.Sphere); |
| sphere3.transform.position = new Vector3(0, 0, -10); |
| sphere3.tag = k_SphereTag; |
| sphere3.name = "sphere3"; |
|
|
|
|
| Physics.SyncTransforms(); |
| } |
|
|
| [Test] |
| public void TestRaycasts() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 1; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
| perception.DetectableTags.Add(k_SphereTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var sensor = perception.CreateSensors()[0]; |
| sensor.Update(); |
|
|
| var expectedObs = (2 * perception.RaysPerDirection + 1) * (perception.DetectableTags.Count + 2); |
| Assert.AreEqual(sensor.GetObservationSpec().Shape[0], expectedObs); |
| var outputBuffer = new float[expectedObs]; |
|
|
| ObservationWriter writer = new ObservationWriter(); |
| writer.SetTarget(outputBuffer, sensor.GetObservationSpec(), 0); |
|
|
| var numWritten = sensor.Write(writer); |
| Assert.AreEqual(numWritten, expectedObs); |
|
|
| |
| |
| |
| |
| |
| |
| Assert.AreEqual(1.0f, outputBuffer[0]); |
| Assert.AreEqual(0.0f, outputBuffer[1]); |
| Assert.AreEqual(0.0f, outputBuffer[2]); |
|
|
| |
| Assert.That( |
| outputBuffer[3], Is.EqualTo((9.5f - castRadius) / perception.RayLength).Within(.0005f) |
| ); |
|
|
| |
| |
| var expectedHitLengthWorldSpace = 5.0f * Mathf.Sqrt(2.0f) - 0.5f - castRadius; |
| Assert.AreEqual(0.0f, outputBuffer[4]); |
| Assert.AreEqual(0.0f, outputBuffer[5]); |
| Assert.AreEqual(0.0f, outputBuffer[6]); |
| Assert.That( |
| outputBuffer[7], Is.EqualTo(expectedHitLengthWorldSpace / perception.RayLength).Within(.0005f) |
| ); |
|
|
| Assert.AreEqual(0.0f, outputBuffer[8]); |
| Assert.AreEqual(1.0f, outputBuffer[9]); |
| Assert.AreEqual(0.0f, outputBuffer[10]); |
| Assert.That( |
| outputBuffer[11], Is.EqualTo(expectedHitLengthWorldSpace / perception.RayLength).Within(.0005f) |
| ); |
| } |
| } |
|
|
| [Test] |
| public void TestRaycastMiss() |
| { |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
| perception.DetectableTags.Add(k_SphereTag); |
|
|
| var sensor = perception.CreateSensors()[0]; |
| sensor.Update(); |
| var expectedObs = (2 * perception.RaysPerDirection + 1) * (perception.DetectableTags.Count + 2); |
| Assert.AreEqual(sensor.GetObservationSpec().Shape[0], expectedObs); |
| var outputBuffer = new float[expectedObs]; |
|
|
| ObservationWriter writer = new ObservationWriter(); |
| writer.SetTarget(outputBuffer, sensor.GetObservationSpec(), 0); |
|
|
| var numWritten = sensor.Write(writer); |
| Assert.AreEqual(numWritten, expectedObs); |
|
|
| |
| Assert.AreEqual(new float[] { 0, 0, 1, 1 }, outputBuffer); |
| } |
|
|
| [Test] |
| public void TestRayFilter() |
| { |
| var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
| cube.transform.position = new Vector3(0, 0, 10); |
| cube.tag = k_CubeTag; |
| cube.name = "cubeFar"; |
|
|
| var cubeFiltered = GameObject.CreatePrimitive(PrimitiveType.Cube); |
| cubeFiltered.transform.position = new Vector3(0, 0, 5); |
| cubeFiltered.tag = k_CubeTag; |
| cubeFiltered.name = "cubeNear"; |
| cubeFiltered.layer = 7; |
|
|
| Physics.SyncTransforms(); |
|
|
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
| perception.RaysPerDirection = 0; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
|
|
| var filterCubeLayers = new[] { false, true }; |
| foreach (var filterCubeLayer in filterCubeLayers) |
| { |
| |
| var layerMask = Physics.DefaultRaycastLayers; |
| if (filterCubeLayer) |
| { |
| layerMask &= ~(1 << cubeFiltered.layer); |
| } |
| perception.RayLayerMask = layerMask; |
|
|
| var sensor = perception.CreateSensors()[0]; |
| sensor.Update(); |
| var expectedObs = (2 * perception.RaysPerDirection + 1) * (perception.DetectableTags.Count + 2); |
| Assert.AreEqual(sensor.GetObservationSpec().Shape[0], expectedObs); |
| var outputBuffer = new float[expectedObs]; |
|
|
| ObservationWriter writer = new ObservationWriter(); |
| writer.SetTarget(outputBuffer, sensor.GetObservationSpec(), 0); |
|
|
| var numWritten = sensor.Write(writer); |
| Assert.AreEqual(numWritten, expectedObs); |
|
|
| if (filterCubeLayer) |
| { |
| |
| Assert.That(outputBuffer[outputBuffer.Length - 1], |
| Is.EqualTo((9.5f - perception.SphereCastRadius) / perception.RayLength).Within(.0005f) |
| ); |
| } |
| else |
| { |
| |
| Assert.That(outputBuffer[outputBuffer.Length - 1], |
| Is.EqualTo((4.5f - perception.SphereCastRadius) / perception.RayLength).Within(.0005f) |
| ); |
| } |
| } |
| } |
|
|
| [Test] |
| public void TestRaycastsScaled() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
| obj.transform.localScale = new Vector3(2, 2, 2); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var sensor = perception.CreateSensors()[0]; |
| sensor.Update(); |
|
|
| var expectedObs = (2 * perception.RaysPerDirection + 1) * (perception.DetectableTags.Count + 2); |
| Assert.AreEqual(sensor.GetObservationSpec().Shape[0], expectedObs); |
| var outputBuffer = new float[expectedObs]; |
|
|
| ObservationWriter writer = new ObservationWriter(); |
| writer.SetTarget(outputBuffer, sensor.GetObservationSpec(), 0); |
|
|
| var numWritten = sensor.Write(writer); |
| Assert.AreEqual(numWritten, expectedObs); |
|
|
| |
| |
| |
| Assert.AreEqual(1.0f, outputBuffer[0]); |
| Assert.AreEqual(0.0f, outputBuffer[1]); |
|
|
| |
| |
| var scaledRayLength = 2 * perception.RayLength; |
| var scaledCastRadius = 2 * castRadius; |
| Assert.That( |
| outputBuffer[2], Is.EqualTo((9.5f - scaledCastRadius) / scaledRayLength).Within(.0005f) |
| ); |
| } |
| } |
|
|
| [Test] |
| public void TestRayZeroLength() |
| { |
| |
| var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
| cube.transform.position = new Vector3(0, 0, .5f); |
| cube.tag = k_CubeTag; |
|
|
| Physics.SyncTransforms(); |
|
|
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
| perception.RaysPerDirection = 0; |
| perception.RayLength = 0.0f; |
| perception.SphereCastRadius = .5f; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
|
|
| { |
| |
|
|
| var sensor = perception.CreateSensors()[0]; |
| sensor.Update(); |
| var expectedObs = (2 * perception.RaysPerDirection + 1) * (perception.DetectableTags.Count + 2); |
| Assert.AreEqual(sensor.GetObservationSpec().Shape[0], expectedObs); |
| var outputBuffer = new float[expectedObs]; |
|
|
| ObservationWriter writer = new ObservationWriter(); |
| writer.SetTarget(outputBuffer, sensor.GetObservationSpec(), 0); |
|
|
| var numWritten = sensor.Write(writer); |
| Assert.AreEqual(numWritten, expectedObs); |
|
|
| |
| Assert.GreaterOrEqual(outputBuffer[2], 0.0f); |
| Assert.LessOrEqual(outputBuffer[2], 1.0f); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticPerceive() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
| perception.DetectableTags.Add(k_SphereTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, false); |
|
|
| Assert.AreEqual(1, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(0, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticPerceiveInvalidTags() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add("Bad tag"); |
| perception.DetectableTags.Add(null); |
| perception.DetectableTags.Add(""); |
| perception.DetectableTags.Add(k_CubeTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
|
|
| |
| |
| LogAssert.Expect(LogType.Error, "Tag: Bad tag is not defined."); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, false); |
|
|
| Assert.AreEqual(1, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(3, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticPerceiveNoTags() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = null; |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, false); |
|
|
| Assert.AreEqual(1, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(-1, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticBatchedPerceive() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 1; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add(k_CubeTag); |
| perception.DetectableTags.Add(k_SphereTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, true); |
|
|
| Assert.AreEqual(3, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(0, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticPerceiveBatchedInvalidTags() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 0; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = new List<string>(); |
| perception.DetectableTags.Add("Bad tag"); |
| perception.DetectableTags.Add(null); |
| perception.DetectableTags.Add(""); |
| perception.DetectableTags.Add(k_CubeTag); |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
|
|
| |
| |
| LogAssert.Expect(LogType.Error, "Tag: Bad tag is not defined."); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, true); |
|
|
| Assert.AreEqual(1, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(3, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestStaticPerceiveBatchedNoTags() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| perception.RaysPerDirection = 1; |
| perception.MaxRayDegrees = 45; |
| perception.RayLength = 20; |
| perception.DetectableTags = null; |
|
|
| var radii = new[] { 0f, .5f }; |
| foreach (var castRadius in radii) |
| { |
| perception.SphereCastRadius = castRadius; |
| var castInput = perception.GetRayPerceptionInput(); |
| var castOutput = RayPerceptionSensor.Perceive(castInput, true); |
|
|
| Assert.AreEqual(3, castOutput.RayOutputs.Length); |
|
|
| |
| Assert.AreEqual("cube", castOutput.RayOutputs[0].HitGameObject.name); |
| Assert.AreEqual(-1, castOutput.RayOutputs[0].HitTagIndex); |
| } |
| } |
|
|
| [Test] |
| public void TestCreateDefault() |
| { |
| SetupScene(); |
| var obj = new GameObject("agent"); |
| var perception = obj.AddComponent<RayPerceptionSensorComponent3D>(); |
|
|
| Assert.DoesNotThrow(() => |
| { |
| perception.CreateSensors(); |
| }); |
| } |
|
|
| #endif |
| } |
| } |
|
|