using UnityEngine; public class Projectile : MonoBehaviour { private GameObject target; private GameObject owner; private float damage; private float speed; private float lifetime; private bool registerEnergyOnHit; private bool initialized; private bool hasHit; public void Initialize( GameObject targetObject, float damageAmount, GameObject ownerObject, float moveSpeed, float lifeTime, bool registerEnergy = true) { target = targetObject; damage = damageAmount; owner = ownerObject; speed = moveSpeed; lifetime = lifeTime; registerEnergyOnHit = registerEnergy; initialized = true; hasHit = false; // The projectile uses its own raycast hit check. Disable the collider so // it cannot be destroyed or disturbed by the owner's launch socket. foreach (var collider in GetComponentsInChildren(true)) { collider.enabled = false; } if (lifetime > 0f) { Destroy(gameObject, lifetime); } } private void Update() { if (!initialized) { return; } if (target == null) { Destroy(gameObject); return; } // 目标根节点位于脚底,只取水平差值,让箭保持当前高度平行飞行。 Vector3 offset = target.transform.position - transform.position; offset.y = 0f; if (offset.sqrMagnitude <= 0.01f) { HitTarget(target); return; } Vector3 direction = offset.normalized; float distance = speed * Time.deltaTime; if (distance >= offset.magnitude) { HitTarget(target); return; } if (Physics.Raycast(transform.position, direction, out RaycastHit hit, distance)) { TryHit(hit.collider.gameObject); if (hit.collider.transform.root == target.transform.root) { return; } } transform.forward = direction; transform.position += direction * distance; } private void OnTriggerEnter(Collider other) { TryHit(other.gameObject); } private void OnCollisionEnter(Collision collision) { TryHit(collision.gameObject); } private void TryHit(GameObject hitObject) { if (!initialized || hitObject == null || target == null || IsOwner(hitObject)) { return; } if (hitObject.transform.root == target.transform.root) { HitTarget(hitObject); } } private void HitTarget(GameObject hitObject) { if (hasHit) { return; } UnitBase targetUnit = hitObject.GetComponentInParent(); BuildingHealth targetBuilding = hitObject.GetComponentInParent(); WorldHealthBar targetHealthBar = hitObject.GetComponentInParent(); if (targetUnit == null && targetBuilding == null && targetHealthBar == null) { return; } hasHit = true; if (targetUnit != null) { targetUnit.ReceiveDamage(damage, owner); } else if (targetBuilding != null) { targetBuilding.ReceiveDamage(damage, owner); } else if (targetHealthBar != null) { // 召唤师等特殊单位(有 WorldHealthBar 但没有 UnitBase) targetHealthBar.TakeDamage(damage, owner); } if (registerEnergyOnHit && owner != null) { UnitBase unit = owner.GetComponent(); if (unit != null) { unit.RegisterAttackHit(); } } Destroy(gameObject); } private bool IsOwner(GameObject hitObject) { return owner != null && (hitObject == owner || hitObject.transform.root == owner.transform.root); } }