|
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() |
|
{ |
|
var objects = GameObject.FindObjectsOfType<GameObject>(); |
|
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 |
|
} |
|
} |
|
|