Files cleanup, added summoner code
This commit is contained in:
327
Assets/AI/_Archer/ArcherProjectile.cs
Normal file
327
Assets/AI/_Archer/ArcherProjectile.cs
Normal file
@@ -0,0 +1,327 @@
|
||||
using Invector;
|
||||
using Invector.vCharacterController;
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// Arrow projectile shot by archer enemies
|
||||
/// Flies in straight line with gravity and deals damage on hit
|
||||
/// </summary>
|
||||
public class ArcherProjectile : MonoBehaviour
|
||||
{
|
||||
#region Configuration
|
||||
|
||||
[Header("Movement")]
|
||||
[Tooltip("Initial velocity of the arrow (m/s)")]
|
||||
public float initialSpeed = 30f;
|
||||
|
||||
[Tooltip("Gravity multiplier (higher = more arc)")]
|
||||
public float gravityMultiplier = 1f;
|
||||
|
||||
[Tooltip("Max lifetime before auto-despawn (seconds)")]
|
||||
public float maxLifetime = 10f;
|
||||
|
||||
[Header("Damage")]
|
||||
[Tooltip("Damage dealt on hit")]
|
||||
public int damage = 15;
|
||||
|
||||
[Tooltip("Knockback force")]
|
||||
public float knockbackForce = 5f;
|
||||
|
||||
[Tooltip("Layers that can be hit")]
|
||||
public LayerMask hitLayers = -1;
|
||||
|
||||
[Header("Effects")]
|
||||
[Tooltip("Impact VFX prefab")]
|
||||
public GameObject impactVFXPrefab;
|
||||
|
||||
[Tooltip("Trail renderer (optional)")]
|
||||
public TrailRenderer trail;
|
||||
|
||||
[Header("Audio")]
|
||||
[Tooltip("Impact sound")]
|
||||
public AudioClip impactSound;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
[Tooltip("Show trajectory gizmos")]
|
||||
public bool showGizmos = true;
|
||||
|
||||
#endregion Configuration
|
||||
|
||||
#region Runtime State
|
||||
|
||||
private Vector3 velocity;
|
||||
private float lifetime = 0f;
|
||||
private bool hasHit = false;
|
||||
private AudioSource audioSource;
|
||||
|
||||
#endregion Runtime State
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (impactSound != null)
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.spatialBlend = 1f; // 3D sound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ResetState();
|
||||
|
||||
// Set initial velocity in forward direction
|
||||
velocity = transform.forward * initialSpeed;
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[ArcherProjectile] Spawned at {transform.position}, velocity: {velocity}");
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (hasHit) return;
|
||||
|
||||
lifetime += Time.deltaTime;
|
||||
|
||||
// Check lifetime
|
||||
if (lifetime >= maxLifetime)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Lifetime expired");
|
||||
Despawn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply gravity
|
||||
velocity += Physics.gravity * gravityMultiplier * Time.deltaTime;
|
||||
|
||||
// Calculate movement step
|
||||
Vector3 moveStep = velocity * Time.deltaTime;
|
||||
Vector3 newPosition = transform.position + moveStep;
|
||||
|
||||
// Raycast for collision detection
|
||||
if (Physics.Raycast(transform.position, moveStep.normalized, out RaycastHit hit,
|
||||
moveStep.magnitude, hitLayers, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
OnHit(hit);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move arrow
|
||||
transform.position = newPosition;
|
||||
|
||||
// Rotate arrow to face movement direction
|
||||
if (velocity.sqrMagnitude > 0.001f)
|
||||
{
|
||||
transform.rotation = Quaternion.LookRotation(velocity.normalized);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Unity Lifecycle
|
||||
|
||||
#region Collision & Damage
|
||||
|
||||
private void OnHit(RaycastHit hit)
|
||||
{
|
||||
if (hasHit) return;
|
||||
hasHit = true;
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[ArcherProjectile] Hit: {hit.collider.name} at {hit.point}");
|
||||
|
||||
// Position arrow at impact point
|
||||
transform.position = hit.point;
|
||||
transform.rotation = Quaternion.LookRotation(hit.normal);
|
||||
|
||||
// Try to deal damage
|
||||
DealDamage(hit.collider, hit.point, hit.normal);
|
||||
|
||||
// Spawn impact VFX
|
||||
SpawnImpactVFX(hit.point, hit.normal);
|
||||
|
||||
// Play impact sound
|
||||
PlayImpactSound();
|
||||
|
||||
// Stick arrow to surface or despawn
|
||||
StickToSurface(hit);
|
||||
}
|
||||
|
||||
private void DealDamage(Collider targetCollider, Vector3 hitPoint, Vector3 hitNormal)
|
||||
{
|
||||
// Calculate hit direction (opposite of normal for knockback)
|
||||
Vector3 hitDirection = -hitNormal;
|
||||
if (velocity.sqrMagnitude > 0.001f)
|
||||
{
|
||||
hitDirection = velocity.normalized;
|
||||
}
|
||||
|
||||
// Create damage info
|
||||
vDamage damageInfo = new vDamage(damage)
|
||||
{
|
||||
sender = transform,
|
||||
hitPosition = hitPoint
|
||||
};
|
||||
|
||||
if (knockbackForce > 0f)
|
||||
{
|
||||
damageInfo.force = hitDirection * knockbackForce;
|
||||
}
|
||||
|
||||
bool damageDealt = false;
|
||||
|
||||
// Try vIDamageReceiver
|
||||
var damageReceiver = targetCollider.GetComponent<vIDamageReceiver>() ??
|
||||
targetCollider.GetComponentInParent<vIDamageReceiver>();
|
||||
|
||||
if (damageReceiver != null)
|
||||
{
|
||||
damageReceiver.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Damage dealt via vIDamageReceiver");
|
||||
}
|
||||
|
||||
// Fallback to vHealthController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var healthController = targetCollider.GetComponent<vHealthController>() ??
|
||||
targetCollider.GetComponentInParent<vHealthController>();
|
||||
|
||||
if (healthController != null)
|
||||
{
|
||||
healthController.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Damage dealt via vHealthController");
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to vThirdPersonController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var tpc = targetCollider.GetComponent<vThirdPersonController>() ??
|
||||
targetCollider.GetComponentInParent<vThirdPersonController>();
|
||||
|
||||
if (tpc != null)
|
||||
{
|
||||
// Handle Beyond variant
|
||||
if (tpc is Beyond.bThirdPersonController beyond)
|
||||
{
|
||||
if (!beyond.GodMode && !beyond.isImmortal)
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Damage dealt via bThirdPersonController");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Target is immortal - no damage");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Damage dealt via vThirdPersonController");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!damageDealt && enableDebug)
|
||||
{
|
||||
Debug.Log("[ArcherProjectile] No damage dealt - no valid receiver found");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Collision & Damage
|
||||
|
||||
#region Effects
|
||||
|
||||
private void SpawnImpactVFX(Vector3 position, Vector3 normal)
|
||||
{
|
||||
if (impactVFXPrefab == null) return;
|
||||
|
||||
Quaternion rotation = Quaternion.LookRotation(normal);
|
||||
GameObject vfx = LeanPool.Spawn(impactVFXPrefab, position, rotation);
|
||||
LeanPool.Despawn(vfx, 3f);
|
||||
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Impact VFX spawned");
|
||||
}
|
||||
|
||||
private void PlayImpactSound()
|
||||
{
|
||||
if (audioSource != null && impactSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(impactSound);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Effects
|
||||
|
||||
#region Arrow Sticking
|
||||
|
||||
private void StickToSurface(RaycastHit hit)
|
||||
{
|
||||
// Option 1: Parent arrow to hit object (if it has rigidbody, it will move with it)
|
||||
// Option 2: Just despawn after short delay
|
||||
|
||||
// For now, despawn after brief delay to show impact
|
||||
LeanPool.Despawn(gameObject, 0.1f);
|
||||
}
|
||||
|
||||
#endregion Arrow Sticking
|
||||
|
||||
#region Pooling
|
||||
|
||||
private void ResetState()
|
||||
{
|
||||
hasHit = false;
|
||||
lifetime = 0f;
|
||||
velocity = Vector3.zero;
|
||||
|
||||
// Reset trail if present
|
||||
if (trail != null)
|
||||
{
|
||||
trail.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void Despawn()
|
||||
{
|
||||
if (enableDebug) Debug.Log("[ArcherProjectile] Despawning");
|
||||
LeanPool.Despawn(gameObject);
|
||||
}
|
||||
|
||||
#endregion Pooling
|
||||
|
||||
#region Gizmos
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!showGizmos || !Application.isPlaying) return;
|
||||
|
||||
// Draw velocity vector
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawRay(transform.position, velocity.normalized * 2f);
|
||||
|
||||
// Draw forward direction
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawRay(transform.position, transform.forward * 1f);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion Gizmos
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/ArcherProjectile.cs.meta
Normal file
2
Assets/AI/_Archer/ArcherProjectile.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c5e129708014c64981ec3d5665de1b4
|
||||
430
Assets/AI/_Archer/ArcherShootingAI.cs
Normal file
430
Assets/AI/_Archer/ArcherShootingAI.cs
Normal file
@@ -0,0 +1,430 @@
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// AI component for archer enemy that shoots arrows at target
|
||||
/// Should be attached to archer enemy prefab
|
||||
/// </summary>
|
||||
public class ArcherShootingAI : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[Tooltip("Transform point from which arrows are shot (usually hand bone or weapon tip)")]
|
||||
public Transform shootPoint;
|
||||
|
||||
[Tooltip("Arrow prefab with ArcherProjectile component")]
|
||||
public GameObject arrowPrefab;
|
||||
|
||||
[Tooltip("Animator for triggering shoot animation")]
|
||||
public Animator animator;
|
||||
|
||||
[Header("Targeting")]
|
||||
[Tooltip("Auto-find player on start")]
|
||||
public bool autoFindPlayer = true;
|
||||
|
||||
[Tooltip("Player tag to search for")]
|
||||
public string playerTag = "Player";
|
||||
|
||||
[Tooltip("Height offset for aiming (aim at chest/head)")]
|
||||
public float targetHeightOffset = 1.2f;
|
||||
|
||||
[Header("Shooting Parameters")]
|
||||
[Tooltip("Minimum distance to shoot from")]
|
||||
public float minShootDistance = 8f;
|
||||
|
||||
[Tooltip("Maximum distance to shoot from")]
|
||||
public float maxShootDistance = 25f;
|
||||
|
||||
[Tooltip("Time between shots (seconds)")]
|
||||
public float shootCooldown = 2f;
|
||||
|
||||
[Tooltip("Arrow launch speed (m/s)")]
|
||||
public float arrowSpeed = 30f;
|
||||
|
||||
[Tooltip("How much to lead the target (predict movement)")]
|
||||
[Range(0f, 1f)]
|
||||
public float leadTargetAmount = 0.5f;
|
||||
|
||||
[Header("Animation")]
|
||||
[Tooltip("Animator trigger parameter for shooting")]
|
||||
public string shootTriggerName = "Shoot";
|
||||
|
||||
[Tooltip("Delay after animation starts before spawning arrow")]
|
||||
public float shootAnimationDelay = 0.3f;
|
||||
|
||||
[Header("Aiming")]
|
||||
[Tooltip("How quickly archer rotates to face target (degrees/sec)")]
|
||||
public float turnSpeed = 180f;
|
||||
|
||||
[Tooltip("Angle tolerance for shooting (degrees)")]
|
||||
public float aimTolerance = 15f;
|
||||
|
||||
[Header("Effects")]
|
||||
[Tooltip("Muzzle flash effect at shoot point")]
|
||||
public GameObject muzzleFlashPrefab;
|
||||
|
||||
[Tooltip("Shoot sound")]
|
||||
public AudioClip shootSound;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
[Tooltip("Show aiming gizmos")]
|
||||
public bool showGizmos = true;
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private Transform target;
|
||||
private float lastShootTime = -999f;
|
||||
private bool isAiming = false;
|
||||
private AudioSource audioSource;
|
||||
private Vector3 lastKnownTargetVelocity;
|
||||
private Vector3 lastTargetPosition;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Setup audio source
|
||||
if (shootSound != null)
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.spatialBlend = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
// Find animator if not assigned
|
||||
if (animator == null)
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
}
|
||||
|
||||
// Find shoot point if not assigned
|
||||
if (shootPoint == null)
|
||||
{
|
||||
// Try to find "ShootPoint" child transform
|
||||
Transform shootPointChild = transform.Find("ShootPoint");
|
||||
shootPoint = shootPointChild != null ? shootPointChild : transform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (autoFindPlayer)
|
||||
{
|
||||
FindPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (target != null && isAiming)
|
||||
{
|
||||
RotateTowardsTarget();
|
||||
UpdateTargetVelocity();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Unity Lifecycle
|
||||
|
||||
#region Target Management
|
||||
|
||||
/// <summary>
|
||||
/// Finds player by tag
|
||||
/// </summary>
|
||||
private void FindPlayer()
|
||||
{
|
||||
GameObject player = GameObject.FindGameObjectWithTag(playerTag);
|
||||
if (player != null)
|
||||
{
|
||||
SetTarget(player.transform);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target to shoot at
|
||||
/// </summary>
|
||||
public void SetTarget(Transform newTarget)
|
||||
{
|
||||
target = newTarget;
|
||||
if (target != null)
|
||||
{
|
||||
lastTargetPosition = target.position;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates target velocity for prediction
|
||||
/// </summary>
|
||||
private void UpdateTargetVelocity()
|
||||
{
|
||||
if (target == null) return;
|
||||
|
||||
Vector3 currentPosition = target.position;
|
||||
lastKnownTargetVelocity = (currentPosition - lastTargetPosition) / Time.deltaTime;
|
||||
lastTargetPosition = currentPosition;
|
||||
}
|
||||
|
||||
#endregion Target Management
|
||||
|
||||
#region Shooting Logic
|
||||
|
||||
/// <summary>
|
||||
/// Checks if archer can shoot at target
|
||||
/// </summary>
|
||||
public bool CanShoot()
|
||||
{
|
||||
if (target == null) return false;
|
||||
|
||||
float distance = Vector3.Distance(transform.position, target.position);
|
||||
|
||||
// Check distance range
|
||||
if (distance < minShootDistance || distance > maxShootDistance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
if (Time.time < lastShootTime + shootCooldown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if facing target (within tolerance)
|
||||
Vector3 directionToTarget = (target.position - transform.position).normalized;
|
||||
float angleToTarget = Vector3.Angle(transform.forward, directionToTarget);
|
||||
|
||||
if (angleToTarget > aimTolerance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates shooting sequence
|
||||
/// </summary>
|
||||
public void StartShooting()
|
||||
{
|
||||
if (!CanShoot())
|
||||
{
|
||||
if (enableDebug) Debug.Log("[ArcherShootingAI] Cannot shoot - conditions not met");
|
||||
return;
|
||||
}
|
||||
|
||||
isAiming = true;
|
||||
|
||||
// Trigger animation
|
||||
if (animator != null && !string.IsNullOrEmpty(shootTriggerName))
|
||||
{
|
||||
animator.SetTrigger(shootTriggerName);
|
||||
}
|
||||
|
||||
// Schedule arrow spawn after animation delay
|
||||
Invoke(nameof(SpawnArrow), shootAnimationDelay);
|
||||
|
||||
lastShootTime = Time.time;
|
||||
|
||||
if (enableDebug) Debug.Log($"[ArcherShootingAI] Started shooting at {target.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns arrow projectile
|
||||
/// </summary>
|
||||
private void SpawnArrow()
|
||||
{
|
||||
if (arrowPrefab == null)
|
||||
{
|
||||
Debug.LogError("[ArcherShootingAI] Arrow prefab not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shootPoint == null)
|
||||
{
|
||||
Debug.LogError("[ArcherShootingAI] Shoot point not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate aim point with prediction
|
||||
Vector3 aimPoint = CalculateAimPoint();
|
||||
|
||||
// Calculate shoot direction
|
||||
Vector3 shootDirection = (aimPoint - shootPoint.position).normalized;
|
||||
|
||||
// Calculate rotation for arrow
|
||||
Quaternion arrowRotation = Quaternion.LookRotation(shootDirection);
|
||||
|
||||
// Spawn arrow
|
||||
GameObject arrow = LeanPool.Spawn(arrowPrefab, shootPoint.position, arrowRotation);
|
||||
|
||||
// Configure arrow projectile
|
||||
var projectile = arrow.GetComponent<ArcherProjectile>();
|
||||
if (projectile != null)
|
||||
{
|
||||
projectile.initialSpeed = arrowSpeed;
|
||||
}
|
||||
|
||||
// Play effects
|
||||
PlayShootEffects();
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[ArcherShootingAI] Arrow spawned, direction: {shootDirection}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates aim point with target prediction
|
||||
/// </summary>
|
||||
private Vector3 CalculateAimPoint()
|
||||
{
|
||||
if (target == null) return shootPoint.position + transform.forward * 10f;
|
||||
|
||||
// Base aim point
|
||||
Vector3 targetPosition = target.position + Vector3.up * targetHeightOffset;
|
||||
|
||||
// Add prediction based on target velocity
|
||||
if (leadTargetAmount > 0f && lastKnownTargetVelocity.sqrMagnitude > 0.1f)
|
||||
{
|
||||
// Estimate time to target
|
||||
float distance = Vector3.Distance(shootPoint.position, targetPosition);
|
||||
float timeToTarget = distance / arrowSpeed;
|
||||
|
||||
// Predict future position
|
||||
Vector3 predictedOffset = lastKnownTargetVelocity * timeToTarget * leadTargetAmount;
|
||||
targetPosition += predictedOffset;
|
||||
}
|
||||
|
||||
return targetPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops shooting sequence
|
||||
/// </summary>
|
||||
public void StopShooting()
|
||||
{
|
||||
isAiming = false;
|
||||
CancelInvoke(nameof(SpawnArrow));
|
||||
}
|
||||
|
||||
#endregion Shooting Logic
|
||||
|
||||
#region Effects
|
||||
|
||||
private void PlayShootEffects()
|
||||
{
|
||||
// Spawn muzzle flash
|
||||
if (muzzleFlashPrefab != null && shootPoint != null)
|
||||
{
|
||||
GameObject flash = LeanPool.Spawn(muzzleFlashPrefab, shootPoint.position, shootPoint.rotation);
|
||||
LeanPool.Despawn(flash, 2f);
|
||||
}
|
||||
|
||||
// Play shoot sound
|
||||
if (audioSource != null && shootSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(shootSound);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Effects
|
||||
|
||||
#region Rotation
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly rotates archer to face target
|
||||
/// </summary>
|
||||
private void RotateTowardsTarget()
|
||||
{
|
||||
if (target == null) return;
|
||||
|
||||
Vector3 directionToTarget = (target.position - transform.position);
|
||||
directionToTarget.y = 0f; // Keep rotation on Y axis only
|
||||
|
||||
if (directionToTarget.sqrMagnitude > 0.001f)
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget);
|
||||
transform.rotation = Quaternion.RotateTowards(
|
||||
transform.rotation,
|
||||
targetRotation,
|
||||
turnSpeed * Time.deltaTime
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if archer is facing target within tolerance
|
||||
/// </summary>
|
||||
public bool IsFacingTarget()
|
||||
{
|
||||
if (target == null) return false;
|
||||
|
||||
Vector3 directionToTarget = (target.position - transform.position).normalized;
|
||||
float angle = Vector3.Angle(transform.forward, directionToTarget);
|
||||
|
||||
return angle <= aimTolerance;
|
||||
}
|
||||
|
||||
#endregion Rotation
|
||||
|
||||
#region Public Query Methods
|
||||
|
||||
public Transform GetTarget() => target;
|
||||
|
||||
public bool IsAiming() => isAiming;
|
||||
|
||||
public float GetTimeSinceLastShot() => Time.time - lastShootTime;
|
||||
|
||||
public float GetDistanceToTarget() => target != null ? Vector3.Distance(transform.position, target.position) : float.MaxValue;
|
||||
|
||||
public bool IsInShootingRange() => GetDistanceToTarget() >= minShootDistance && GetDistanceToTarget() <= maxShootDistance;
|
||||
|
||||
#endregion Public Query Methods
|
||||
|
||||
#region Gizmos
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!showGizmos) return;
|
||||
|
||||
// Draw shooting range
|
||||
Gizmos.color = Color.yellow;
|
||||
UnityEditor.Handles.color = new Color(1f, 1f, 0f, 0.1f);
|
||||
UnityEditor.Handles.DrawSolidDisc(transform.position, Vector3.up, minShootDistance);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
UnityEditor.Handles.color = new Color(0f, 1f, 0f, 0.1f);
|
||||
UnityEditor.Handles.DrawSolidDisc(transform.position, Vector3.up, maxShootDistance);
|
||||
|
||||
// Draw shoot point
|
||||
if (shootPoint != null)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(shootPoint.position, 0.1f);
|
||||
Gizmos.DrawLine(shootPoint.position, shootPoint.position + shootPoint.forward * 2f);
|
||||
}
|
||||
|
||||
// Draw aim line to target
|
||||
if (target != null && Application.isPlaying)
|
||||
{
|
||||
Vector3 aimPoint = CalculateAimPoint();
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawLine(shootPoint != null ? shootPoint.position : transform.position, aimPoint);
|
||||
Gizmos.DrawWireSphere(aimPoint, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion Gizmos
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/ArcherShootingAI.cs.meta
Normal file
2
Assets/AI/_Archer/ArcherShootingAI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b6fc5c257dfe0a42856b4f8169fb925
|
||||
3149
Assets/AI/_Archer/Archer_BaseModel.prefab
Normal file
3149
Assets/AI/_Archer/Archer_BaseModel.prefab
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/AI/_Archer/Archer_BaseModel.prefab.meta
Normal file
7
Assets/AI/_Archer/Archer_BaseModel.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8faaed5f0f8beac4193201e791fa0406
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
864
Assets/AI/_Archer/ArrowProjectile_AI.prefab
Normal file
864
Assets/AI/_Archer/ArrowProjectile_AI.prefab
Normal file
@@ -0,0 +1,864 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1344820394587964
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4219749601733482}
|
||||
m_Layer: 0
|
||||
m_Name: Bone
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4219749601733482
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1344820394587964}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.0018099877, y: -3.6349493e-26, z: -1.4291102e-16, w: 0.9999984}
|
||||
m_LocalPosition: {x: -0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4010765377112918}
|
||||
m_Father: {fileID: 4900627540279602}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1347971728965404
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4010765377112918}
|
||||
m_Layer: 0
|
||||
m_Name: Bone.007
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4010765377112918
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1347971728965404}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: 1.2924697e-26, z: 5.293986e-23, w: 1}
|
||||
m_LocalPosition: {x: 1.4351664e-25, y: 0.006778502, z: 2.3283064e-12}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4730953940066258}
|
||||
m_Father: {fileID: 4219749601733482}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1422405211252710
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4730953940066258}
|
||||
m_Layer: 0
|
||||
m_Name: Bone.007_end
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4730953940066258
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1422405211252710}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: -0, y: 0.00096835766, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4010765377112918}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1498346897932278
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4792533254389488}
|
||||
- component: {fileID: 95282376641117848}
|
||||
m_Layer: 0
|
||||
m_Name: AnimatedArrow (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4792533254389488
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1498346897932278}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0.27999878, y: 0, z: -2.2799993}
|
||||
m_LocalScale: {x: 10, y: 10, z: 10}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4900627540279602}
|
||||
- {fileID: 4892945928622000}
|
||||
- {fileID: 536000009008614867}
|
||||
m_Father: {fileID: 4863395260852438}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!95 &95282376641117848
|
||||
Animator:
|
||||
serializedVersion: 7
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1498346897932278}
|
||||
m_Enabled: 1
|
||||
m_Avatar: {fileID: 9000000, guid: fa7d4475dc3142843b220a2f24e6b960, type: 3}
|
||||
m_Controller: {fileID: 9100000, guid: 53cbc0e0fd100404897cfdc54c23dd80, type: 2}
|
||||
m_CullingMode: 1
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_AnimatePhysics: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
--- !u!1 &1588400079109534
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4997315693649486}
|
||||
- component: {fileID: 33548075977177624}
|
||||
- component: {fileID: 23410847150294290}
|
||||
m_Layer: 0
|
||||
m_Name: vArrow (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!4 &4997315693649486
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1588400079109534}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0.05}
|
||||
m_LocalScale: {x: 1000.00116, y: 1000.00085, z: 1000.00024}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4863395260852438}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!33 &33548075977177624
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1588400079109534}
|
||||
m_Mesh: {fileID: 4300000, guid: d4eaee46c14533841a9e60c522f4d41b, type: 3}
|
||||
--- !u!23 &23410847150294290
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1588400079109534}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 850ca38c0bc85e04f8a7bd491c3724fc, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &1663245175560644
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4892945928622000}
|
||||
- component: {fileID: 137020979278887274}
|
||||
m_Layer: 0
|
||||
m_Name: vArrow
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!4 &4892945928622000
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1663245175560644}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.000000057601152, y: -0.00000005760116, z: -0.7071068, w: 0.7071067}
|
||||
m_LocalPosition: {x: -0.02748537, y: 0.000000002066372, z: 0.22880898}
|
||||
m_LocalScale: {x: 100.000015, y: 100.000015, z: 100.000015}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4792533254389488}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!137 &137020979278887274
|
||||
SkinnedMeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1663245175560644}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 3
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 850ca38c0bc85e04f8a7bd491c3724fc, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
serializedVersion: 2
|
||||
m_Quality: 0
|
||||
m_UpdateWhenOffscreen: 0
|
||||
m_SkinnedMotionVectors: 1
|
||||
m_Mesh: {fileID: 4300000, guid: fa7d4475dc3142843b220a2f24e6b960, type: 3}
|
||||
m_Bones:
|
||||
- {fileID: 4219749601733482}
|
||||
- {fileID: 4010765377112918}
|
||||
m_BlendShapeWeights: []
|
||||
m_RootBone: {fileID: 4219749601733482}
|
||||
m_AABB:
|
||||
m_Center: {x: 0.00000047206413, y: 0.0037558018, z: -0.000028062193}
|
||||
m_Extent: {x: 0.00010841609, y: 0.0043096873, z: 0.00010976363}
|
||||
m_DirtyAABB: 0
|
||||
--- !u!1 &1726370018232204
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4921100307380848}
|
||||
- component: {fileID: 54561025541887046}
|
||||
- component: {fileID: 136645779043982694}
|
||||
- component: {fileID: 114939945238106750}
|
||||
- component: {fileID: 96572304949075240}
|
||||
- component: {fileID: 114630740814216480}
|
||||
- component: {fileID: 114305072012467408}
|
||||
m_Layer: 11
|
||||
m_Name: ArrowProjectile_AI
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 571167235653111936, guid: 0000000000000000d000000000000000, type: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4921100307380848
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 120.09543, y: 6.164, z: 35.62166}
|
||||
m_LocalScale: {x: 0.1, y: 0.1, z: 0.1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4863395260852438}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!54 &54561025541887046
|
||||
Rigidbody:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
serializedVersion: 4
|
||||
m_Mass: 0.1
|
||||
m_Drag: 0
|
||||
m_AngularDrag: 0.005
|
||||
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ImplicitCom: 1
|
||||
m_ImplicitTensor: 1
|
||||
m_UseGravity: 1
|
||||
m_IsKinematic: 0
|
||||
m_Interpolate: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 2
|
||||
--- !u!136 &136645779043982694
|
||||
CapsuleCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 0
|
||||
serializedVersion: 2
|
||||
m_Radius: 0.1
|
||||
m_Height: 0.1
|
||||
m_Direction: 2
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &114939945238106750
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4586a0f34d379cb49ae69bc6c996c2c0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
openCloseEvents: 1
|
||||
openCloseWindow: 0
|
||||
selectedToolbar: 0
|
||||
bulletLifeSettings: {fileID: 11400000, guid: 9cf8fdb35fce3c748ba3e6d6502cf435, type: 2}
|
||||
bulletLife: 50
|
||||
debugTrajetory: 0
|
||||
debugHittedObject: 0
|
||||
damage:
|
||||
damageValue: 100
|
||||
staminaBlockCost: 0
|
||||
staminaRecoveryDelay: 0
|
||||
ignoreDefense: 1
|
||||
activeRagdoll: 0
|
||||
senselessTime: 0
|
||||
sender: {fileID: 0}
|
||||
receiver: {fileID: 0}
|
||||
hitPosition: {x: 0, y: 0, z: 0}
|
||||
hitReaction: 1
|
||||
recoil_id: 0
|
||||
reaction_id: -1
|
||||
damageType:
|
||||
force: {x: 0, y: 0, z: 0}
|
||||
ignoreAllHitEffects: 0
|
||||
forceMultiplier: 0.02
|
||||
destroyOnCast: 1
|
||||
trail: {fileID: 0}
|
||||
onPassDamage:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
onCastCollider:
|
||||
m_PersistentCalls:
|
||||
m_Calls:
|
||||
- m_Target: {fileID: 114630740814216480}
|
||||
m_TargetAssemblyTypeName:
|
||||
m_MethodName: CreateDecal
|
||||
m_Mode: 0
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 0
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument:
|
||||
m_BoolArgument: 0
|
||||
m_CallState: 2
|
||||
onDestroyProjectile:
|
||||
m_PersistentCalls:
|
||||
m_Calls:
|
||||
- m_Target: {fileID: 114305072012467408}
|
||||
m_TargetAssemblyTypeName:
|
||||
m_MethodName: OnDestroyProjectile
|
||||
m_Mode: 0
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 0
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument:
|
||||
m_BoolArgument: 0
|
||||
m_CallState: 2
|
||||
- m_Target: {fileID: 135936980546969828}
|
||||
m_TargetAssemblyTypeName:
|
||||
m_MethodName: set_enabled
|
||||
m_Mode: 6
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 0
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument:
|
||||
m_BoolArgument: 1
|
||||
m_CallState: 2
|
||||
- m_Target: {fileID: 95282376641117848}
|
||||
m_TargetAssemblyTypeName:
|
||||
m_MethodName: SetTrigger
|
||||
m_Mode: 5
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 0
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument: Hit
|
||||
m_BoolArgument: 1
|
||||
m_CallState: 2
|
||||
instantiateData:
|
||||
aimPos: {x: 0, y: 0, z: 0}
|
||||
dir: {x: 0, y: 0, z: 0}
|
||||
vel: 0
|
||||
--- !u!96 &96572304949075240
|
||||
TrailRenderer:
|
||||
serializedVersion: 3
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 7a9c81640763dc54b9638d56c3c7f72a, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Time: 1
|
||||
m_PreviewTimeScale: 1
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
widthCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0.02
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 0.15
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
colorGradient:
|
||||
serializedVersion: 2
|
||||
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||
key2: {r: 1, g: 1, b: 1, a: 1}
|
||||
key3: {r: 1, g: 1, b: 1, a: 1}
|
||||
key4: {r: 1, g: 1, b: 1, a: 0}
|
||||
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||
ctime0: 0
|
||||
ctime1: 16384
|
||||
ctime2: 32768
|
||||
ctime3: 49151
|
||||
ctime4: 65535
|
||||
ctime5: 0
|
||||
ctime6: 0
|
||||
ctime7: 0
|
||||
atime0: 0
|
||||
atime1: 16384
|
||||
atime2: 32768
|
||||
atime3: 49151
|
||||
atime4: 65535
|
||||
atime5: 0
|
||||
atime6: 0
|
||||
atime7: 0
|
||||
m_Mode: 0
|
||||
m_ColorSpace: -1
|
||||
m_NumColorKeys: 5
|
||||
m_NumAlphaKeys: 5
|
||||
numCornerVertices: 0
|
||||
numCapVertices: 0
|
||||
alignment: 0
|
||||
textureMode: 0
|
||||
textureScale: {x: 1, y: 1}
|
||||
shadowBias: 0
|
||||
generateLightingData: 0
|
||||
m_MinVertexDistance: 0.1
|
||||
m_MaskInteraction: 0
|
||||
m_Autodestruct: 0
|
||||
m_Emitting: 1
|
||||
m_ApplyActiveColorSpace: 0
|
||||
--- !u!114 &114630740814216480
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 192cf5e148161f24ca871f4b201bccd9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
openCloseEvents: 0
|
||||
openCloseWindow: 0
|
||||
selectedToolbar: 0
|
||||
layermask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 32769
|
||||
decalObjects:
|
||||
- tag: Metal
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
- tag: Wood
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
- tag: Glass
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
- tag: Concrete
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
- tag: Dirt
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
- tag: Barrel
|
||||
impactEffect: {fileID: 0}
|
||||
additionalEffects: []
|
||||
--- !u!114 &114305072012467408
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726370018232204}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f10e58d3d067b0742ab5b0eda1436b9e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
projectileControl: {fileID: 114939945238106750}
|
||||
detachObject: {fileID: 4863395260852438}
|
||||
raycastBackSide: 1
|
||||
alignToNormal: 0
|
||||
penetration: 0
|
||||
debugPenetration: 0
|
||||
--- !u!1 &1781869610572578
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4863395260852438}
|
||||
- component: {fileID: 135936980546969828}
|
||||
m_Layer: 11
|
||||
m_Name: ArrowRoot
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4863395260852438
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1781869610572578}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4792533254389488}
|
||||
- {fileID: 4997315693649486}
|
||||
m_Father: {fileID: 4921100307380848}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!135 &135936980546969828
|
||||
SphereCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1781869610572578}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 1
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 0
|
||||
serializedVersion: 3
|
||||
m_Radius: 4.87
|
||||
m_Center: {x: 0, y: 0, z: -3.57}
|
||||
--- !u!1 &1993384683104020
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4900627540279602}
|
||||
m_Layer: 0
|
||||
m_Name: Armature
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4900627540279602
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1993384683104020}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067}
|
||||
m_LocalPosition: {x: -0.02748537, y: 0.000000002066372, z: 0.22880898}
|
||||
m_LocalScale: {x: 100, y: 100, z: 100}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4219749601733482}
|
||||
m_Father: {fileID: 4792533254389488}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &8661725544986759563
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 4792533254389488}
|
||||
m_Modifications:
|
||||
- target: {fileID: 8362956884574734050, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_Name
|
||||
value: vArrow
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.z
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -0.03
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0.00007915497
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: -0.303
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 0.7071068
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0.7071068
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 90
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: a3abb2598d285ed47a69dd12cccc0833, type: 3}
|
||||
--- !u!4 &536000009008614867 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 9170701404733201496, guid: a3abb2598d285ed47a69dd12cccc0833,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 8661725544986759563}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
8
Assets/AI/_Archer/ArrowProjectile_AI.prefab.meta
Normal file
8
Assets/AI/_Archer/ArrowProjectile_AI.prefab.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49dfe01444f3aeb409a3e3ecc971322c
|
||||
timeCreated: 1511197476
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/AI/_Archer/DEC_CanShoot.cs
Normal file
66
Assets/AI/_Archer/DEC_CanShoot.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Invector.vCharacterController.AI.FSMBehaviour;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// Decision checking if archer can shoot
|
||||
/// Checks cooldown, aiming, and射ing AI component readiness
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Decisions/Archer/Can Shoot")]
|
||||
public class DEC_CanShoot : vStateDecision
|
||||
{
|
||||
public override string categoryName => "Archer/Combat";
|
||||
public override string defaultName => "Can Shoot";
|
||||
|
||||
[Header("Configuration")]
|
||||
[Tooltip("Check if archer is facing target within tolerance")]
|
||||
public bool checkFacingTarget = true;
|
||||
|
||||
[Tooltip("Angle tolerance for shooting (degrees)")]
|
||||
public float aimTolerance = 20f;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
public override bool Decide(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
// Get ArcherShootingAI component
|
||||
var shootingAI = fsmBehaviour.gameObject.GetComponent<ArcherShootingAI>();
|
||||
|
||||
if (shootingAI == null)
|
||||
{
|
||||
if (enableDebug) Debug.LogWarning("[DEC_CanShoot] No ArcherShootingAI component found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the shooting AI's CanShoot method
|
||||
bool canShoot = shootingAI.CanShoot();
|
||||
|
||||
// Optional: additional facing check
|
||||
if (canShoot && checkFacingTarget)
|
||||
{
|
||||
Transform target = shootingAI.GetTarget();
|
||||
if (target != null)
|
||||
{
|
||||
Vector3 directionToTarget = (target.position - fsmBehaviour.transform.position).normalized;
|
||||
float angle = Vector3.Angle(fsmBehaviour.transform.forward, directionToTarget);
|
||||
|
||||
if (angle > aimTolerance)
|
||||
{
|
||||
canShoot = false;
|
||||
if (enableDebug) Debug.Log($"[DEC_CanShoot] Not facing target: {angle:F1}° (tolerance: {aimTolerance}°)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
Debug.Log($"[DEC_CanShoot] {(canShoot ? "CAN SHOOT" : "CANNOT SHOOT")}");
|
||||
}
|
||||
|
||||
return canShoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/DEC_CanShoot.cs.meta
Normal file
2
Assets/AI/_Archer/DEC_CanShoot.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd96521d511252744900d3adb9ef1b2e
|
||||
112
Assets/AI/_Archer/DEC_PlayerInShootRange.cs
Normal file
112
Assets/AI/_Archer/DEC_PlayerInShootRange.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using Invector.vCharacterController.AI.FSMBehaviour;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// Decision checking if player is in optimal shooting range
|
||||
/// Returns true when player is far enough but not too far
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Decisions/Archer/Player In Shoot Range")]
|
||||
public class DEC_PlayerInShootRange : vStateDecision
|
||||
{
|
||||
public override string categoryName => "Archer/Combat";
|
||||
public override string defaultName => "Player In Shoot Range";
|
||||
|
||||
[Header("Range Configuration")]
|
||||
[Tooltip("Minimum safe distance to start shooting")]
|
||||
public float minShootDistance = 8f;
|
||||
|
||||
[Tooltip("Maximum effective shooting distance")]
|
||||
public float maxShootDistance = 25f;
|
||||
|
||||
[Tooltip("Also check if we have clear line of sight")]
|
||||
public bool checkLineOfSight = true;
|
||||
|
||||
[Tooltip("Layers that block line of sight")]
|
||||
public LayerMask obstacleMask = -1;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
[Tooltip("Show range gizmos")]
|
||||
public bool showGizmos = true;
|
||||
|
||||
public override bool Decide(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
Transform target = GetTarget(fsmBehaviour);
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[DEC_PlayerInShootRange] No target found");
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 archerPos = fsmBehaviour.transform.position;
|
||||
Vector3 targetPos = target.position;
|
||||
|
||||
float distance = Vector3.Distance(archerPos, targetPos);
|
||||
|
||||
// Check distance range
|
||||
bool inRange = distance >= minShootDistance && distance <= maxShootDistance;
|
||||
|
||||
if (!inRange)
|
||||
{
|
||||
if (enableDebug)
|
||||
{
|
||||
if (distance < minShootDistance)
|
||||
Debug.Log($"[DEC_PlayerInShootRange] Player too close: {distance:F1}m (min: {minShootDistance})");
|
||||
else
|
||||
Debug.Log($"[DEC_PlayerInShootRange] Player too far: {distance:F1}m (max: {maxShootDistance})");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check line of sight if enabled
|
||||
if (checkLineOfSight)
|
||||
{
|
||||
Vector3 shootPoint = archerPos + Vector3.up * 1.5f; // Approximate chest height
|
||||
Vector3 targetPoint = targetPos + Vector3.up * 1f;
|
||||
Vector3 direction = targetPoint - shootPoint;
|
||||
|
||||
if (Physics.Raycast(shootPoint, direction.normalized, distance, obstacleMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
if (enableDebug) Debug.Log("[DEC_PlayerInShootRange] Line of sight blocked");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
Debug.Log($"[DEC_PlayerInShootRange] IN RANGE: {distance:F1}m");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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) return;
|
||||
|
||||
// This would need to be drawn from the archer's position in-game
|
||||
// For now, just a visual reference
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/DEC_PlayerInShootRange.cs.meta
Normal file
2
Assets/AI/_Archer/DEC_PlayerInShootRange.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6577855f02c3b2649b6a787e88baea8b
|
||||
80
Assets/AI/_Archer/DEC_PlayerTooClose.cs
Normal file
80
Assets/AI/_Archer/DEC_PlayerTooClose.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Invector.vCharacterController.AI.FSMBehaviour;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// Decision checking if player is too close to the archer
|
||||
/// Used to trigger retreat/flee behavior
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Decisions/Archer/Player Too Close")]
|
||||
public class DEC_PlayerTooClose : vStateDecision
|
||||
{
|
||||
public override string categoryName => "Archer/Combat";
|
||||
public override string defaultName => "Player Too Close";
|
||||
|
||||
[Header("Distance Configuration")]
|
||||
[Tooltip("Distance below which player is considered too close")]
|
||||
public float dangerDistance = 6f;
|
||||
|
||||
[Tooltip("Optional: check only if player is approaching (not retreating)")]
|
||||
public bool checkIfApproaching = false;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
private Vector3 lastPlayerPosition;
|
||||
private bool hasLastPosition = false;
|
||||
|
||||
public override bool Decide(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
Transform target = GetTarget(fsmBehaviour);
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[DEC_PlayerTooClose] No target found");
|
||||
hasLastPosition = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(fsmBehaviour.transform.position, target.position);
|
||||
bool tooClose = distance < dangerDistance;
|
||||
|
||||
// Optional: check if player is approaching
|
||||
if (checkIfApproaching && hasLastPosition)
|
||||
{
|
||||
float previousDistance = Vector3.Distance(fsmBehaviour.transform.position, lastPlayerPosition);
|
||||
bool isApproaching = distance < previousDistance;
|
||||
|
||||
if (!isApproaching)
|
||||
{
|
||||
tooClose = false; // Player is moving away, not a threat
|
||||
}
|
||||
}
|
||||
|
||||
// Store current position for next frame
|
||||
lastPlayerPosition = target.position;
|
||||
hasLastPosition = true;
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
Debug.Log($"[DEC_PlayerTooClose] Distance: {distance:F1}m - {(tooClose ? "TOO CLOSE" : "SAFE")}");
|
||||
}
|
||||
|
||||
return tooClose;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/DEC_PlayerTooClose.cs.meta
Normal file
2
Assets/AI/_Archer/DEC_PlayerTooClose.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3508c9b7b0af0d540a18d406403cff4d
|
||||
269
Assets/AI/_Archer/SA_FleeFromPlayer.cs
Normal file
269
Assets/AI/_Archer/SA_FleeFromPlayer.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/SA_FleeFromPlayer.cs.meta
Normal file
2
Assets/AI/_Archer/SA_FleeFromPlayer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5e9bb52e9254ac4895a3e9192dc2692
|
||||
105
Assets/AI/_Archer/SA_ShootArrow.cs
Normal file
105
Assets/AI/_Archer/SA_ShootArrow.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using Invector.vCharacterController.AI.FSMBehaviour;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArcherEnemy
|
||||
{
|
||||
/// <summary>
|
||||
/// State action that makes archer shoot an arrow
|
||||
/// Should be called in OnStateEnter or OnStateUpdate
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Actions/Archer/Shoot Arrow")]
|
||||
public class SA_ShootArrow : vStateAction
|
||||
{
|
||||
public override string categoryName => "Archer/Combat";
|
||||
public override string defaultName => "Shoot Arrow";
|
||||
|
||||
[Header("Configuration")]
|
||||
[Tooltip("Shoot once per state enter, or continuously?")]
|
||||
public bool shootOnce = true;
|
||||
|
||||
[Tooltip("Time between shots when shooting continuously")]
|
||||
public float shootInterval = 2f;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
private float lastShootTime = -999f;
|
||||
private bool hasShotThisState = false;
|
||||
|
||||
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)
|
||||
{
|
||||
hasShotThisState = false;
|
||||
|
||||
if (shootOnce)
|
||||
{
|
||||
TryShoot(fsmBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdate(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
if (shootOnce && hasShotThisState)
|
||||
{
|
||||
return; // Already shot once this state
|
||||
}
|
||||
|
||||
// Check interval for continuous shooting
|
||||
if (!shootOnce && Time.time >= lastShootTime + shootInterval)
|
||||
{
|
||||
TryShoot(fsmBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExit(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
// Stop any shooting sequence
|
||||
var shootingAI = fsmBehaviour.gameObject.GetComponent<ArcherShootingAI>();
|
||||
if (shootingAI != null)
|
||||
{
|
||||
shootingAI.StopShooting();
|
||||
}
|
||||
}
|
||||
|
||||
private void TryShoot(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
var shootingAI = fsmBehaviour.gameObject.GetComponent<ArcherShootingAI>();
|
||||
|
||||
if (shootingAI == null)
|
||||
{
|
||||
if (enableDebug) Debug.LogError("[SA_ShootArrow] No ArcherShootingAI component found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to shoot
|
||||
if (shootingAI.CanShoot())
|
||||
{
|
||||
shootingAI.StartShooting();
|
||||
hasShotThisState = true;
|
||||
lastShootTime = Time.time;
|
||||
|
||||
if (enableDebug) Debug.Log("[SA_ShootArrow] Shooting arrow");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enableDebug) Debug.Log("[SA_ShootArrow] Cannot shoot - conditions not met");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/AI/_Archer/SA_ShootArrow.cs.meta
Normal file
2
Assets/AI/_Archer/SA_ShootArrow.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbef6f8d442ae24408f1132173eca9e0
|
||||
70
Assets/AI/_Archer/SM_Skeleton_Archer_Arrow.prefab
Normal file
70
Assets/AI/_Archer/SM_Skeleton_Archer_Arrow.prefab
Normal file
@@ -0,0 +1,70 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &8704993173873703859
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 111.40727
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 30.45203
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 87.067314
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0.000000021855694
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: -8679921383154817045, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 919132149155446097, guid: 79022e6ea9c05a242b64fbe766aac28b,
|
||||
type: 3}
|
||||
propertyPath: m_Name
|
||||
value: SM_Skeleton_Archer_Arrow
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 79022e6ea9c05a242b64fbe766aac28b, type: 3}
|
||||
7
Assets/AI/_Archer/SM_Skeleton_Archer_Arrow.prefab.meta
Normal file
7
Assets/AI/_Archer/SM_Skeleton_Archer_Arrow.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3abb2598d285ed47a69dd12cccc0833
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user