Files
beyond/Assets/AI/_Archer/SA_FleeFromPlayer.cs
2025-11-20 14:34:59 +01:00

269 lines
7.6 KiB
C#

using Invector.vCharacterController.AI.FSMBehaviour;
using UnityEngine;
namespace ArcherEnemy
{
/// <summary>
/// State action that makes archer flee/retreat from player
/// Moves archer away from player while trying to maintain shooting distance
/// </summary>
[CreateAssetMenu(menuName = "Invector/FSM/Actions/Archer/Flee From Player")]
public class SA_FleeFromPlayer : vStateAction
{
public override string categoryName => "Archer/Combat";
public override string defaultName => "Flee From Player";
[Header("Flee Configuration")]
[Tooltip("Desired safe distance from player")]
public float safeDistance = 12f;
[Tooltip("How far to look ahead when fleeing")]
public float fleeDistance = 5f;
[Tooltip("Check for obstacles when fleeing")]
public bool avoidObstacles = true;
[Tooltip("Layers considered as obstacles")]
public LayerMask obstacleMask = -1;
[Tooltip("Number of directions to try when finding flee path")]
public int directionSamples = 8;
[Header("Movement")]
[Tooltip("Movement speed multiplier (uses AI's speed)")]
[Range(0.5f, 2f)]
public float speedMultiplier = 1.2f;
[Tooltip("Make archer sprint while fleeing")]
public bool useSprint = true;
[Header("Rotation")]
[Tooltip("Keep facing player while backing away")]
public bool facePlayer = true;
[Tooltip("Rotation speed when facing player (degrees/sec)")]
public float turnSpeed = 180f;
[Header("Debug")]
[Tooltip("Enable debug logging")]
public bool enableDebug = false;
[Tooltip("Show flee direction gizmos")]
public bool showGizmos = true;
private Vector3 currentFleeDirection;
private Transform currentTarget;
public override void DoAction(vIFSMBehaviourController fsmBehaviour, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
{
if (executionType == vFSMComponentExecutionType.OnStateEnter)
{
OnEnter(fsmBehaviour);
}
else if (executionType == vFSMComponentExecutionType.OnStateUpdate)
{
OnUpdate(fsmBehaviour);
}
else if (executionType == vFSMComponentExecutionType.OnStateExit)
{
OnExit(fsmBehaviour);
}
}
private void OnEnter(vIFSMBehaviourController fsmBehaviour)
{
currentTarget = GetTarget(fsmBehaviour);
if (currentTarget == null)
{
if (enableDebug) Debug.LogWarning("[SA_FleeFromPlayer] No target found!");
return;
}
// Calculate initial flee direction
currentFleeDirection = CalculateFleeDirection(fsmBehaviour);
// Set AI to sprint if enabled
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController != null && useSprint)
{
}
if (enableDebug) Debug.Log("[SA_FleeFromPlayer] Started fleeing from player");
}
private void OnUpdate(vIFSMBehaviourController fsmBehaviour)
{
if (currentTarget == null)
{
currentTarget = GetTarget(fsmBehaviour);
if (currentTarget == null) return;
}
// Recalculate flee direction periodically
currentFleeDirection = CalculateFleeDirection(fsmBehaviour);
// Move in flee direction
MoveInDirection(fsmBehaviour, currentFleeDirection);
// Face player while fleeing if enabled
if (facePlayer)
{
RotateTowardsPlayer(fsmBehaviour);
}
}
private void OnExit(vIFSMBehaviourController fsmBehaviour)
{
// Reset AI speed
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController != null)
{
}
if (enableDebug) Debug.Log("[SA_FleeFromPlayer] Stopped fleeing");
}
/// <summary>
/// Calculates the best direction to flee
/// </summary>
private Vector3 CalculateFleeDirection(vIFSMBehaviourController fsmBehaviour)
{
Vector3 archerPos = fsmBehaviour.transform.position;
Vector3 playerPos = currentTarget.position;
// Basic flee direction: away from player
Vector3 awayFromPlayer = (archerPos - playerPos).normalized;
// If not avoiding obstacles, return simple direction
if (!avoidObstacles)
{
return awayFromPlayer;
}
// Try to find clear path
Vector3 bestDirection = awayFromPlayer;
float bestScore = EvaluateDirection(archerPos, awayFromPlayer, playerPos);
// Sample multiple directions around the flee vector
for (int i = 0; i < directionSamples; i++)
{
float angle = (360f / directionSamples) * i;
Vector3 testDirection = Quaternion.Euler(0f, angle, 0f) * awayFromPlayer;
float score = EvaluateDirection(archerPos, testDirection, playerPos);
if (score > bestScore)
{
bestScore = score;
bestDirection = testDirection;
}
}
return bestDirection;
}
/// <summary>
/// Evaluates how good a flee direction is (higher = better)
/// </summary>
private float EvaluateDirection(Vector3 from, Vector3 direction, Vector3 playerPos)
{
float score = 0f;
// Check if path is clear
Ray ray = new Ray(from + Vector3.up * 0.5f, direction);
bool isBlocked = Physics.Raycast(ray, fleeDistance, obstacleMask, QueryTriggerInteraction.Ignore);
if (!isBlocked)
{
score += 10f; // Big bonus for clear path
}
// Prefer directions that move away from player
Vector3 awayFromPlayer = (from - playerPos).normalized;
float alignment = Vector3.Dot(direction, awayFromPlayer);
score += alignment * 5f;
// Check destination height (avoid running off cliffs)
Vector3 destination = from + direction * fleeDistance;
if (Physics.Raycast(destination + Vector3.up * 2f, Vector3.down, 5f, obstacleMask))
{
score += 3f; // Bonus for having ground
}
return score;
}
/// <summary>
/// Moves archer in specified direction
/// </summary>
private void MoveInDirection(vIFSMBehaviourController fsmBehaviour, Vector3 direction)
{
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController == null) return;
// Calculate destination point
Vector3 destination = fsmBehaviour.transform.position + direction * fleeDistance;
// Use Invector's AI movement
aiController.MoveTo(destination);
// Apply speed multiplier
var motor = aiController as Invector.vCharacterController.AI.vSimpleMeleeAI_Controller;
if (motor != null)
{
// This would need to be adapted to your specific Invector version
// The goal is to make the archer move faster while fleeing
}
}
/// <summary>
/// Rotates archer to face player while backing away
/// </summary>
private void RotateTowardsPlayer(vIFSMBehaviourController fsmBehaviour)
{
if (currentTarget == null) return;
Vector3 directionToPlayer = (currentTarget.position - fsmBehaviour.transform.position);
directionToPlayer.y = 0f;
if (directionToPlayer.sqrMagnitude > 0.001f)
{
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);
fsmBehaviour.transform.rotation = Quaternion.RotateTowards(
fsmBehaviour.transform.rotation,
targetRotation,
turnSpeed * Time.deltaTime
);
}
}
private Transform GetTarget(vIFSMBehaviourController fsmBehaviour)
{
// Try through AI controller
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController != null && aiController.currentTarget != null)
return aiController.currentTarget.transform;
// Fallback - find player
GameObject player = GameObject.FindGameObjectWithTag("Player");
return player?.transform;
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (!showGizmos || !Application.isPlaying) return;
// Draw current flee direction
if (currentFleeDirection != Vector3.zero)
{
Gizmos.color = Color.red;
// This would need the archer's position to draw properly
}
}
#endif
}
}