Ray perception sensor works like this suppose there are 2 tags wall , goal . No of rays is 3
so each ray will have a list of 1's and 0's:
Ray 1 - [(hashitwall) , (hashitgoal) , (hitnothing), (hitdistanceratio)
Ray 2 - [(hashitwall) , (hashitgoal) , (hitnothing), (hitdistanceratio)
Ray 3 - [(hashitwall) , (hashitgoal) , (hitnothing), (hitdistanceratio)
Let's say ray1 hit wall only , ray2 hit goal only ray3 hit nothing
- Ray1 - [1,0,0,0.2f]
- Ray2 - [0,1,0,0.5f]
- Ray3 - [0,0,1,0]
Then we will combine these list into 1 list
finallist = [1,0,0,0.2f , 0,1,0,0.5f , 0,0,1,0]
Strangely but AI is excellent in finding patterns and it will definately find pattern here
using System.Collections.Generic;
using Unity.MLAgents.Policies;
using UnityEngine;
public class CustomRaycast3D : MonoBehaviour
{
[SerializeField] private List<string> detectableTags;
[SerializeField] private float angle = 90;
[SerializeField] private int numberOfRays = 20;
[SerializeField] private float rayDistance = 10f;
[SerializeField] private float sphereCastRadius = 0.5f;
[SerializeField] private BehaviorParameters behaviorParameters;
private void Awake()
{
behaviorParameters.BrainParameters.VectorObservationSize = numberOfRays * (detectableTags.Count + 2) + 3;
}
private void Update()
{
CastRays();
}
public List<float> CastRays()
{
List<float> finalOutput = new List<float>();
for (int i = 0; i < numberOfRays; i++)
{
float rayAngle = i * angle / (numberOfRays - 1) - angle / 2;
Vector3 rayDirection = Quaternion.Euler(0, rayAngle, 0) * transform.forward;
Ray ray = new Ray(transform.position, rayDirection);
List<float> oneHot = new List<float>();
if(Physics.SphereCast(ray , sphereCastRadius , out RaycastHit hit , rayDistance))
{
foreach (string currentTag in detectableTags)
{
oneHot.Add(hit.collider.gameObject.CompareTag(currentTag) ? 1.0f : 0.0f);
if (hit.collider.gameObject.CompareTag("BodyPart") && currentTag == "BodyPart")
{
Transform hitParentTransform = hit.collider.transform.parent;
Transform ourParentTransform = transform.parent.transform.parent;
if (hitParentTransform.gameObject == ourParentTransform.gameObject)
{
oneHot[oneHot.Count - 1] = 0f;
}
}
}
oneHot.Add(0f); //No collision is false
oneHot.Add(hit.distance / rayDistance);
}
else
{
foreach (string tag in detectableTags)
{
oneHot.Add(0f);
}
oneHot.Add(1f); //No collision is true
oneHot.Add(0f);
}
finalOutput.AddRange(oneHot);
}
return finalOutput;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
for (int i = 0; i < numberOfRays; i++)
{
float rayAngle = i * angle / (numberOfRays - 1) - angle / 2;
Vector3 rayDirection = Quaternion.Euler(0, rayAngle, 0) * transform.forward;
Ray ray = new Ray(transform.position, rayDirection);
Gizmos.DrawRay(ray.origin, ray.direction * rayDistance);
Gizmos.DrawWireSphere(ray.origin + ray.direction * rayDistance, sphereCastRadius);
}
}
private void DebugName(List<float> list)
{
Debug.Log(list.Count);
List<string> stringList = list.ConvertAll(f => f.ToString());
string listString = string.Join(", ", stringList);
Debug.Log(listString);
}
}