using System;
using System.Collections.Generic;
using UnityEngine;
namespace MLAgents
{
///
/// Ray perception component. Attach this to agents to enable "local perception"
/// via the use of ray casts directed outward from the agent.
///
public class RayPerception3D : RayPerception
{
Vector3 m_EndPosition;
RaycastHit m_Hit;
private float[] m_SubList;
///
/// Creates perception vector to be used as part of an observation of an agent.
/// Each ray in the rayAngles array adds a sublist of data to the observation.
/// The sublist contains the observation data for a single ray. The list is composed of the following:
/// 1. A one-hot encoding for detectable objects. For example, if detectableObjects.Length = n, the
/// first n elements of the sublist will be a one-hot encoding of the detectableObject that was hit, or
/// all zeroes otherwise.
/// 2. The 'length' element of the sublist will be 1 if the ray missed everything, or 0 if it hit
/// something (detectable or not).
/// 3. The 'length+1' element of the sublist will contain the normalised distance to the object hit.
/// NOTE: Only objects with tags in the detectableObjects array will have a distance set.
///
/// The partial vector observation corresponding to the set of rays
/// Radius of rays
/// Angles of rays (starting from (1,0) on unit circle).
/// List of tags which correspond to object types agent can see
/// Starting height offset of ray from center of agent.
/// Ending height offset of ray from center of agent.
public override List Perceive(float rayDistance,
float[] rayAngles, string[] detectableObjects,
float startOffset, float endOffset)
{
if (m_SubList == null || m_SubList.Length != detectableObjects.Length + 2)
m_SubList = new float[detectableObjects.Length + 2];
m_PerceptionBuffer.Clear();
m_PerceptionBuffer.Capacity = m_SubList.Length * rayAngles.Length;
// For each ray sublist stores categorical information on detected object
// along with object distance.
foreach (var angle in rayAngles)
{
m_EndPosition = transform.TransformDirection(
PolarToCartesian(rayDistance, angle));
m_EndPosition.y = endOffset;
if (Application.isEditor)
{
Debug.DrawRay(transform.position + new Vector3(0f, startOffset, 0f),
m_EndPosition, Color.black, 0.01f, true);
}
Array.Clear(m_SubList, 0, m_SubList.Length);
if (Physics.SphereCast(transform.position +
new Vector3(0f, startOffset, 0f), 0.5f,
m_EndPosition, out m_Hit, rayDistance))
{
for (var i = 0; i < detectableObjects.Length; i++)
{
if (m_Hit.collider.gameObject.CompareTag(detectableObjects[i]))
{
m_SubList[i] = 1;
m_SubList[detectableObjects.Length + 1] = m_Hit.distance / rayDistance;
break;
}
}
}
else
{
m_SubList[detectableObjects.Length] = 1f;
}
Utilities.AddRangeNoAlloc(m_PerceptionBuffer, m_SubList);
}
return m_PerceptionBuffer;
}
///
/// Converts polar coordinate to cartesian coordinate.
///
public static Vector3 PolarToCartesian(float radius, float angle)
{
var x = radius * Mathf.Cos(DegreeToRadian(angle));
var z = radius * Mathf.Sin(DegreeToRadian(angle));
return new Vector3(x, 0f, z);
}
}
}