using UnityEngine; public class Projectile : MonoBehaviour { [Header("Projectile Settings")] [Tooltip("How fast the projectile moves forward")] public float speed = 25f; [Tooltip("How long before the projectile destroys itself if it misses")] public float lifetime = 5f; [Tooltip("Offset for embedding the arrow into the target so it doesn't just stick on the very edge")] public float embedDepth = 0.2f; [Header("Loaded Offsets (Before Firing)")] [Tooltip("Adjust this to fix how the arrow sits in the hand before it is fired")] public Vector3 loadedPositionOffset = Vector3.zero; public Vector3 loadedRotationOffset = Vector3.zero; private float _damage; private LayerMask _targetLayers; private bool _hasHit; private Vector3 _lastPosition; public void Initialize(float damage, LayerMask targets) { _damage = damage; _targetLayers = targets; // Destroy the arrow after a few seconds if it flies into the sky Destroy(gameObject, lifetime); } private void Start() { _lastPosition = transform.position; } private void Update() { if (_hasHit) return; // Move the projectile forward Vector3 direction = transform.forward; float distance = speed * Time.deltaTime; transform.position += direction * distance; // Raycast from the previous position to the new position to detect hits // This prevents fast-moving arrows from passing straight through thin walls or enemies if (Physics.Raycast(_lastPosition, direction, out RaycastHit hit, distance, _targetLayers)) { HandleHit(hit); } _lastPosition = transform.position; } private void HandleHit(RaycastHit hit) { _hasHit = true; // Move the arrow right exactly to the hit point, then embed it slightly transform.position = hit.point + (transform.forward * embedDepth); // Parent the arrow to the object it hit so it sticks and moves with it transform.SetParent(hit.collider.transform, true); // If your Arrow Prefab has a Rigidbody on it, it will fall down due to gravity even after being parented! // We must destroy the Rigidbody component so it stays perfectly glued to the target. Rigidbody rb = GetComponent(); if (rb != null) { Destroy(rb); } // Deal damage Debug.Log($"Arrow hit {hit.collider.name} for {_damage} damage!"); hit.collider.GetComponent()?.TakeDamage(_damage); // Optional: Disable collider or script so it doesn't keep checking for hits Collider myCollider = GetComponent(); if (myCollider != null) myCollider.enabled = false; this.enabled = false; // Stops the Update loop } }