using UnityEngine; namespace NanoBrain.Unity.Braitenberg { /// /// A light sensor /// [HelpURL("https://passer.life/documentation/nanobrain/Documentation/html/class_nano_brain_1_1_unity_1_1_braitenberg_1_1_light_sensor.html")] public class LightSensor : Sensor { /// /// If true, perform occlusion checks with raycasts /// [Tooltip("If true, perform occlusion checks with raycasts.")] public bool useOcclusion = true; /// /// Angle in degrees for directional sensitivity; >= 180: omnidirectional. /// [Tooltip("Angle (degrees) for directional sensitivity; >=180: omnidirectional.")] public float sensitivityAngle = 180f; protected override float SampleSensor() { float sum = 0f; // Get all active lights in scene (Point lights only) #if UNITY_6000_0_OR_NEWER Light[] lights = FindObjectsByType(); #else Light[] lights = FindObjectsByType(FindObjectsSortMode.None); #endif Vector3 pos = transform.position; Vector3 forward = transform.forward; float halfAngleCos = Mathf.Cos(sensitivityAngle * 0.5f * Mathf.Deg2Rad); int lightCount = 0; foreach (Light light in lights) { if (!light.enabled || light.type != LightType.Point) continue; // quick distance check float dist = Vector3.Distance(light.transform.position, pos); if (dist > this.sensorRange + light.range) continue; // outside both sensor and light ranges // angular sensitivity Vector3 toLight = (light.transform.position - pos).normalized; if (sensitivityAngle < 180f) { if (Vector3.Dot(forward, toLight) < halfAngleCos) continue; } // compute light falloff: Unity point lights use inverse squared (physically plausible) // you can approximate using L.intensity and L.range float attenuation = 0f; if (dist < light.range) { // inverse-square like falloff (avoid divide-by-zero) attenuation = light.intensity / Mathf.Max(0.01f, dist * dist); // optionally scale by (1 - dist/range) for a linear blend: attenuation *= 1f - dist / light.range; } else continue; // occlusion check if (useOcclusion) { Vector3 dir = (light.transform.position - pos).normalized; float rayDist = Mathf.Min(dist, light.range); if (Physics.Raycast(pos, dir, out RaycastHit hit, rayDist, senseLayer)) attenuation *= 0f; // completely blocked; or reduce by factor } //Debug.DrawRay(this.transform.position, toLight * sensorRange); // color consideration: convert light color to luminance if needed float luminance = RGBToLuminance(light.color) * attenuation; //Debug.Log(luminance); sum += luminance; lightCount++; } if (lightCount == 0) return 0; else return sum / lightCount; } static float RGBToLuminance(Color c) { // simple Rec.709 luminance return 0.2126f * c.r + 0.7152f * c.g + 0.0722f * c.b; } } }