using System; using System.Collections.Generic; using ActionGameFramework.Helpers; using UnityEngine; namespace ActionGameFramework.Projectiles { /// /// Simple IProjectile implementation for projectile that flies in a parabolic arc with no further m_Acceleration. /// [RequireComponent(typeof(Rigidbody))] public class BallisticProjectile : MonoBehaviour, IProjectile { public BallisticArcHeight arcPreference; public BallisticFireMode fireMode; [Range(-90, 90)] public float firingAngle; public float startSpeed; /// /// The duration that collisions between this gameObjects colliders /// and the given colliders will be ignored. /// public float collisionIgnoreTime = 0.35f; protected bool m_Fired, m_IgnoringCollsions; protected float m_CollisionIgnoreCount = 0; protected Rigidbody m_Rigidbody; protected List m_CollidersIgnoring = new List(); /// /// All the colliders attached to this gameObject and its children /// protected Collider[] m_Colliders; public event Action fired; /// /// Fires this projectile from a designated start point to a designated world coordinate. /// Automatically sets firing angle to suit launch speed unless angle is overridden, in which case launch speed is overridden to suit angle. /// /// Start point of the flight. /// Target point to fly to. public virtual void FireAtPoint(Vector3 startPoint, Vector3 targetPoint) { transform.position = startPoint; Vector3 firingVector; switch (fireMode) { case BallisticFireMode.UseLaunchSpeed: firingVector = Ballistics.CalculateBallisticFireVectorFromVelocity(startPoint, targetPoint, startSpeed, arcPreference); firingAngle = Ballistics.CalculateBallisticFireAngle(startPoint, targetPoint, startSpeed, arcPreference); break; case BallisticFireMode.UseLaunchAngle: firingVector = Ballistics.CalculateBallisticFireVectorFromAngle(startPoint, targetPoint, firingAngle); startSpeed = firingVector.magnitude; break; default: throw new ArgumentOutOfRangeException(); } Fire(firingVector); } /// /// Fires this projectile in a designated direction at the launch speed. /// /// Start point of the flight. /// Vector representing launch direction. public virtual void FireInDirection(Vector3 startPoint, Vector3 fireVector) { transform.position = startPoint; Fire(fireVector.normalized * startSpeed); } /// /// Fires this projectile at a designated starting velocity, overriding any starting speeds. /// /// Start point of the flight. /// Vector3 representing launch velocity. public void FireAtVelocity(Vector3 startPoint, Vector3 fireVelocity) { transform.position = startPoint; startSpeed = fireVelocity.magnitude; Fire(fireVelocity); } /// /// Ignores all collisions between this and the given colliders for a defined period of time /// /// Colliders to ignore public void IgnoreCollision(Collider[] collidersToIgnore) { if (collisionIgnoreTime > 0) { m_IgnoringCollsions = true; m_CollisionIgnoreCount = 0.0f; foreach (Collider colliderToIgnore in collidersToIgnore) { if (m_CollidersIgnoring.Contains(colliderToIgnore)) { continue; } foreach (Collider projectileCollider in m_Colliders) { Physics.IgnoreCollision(colliderToIgnore, projectileCollider, true); } m_CollidersIgnoring.Add(colliderToIgnore); } } } protected virtual void Awake() { m_Rigidbody = GetComponent(); m_Colliders = GetComponentsInChildren(); } protected virtual void Update() { if (!m_Fired) { return; } // If we are ignoring collisions, increment counter. // If counter is complete, reenable collisions if (m_IgnoringCollsions) { m_CollisionIgnoreCount += Time.deltaTime; if (m_CollisionIgnoreCount >= collisionIgnoreTime) { m_IgnoringCollsions = false; foreach (Collider colliderIgnoring in m_CollidersIgnoring) { foreach (Collider projectileCollider in m_Colliders) { Physics.IgnoreCollision(colliderIgnoring, projectileCollider, false); } } m_CollidersIgnoring.Clear(); } } transform.rotation = Quaternion.LookRotation(m_Rigidbody.velocity); } protected virtual void Fire(Vector3 firingVector) { transform.rotation = Quaternion.LookRotation(firingVector); m_Rigidbody.velocity = firingVector; m_Fired = true; m_CollidersIgnoring.Clear(); // 因为炮弹没有模型,本身就是一个带碰撞体的粒子特效 ParticleSystem ps = GetComponent(); if (ps == null) ps = transform.GetChild(0).GetComponent(); ps?.Play(); if (fired != null) { fired(); } } #if UNITY_EDITOR protected virtual void OnValidate() { if (Mathf.Abs(firingAngle) >= 90f) { firingAngle = Mathf.Sign(firingAngle) * 89.5f; Debug.LogWarning("Clamping angle to under +- 90 degrees to avoid errors."); } } #endif } }