using UnityEngine; public class Projectile : MonoBehaviour { [Header("Projectile Settings")] [Tooltip("How fast the projectile moves forward")] public float speed = 50f; [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; [Tooltip("How strongly gravity affects the arrow. 1 = standard Unity gravity, 0 = flies perfectly straight.")] public float gravityScale = 1f; private float _damage; private LayerMask _targetLayers; private bool _hasHit; private Vector3 _lastPosition; private Vector3 _startPosition; private Vector3 _velocity; public void Initialize(float damage, LayerMask targets, float powerMultiplier) { // Damage is scaled based on how long the bow was drawn _damage = damage * powerMultiplier; _targetLayers = targets; // Initial velocity is scaled by power multiplier _velocity = transform.forward * (speed * powerMultiplier); // Destroy the arrow after a few seconds if it flies into the sky Destroy(gameObject, lifetime); _startPosition = transform.position; } private void Start() { _lastPosition = transform.position; } private void Update() { if (_hasHit) return; // Apply gravity to velocity over time (this creates the arc!) _velocity += Physics.gravity * gravityScale * Time.deltaTime; // Make the arrow physically point in the direction it is travelling if (_velocity.sqrMagnitude > 0.01f) { transform.forward = _velocity.normalized; } Vector3 direction = _velocity.normalized; float distance = _velocity.magnitude * Time.deltaTime; transform.position += _velocity * Time.deltaTime; // Raycast from the previous position to the new position to detect hits // We explicitly Ignore triggers so arrows don't hit the giant Lock-On detection spheres! if (Physics.Raycast(_lastPosition, direction, out RaycastHit hit, distance, _targetLayers, QueryTriggerInteraction.Ignore)) { 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 and log distance float distanceFlown = Vector3.Distance(_startPosition, transform.position); Debug.Log($"Arrow hit {hit.collider.name} for {_damage:F1} damage! It flew {distanceFlown:F1} meters."); hit.collider.GetComponentInParent()?.TakeDamage(_damage); hit.collider.GetComponentInParent()?.TakeDamage(_damage); // Try to hit the combat controller first so it can process blocks/stamina MeleeCombatLayerController playerCombat = hit.collider.GetComponentInParent(); if (playerCombat != null) playerCombat.TakeDamage(_damage); else hit.collider.GetComponentInParent()?.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 } }