Files
LowPolyBattleSim/Assets/Scripts/UnitSightSystem.cs
Caleb Sandford deQuincey ecdd3e2a9e intial commit
2025-06-27 23:27:49 +01:00

322 lines
10 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using RaycastPro.Detectors;
[RequireComponent(typeof(UnitController))]
[RequireComponent(typeof(SightDetector))]
public class UnitSightSystem : MonoBehaviour
{
[Header("References")]
public UnitController unitController;
public SightDetector sightDetector;
[Header("Detection Settings")]
public LayerMask enemyLayerMask = -1;
public LayerMask allyLayerMask = -1;
public LayerMask obstacleLayerMask = -1;
[Header("Debug")]
public bool showDebugInfo = false;
// Cached detected units
private List<UnitController> detectedEnemies = new List<UnitController>();
private List<UnitController> detectedAllies = new List<UnitController>();
// Events for unit detection
public System.Action<UnitController> OnEnemyDetected;
public System.Action<UnitController> OnEnemyLost;
public System.Action<UnitController> OnAllyDetected;
public System.Action<UnitController> OnAllyLost;
private void Awake()
{
InitializeComponents();
}
private void Start()
{
ConfigureSightDetector();
}
private void InitializeComponents()
{
if (unitController == null)
unitController = GetComponent<UnitController>();
if (sightDetector == null)
sightDetector = GetComponent<SightDetector>();
if (sightDetector == null)
{
Debug.LogError($"SightDetector component not found on {gameObject.name}. Please add a SightDetector component.");
return;
}
// Subscribe to SightDetector events
sightDetector.onNewCollider.AddListener(OnTargetFirstEnter);
sightDetector.onLostCollider.AddListener(OnTargetLastExit);
}
private void ConfigureSightDetector()
{
if (sightDetector == null || unitController == null || unitController.unitData == null)
return;
UnitData unitData = unitController.unitData;
if (!unitData.autoConfigureSightDetector)
return;
// Configure SightDetector based on UnitData
sightDetector.radius = unitData.detectionRange;
sightDetector.angleX = unitData.sightAngleX;
sightDetector.angleY = unitData.sightAngleY;
sightDetector.fullAwareness = unitData.fullAwarenessRadius;
sightDetector.minRadius = unitData.minDetectionRadius;
// Configure pulse settings
// Note: RaycastPro handles pulse timing internally
// The usePulseDetection flag is mainly for our reference
if (unitData.usePulseDetection)
{
// Pulse mode is controlled by RaycastPro's internal systems
// We can trigger manual pulses if needed via TriggerDetectionPulse()
}
// Configure target limits
sightDetector.Limited = unitData.limitDetectedTargets;
if (unitData.limitDetectedTargets)
{
sightDetector.LimitCount = unitData.maxDetectedTargets;
}
// Set up layer masks
sightDetector.detectLayer = enemyLayerMask | allyLayerMask;
sightDetector.blockLayer = obstacleLayerMask;
if (showDebugInfo)
{
Debug.Log($"Configured SightDetector for {gameObject.name}: " +
$"Range={sightDetector.radius}, AngleX={sightDetector.angleX}, AngleY={sightDetector.angleY}");
}
}
private void OnTargetFirstEnter(Collider target)
{
UnitController targetUnit = target.GetComponent<UnitController>();
if (targetUnit == null) return;
if (IsEnemy(targetUnit))
{
if (!detectedEnemies.Contains(targetUnit))
{
detectedEnemies.Add(targetUnit);
OnEnemyDetected?.Invoke(targetUnit);
if (showDebugInfo)
Debug.Log($"{gameObject.name} detected enemy: {targetUnit.gameObject.name}");
}
}
else if (IsAlly(targetUnit))
{
if (!detectedAllies.Contains(targetUnit))
{
detectedAllies.Add(targetUnit);
OnAllyDetected?.Invoke(targetUnit);
if (showDebugInfo)
Debug.Log($"{gameObject.name} detected ally: {targetUnit.gameObject.name}");
}
}
}
// Note: Continuous tracking is handled through Update method checking DetectedColliders
// since RaycastPro doesn't have OnStay events
private void Update()
{
// Continuously update unit controller with current targets in sight
if (sightDetector != null && unitController != null)
{
foreach (Collider detectedCollider in sightDetector.DetectedColliders)
{
UnitController targetUnit = detectedCollider.GetComponent<UnitController>();
if (targetUnit != null && IsEnemy(targetUnit))
{
unitController.OnEnemyInSight(targetUnit);
}
}
}
}
private void OnTargetLastExit(Collider target)
{
UnitController targetUnit = target.GetComponent<UnitController>();
if (targetUnit == null) return;
if (IsEnemy(targetUnit) && detectedEnemies.Contains(targetUnit))
{
detectedEnemies.Remove(targetUnit);
OnEnemyLost?.Invoke(targetUnit);
if (showDebugInfo)
Debug.Log($"{gameObject.name} lost sight of enemy: {targetUnit.gameObject.name}");
}
else if (IsAlly(targetUnit) && detectedAllies.Contains(targetUnit))
{
detectedAllies.Remove(targetUnit);
OnAllyLost?.Invoke(targetUnit);
if (showDebugInfo)
Debug.Log($"{gameObject.name} lost sight of ally: {targetUnit.gameObject.name}");
}
}
private bool IsEnemy(UnitController targetUnit)
{
return targetUnit.team != unitController.team;
}
private bool IsAlly(UnitController targetUnit)
{
return targetUnit.team == unitController.team && targetUnit != unitController;
}
// Public methods for UnitController to access
public List<UnitController> GetDetectedEnemies()
{
// Remove any null references
detectedEnemies.RemoveAll(enemy => enemy == null);
return new List<UnitController>(detectedEnemies);
}
public List<UnitController> GetDetectedAllies()
{
// Remove any null references
detectedAllies.RemoveAll(ally => ally == null);
return new List<UnitController>(detectedAllies);
}
public UnitController GetClosestEnemy()
{
UnitController closest = null;
float closestDistance = float.MaxValue;
foreach (UnitController enemy in detectedEnemies)
{
if (enemy == null || enemy.currentHealth <= 0) continue;
float distance = Vector3.Distance(transform.position, enemy.transform.position);
if (distance < closestDistance)
{
closest = enemy;
closestDistance = distance;
}
}
return closest;
}
public UnitController GetClosestAlly()
{
UnitController closest = null;
float closestDistance = float.MaxValue;
foreach (UnitController ally in detectedAllies)
{
if (ally == null || ally.currentHealth <= 0) continue;
float distance = Vector3.Distance(transform.position, ally.transform.position);
if (distance < closestDistance)
{
closest = ally;
closestDistance = distance;
}
}
return closest;
}
public bool CanSeeTarget(Transform target)
{
if (sightDetector == null || target == null) return false;
// Check if target is in the detected colliders list
foreach (Collider detectedCollider in sightDetector.DetectedColliders)
{
if (detectedCollider != null && detectedCollider.transform == target)
return true;
}
return false;
}
public bool IsTargetInAttackRange(Transform target)
{
if (target == null || unitController == null || unitController.unitData == null)
return false;
float distance = Vector3.Distance(transform.position, target.position);
return distance <= unitController.unitData.attackRange;
}
// Update sight detector configuration at runtime
public void UpdateSightConfiguration()
{
ConfigureSightDetector();
}
// Manual pulse trigger for performance optimization
public void TriggerDetectionPulse()
{
if (sightDetector != null)
{
// The OnPulse method is part of IPulse interface
// It's typically called automatically by RaycastPro, but can be triggered manually
sightDetector.OnPulse();
}
}
private void OnDestroy()
{
// Unsubscribe from events
if (sightDetector != null)
{
sightDetector.onNewCollider.RemoveListener(OnTargetFirstEnter);
sightDetector.onLostCollider.RemoveListener(OnTargetLastExit);
}
}
private void OnDrawGizmosSelected()
{
if (!showDebugInfo) return;
// Draw attack range
if (unitController != null && unitController.unitData != null)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, unitController.unitData.attackRange);
}
// Draw connections to detected enemies
Gizmos.color = Color.red;
foreach (UnitController enemy in detectedEnemies)
{
if (enemy != null)
{
Gizmos.DrawLine(transform.position, enemy.transform.position);
}
}
// Draw connections to detected allies
Gizmos.color = Color.green;
foreach (UnitController ally in detectedAllies)
{
if (ally != null)
{
Gizmos.DrawLine(transform.position, ally.transform.position);
}
}
}
}