using System.Collections; using System.Collections.Generic; using UnityEngine; namespace BNG { /// /// A Grabbable object that can stick to objects and deal damage /// public class Arrow : MonoBehaviour { Rigidbody rb; Grabbable grab; public bool Flying = false; public float ZVel = 0; public Collider ShaftCollider; AudioSource impactSound; float flightTime = 0f; float destroyTime = 10f; // Time in seconds to destroy arrow Coroutine queueDestroy; public Projectile ProjectileObject; // Get this value from the ProjectileObject float arrowDamage; // Start is called before the first frame update void Start() { rb = GetComponent(); impactSound = GetComponent(); ShaftCollider = GetComponent(); grab = GetComponent(); if(ProjectileObject == null) { ProjectileObject = gameObject.AddComponent(); ProjectileObject.Damage = 50; ProjectileObject.StickToObject = true; ProjectileObject.enabled = false; } arrowDamage = ProjectileObject.Damage; } void FixedUpdate() { bool beingHeld = grab != null && grab.BeingHeld; // Align arrow with velocity if (!beingHeld && rb != null && rb.velocity != Vector3.zero && Flying && ZVel > 0.02) { rb.rotation = Quaternion.LookRotation(rb.velocity); } ZVel = transform.InverseTransformDirection(rb.velocity).z; if (Flying) { flightTime += Time.fixedDeltaTime; } // Cancel Destroy if we just picked this up if(queueDestroy != null && grab != null && grab.BeingHeld) { StopCoroutine(queueDestroy); } } public void ShootArrow(Vector3 shotForce) { flightTime = 0f; Flying = true; transform.parent = null; rb.isKinematic = false; rb.useGravity = true; rb.collisionDetectionMode = CollisionDetectionMode.Continuous; rb.constraints = RigidbodyConstraints.None; rb.AddForce(shotForce, ForceMode.VelocityChange); StartCoroutine(ReEnableCollider()); queueDestroy = StartCoroutine(QueueDestroy()); } IEnumerator QueueDestroy() { yield return new WaitForSeconds(destroyTime); if (grab != null && !grab.BeingHeld && transform.parent == null) { Destroy(this.gameObject); } } IEnumerator ReEnableCollider() { // Wait a few frames before re-enabling collider on bow shaft // This prevents the arrow from shooting ourselves, the bow, etc. // If you want the arrow to still have physics while attached, // parent a collider to the arrow near the tip int waitFrames = 3; for (int x = 0; x < waitFrames; x++) { yield return new WaitForFixedUpdate(); } ShaftCollider.enabled = true; } private void OnCollisionEnter(Collision collision) { // Ignore parent collisions if (transform.parent != null && collision.transform == transform.parent) { return; } // Don't count collisions if being held if(grab != null && grab.BeingHeld) { return; } // Don't Count Triggers if(collision.collider.isTrigger) { return; } string colNameLower = collision.transform.name.ToLower(); // Ignore other very close bows and arrows if (flightTime < 1 && (colNameLower.Contains("arrow") || colNameLower.Contains("bow"))) { Physics.IgnoreCollision(collision.collider, ShaftCollider, true); return; } // ignore player collision if quick shot if (flightTime < 1 && collision.transform.name.ToLower().Contains("player")) { Physics.IgnoreCollision(collision.collider, ShaftCollider, true); return; } // Damage if possible float zVel = System.Math.Abs(transform.InverseTransformDirection(rb.velocity).z); bool doStick = true; if (zVel > 0.02f && !rb.isKinematic) { Damageable d = collision.gameObject.GetComponent(); if (d) { d.DealDamage(arrowDamage, collision.GetContact(0).point, collision.GetContact(0).normal, true, gameObject, collision.collider.gameObject); } // Don't stick to dead objects if (d != null && d.Health <= 0) { doStick = false; } } // Check to stick to object if (!rb.isKinematic && Flying) { if (zVel > 0.02f) { if (grab != null && grab.BeingHeld) { grab.DropItem(false, false); } if (doStick) { tryStickArrow(collision); } Flying = false; playSoundInterval(2.462f, 2.68f); } } } // Attach to collider void tryStickArrow(Collision collision) { Rigidbody colRigid = collision.collider.GetComponent(); transform.parent = null; // Start out with arrow being in World space // If the collider is static then we don't need to do anything. Just stop it. if (collision.gameObject.isStatic) { rb.collisionDetectionMode = CollisionDetectionMode.Discrete; rb.isKinematic = true; } // Next try attaching to rigidbody via FixedJoint else if (colRigid != null && !colRigid.isKinematic) { FixedJoint joint = gameObject.AddComponent(); joint.connectedBody = colRigid; joint.enableCollision = false; joint.breakForce = float.MaxValue; joint.breakTorque = float.MaxValue; } else if (colRigid != null && colRigid.isKinematic && collision.transform.localScale == Vector3.one) { transform.SetParent(collision.transform); rb.useGravity = false; rb.collisionDetectionMode = CollisionDetectionMode.Discrete; rb.isKinematic = true; rb.constraints = RigidbodyConstraints.FreezeAll; rb.WakeUp(); } // Finally, try parenting or just setting the arrow to kinematic else { if (collision.transform.localScale == Vector3.one) { transform.SetParent(collision.transform); rb.constraints = RigidbodyConstraints.FreezeAll; } else { rb.collisionDetectionMode = CollisionDetectionMode.Discrete; rb.useGravity = false; rb.isKinematic = true; } } } void playSoundInterval(float fromSeconds, float toSeconds) { if (impactSound) { if (impactSound.isPlaying) { impactSound.Stop(); } impactSound.time = fromSeconds; impactSound.pitch = Time.timeScale; impactSound.Play(); impactSound.SetScheduledEndTime(AudioSettings.dspTime + (toSeconds - fromSeconds)); } } } }