269 lines
7.6 KiB
C#
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
|
|
}
|
|
} |