Arrow.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: The arrow for the longbow
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System.Collections;
  8. namespace Valve.VR.InteractionSystem
  9. {
  10. //-------------------------------------------------------------------------
  11. public class Arrow : MonoBehaviour
  12. {
  13. public ParticleSystem glintParticle;
  14. public Rigidbody arrowHeadRB;
  15. public Rigidbody shaftRB;
  16. public PhysicMaterial targetPhysMaterial;
  17. private Vector3 prevPosition;
  18. private Quaternion prevRotation;
  19. private Vector3 prevVelocity;
  20. private Vector3 prevHeadPosition;
  21. public SoundPlayOneshot fireReleaseSound;
  22. public SoundPlayOneshot airReleaseSound;
  23. public SoundPlayOneshot hitTargetSound;
  24. public PlaySound hitGroundSound;
  25. private bool inFlight;
  26. private bool released;
  27. private bool hasSpreadFire = false;
  28. private int travelledFrames = 0;
  29. private GameObject scaleParentObject = null;
  30. //-------------------------------------------------
  31. void Start()
  32. {
  33. Physics.IgnoreCollision( shaftRB.GetComponent<Collider>(), Player.instance.headCollider );
  34. }
  35. //-------------------------------------------------
  36. void FixedUpdate()
  37. {
  38. if ( released && inFlight )
  39. {
  40. prevPosition = transform.position;
  41. prevRotation = transform.rotation;
  42. prevVelocity = GetComponent<Rigidbody>().velocity;
  43. prevHeadPosition = arrowHeadRB.transform.position;
  44. travelledFrames++;
  45. }
  46. }
  47. //-------------------------------------------------
  48. public void ArrowReleased( float inputVelocity )
  49. {
  50. inFlight = true;
  51. released = true;
  52. airReleaseSound.Play();
  53. if ( glintParticle != null )
  54. {
  55. glintParticle.Play();
  56. }
  57. if ( gameObject.GetComponentInChildren<FireSource>().isBurning )
  58. {
  59. fireReleaseSound.Play();
  60. }
  61. // Check if arrow is shot inside or too close to an object
  62. RaycastHit[] hits = Physics.SphereCastAll( transform.position, 0.01f, transform.forward, 0.80f, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore );
  63. foreach ( RaycastHit hit in hits )
  64. {
  65. if ( hit.collider.gameObject != gameObject && hit.collider.gameObject != arrowHeadRB.gameObject && hit.collider != Player.instance.headCollider )
  66. {
  67. Destroy( gameObject );
  68. return;
  69. }
  70. }
  71. travelledFrames = 0;
  72. prevPosition = transform.position;
  73. prevRotation = transform.rotation;
  74. prevHeadPosition = arrowHeadRB.transform.position;
  75. prevVelocity = GetComponent<Rigidbody>().velocity;
  76. SetCollisionMode(CollisionDetectionMode.ContinuousDynamic);
  77. Destroy( gameObject, 30 );
  78. }
  79. protected void SetCollisionMode(CollisionDetectionMode newMode, bool force = false)
  80. {
  81. Rigidbody[] rigidBodies = this.GetComponentsInChildren<Rigidbody>();
  82. for (int rigidBodyIndex = 0; rigidBodyIndex < rigidBodies.Length; rigidBodyIndex++)
  83. {
  84. if (rigidBodies[rigidBodyIndex].isKinematic == false || force)
  85. rigidBodies[rigidBodyIndex].collisionDetectionMode = newMode;
  86. }
  87. }
  88. //-------------------------------------------------
  89. void OnCollisionEnter( Collision collision )
  90. {
  91. if ( inFlight )
  92. {
  93. Rigidbody rb = GetComponent<Rigidbody>();
  94. float rbSpeed = rb.velocity.sqrMagnitude;
  95. bool canStick = ( targetPhysMaterial != null && collision.collider.sharedMaterial == targetPhysMaterial && rbSpeed > 0.2f );
  96. bool hitBalloon = collision.collider.gameObject.GetComponent<Balloon>() != null;
  97. if ( travelledFrames < 2 && !canStick )
  98. {
  99. // Reset transform but halve your velocity
  100. transform.position = prevPosition - prevVelocity * Time.deltaTime;
  101. transform.rotation = prevRotation;
  102. Vector3 reflfectDir = Vector3.Reflect( arrowHeadRB.velocity, collision.contacts[0].normal );
  103. arrowHeadRB.velocity = reflfectDir * 0.25f;
  104. shaftRB.velocity = reflfectDir * 0.25f;
  105. travelledFrames = 0;
  106. return;
  107. }
  108. if ( glintParticle != null )
  109. {
  110. glintParticle.Stop( true );
  111. }
  112. // Only play hit sounds if we're moving quickly
  113. if ( rbSpeed > 0.1f )
  114. {
  115. hitGroundSound.Play();
  116. }
  117. FireSource arrowFire = gameObject.GetComponentInChildren<FireSource>();
  118. FireSource fireSourceOnTarget = collision.collider.GetComponentInParent<FireSource>();
  119. if ( arrowFire != null && arrowFire.isBurning && ( fireSourceOnTarget != null ) )
  120. {
  121. if ( !hasSpreadFire )
  122. {
  123. collision.collider.gameObject.SendMessageUpwards( "FireExposure", gameObject, SendMessageOptions.DontRequireReceiver );
  124. hasSpreadFire = true;
  125. }
  126. }
  127. else
  128. {
  129. // Only count collisions with good speed so that arrows on the ground can't deal damage
  130. // always pop balloons
  131. if ( rbSpeed > 0.1f || hitBalloon )
  132. {
  133. collision.collider.gameObject.SendMessageUpwards( "ApplyDamage", SendMessageOptions.DontRequireReceiver );
  134. gameObject.SendMessage( "HasAppliedDamage", SendMessageOptions.DontRequireReceiver );
  135. }
  136. }
  137. if ( hitBalloon )
  138. {
  139. // Revert my physics properties cause I don't want balloons to influence my travel
  140. transform.position = prevPosition;
  141. transform.rotation = prevRotation;
  142. arrowHeadRB.velocity = prevVelocity;
  143. Physics.IgnoreCollision( arrowHeadRB.GetComponent<Collider>(), collision.collider );
  144. Physics.IgnoreCollision( shaftRB.GetComponent<Collider>(), collision.collider );
  145. }
  146. if ( canStick )
  147. {
  148. StickInTarget( collision, travelledFrames < 2 );
  149. }
  150. // Player Collision Check (self hit)
  151. if ( Player.instance && collision.collider == Player.instance.headCollider )
  152. {
  153. Player.instance.PlayerShotSelf();
  154. }
  155. }
  156. }
  157. //-------------------------------------------------
  158. private void StickInTarget( Collision collision, bool bSkipRayCast )
  159. {
  160. Vector3 prevForward = prevRotation * Vector3.forward;
  161. // Only stick in target if the collider is front of the arrow head
  162. if ( !bSkipRayCast )
  163. {
  164. RaycastHit[] hitInfo;
  165. hitInfo = Physics.RaycastAll( prevHeadPosition - prevVelocity * Time.deltaTime, prevForward, prevVelocity.magnitude * Time.deltaTime * 2.0f );
  166. bool properHit = false;
  167. for ( int i = 0; i < hitInfo.Length; ++i )
  168. {
  169. RaycastHit hit = hitInfo[i];
  170. if ( hit.collider == collision.collider )
  171. {
  172. properHit = true;
  173. break;
  174. }
  175. }
  176. if ( !properHit )
  177. {
  178. return;
  179. }
  180. }
  181. Destroy( glintParticle );
  182. inFlight = false;
  183. SetCollisionMode(CollisionDetectionMode.Discrete, true);
  184. shaftRB.velocity = Vector3.zero;
  185. shaftRB.angularVelocity = Vector3.zero;
  186. shaftRB.isKinematic = true;
  187. shaftRB.useGravity = false;
  188. shaftRB.transform.GetComponent<BoxCollider>().enabled = false;
  189. arrowHeadRB.velocity = Vector3.zero;
  190. arrowHeadRB.angularVelocity = Vector3.zero;
  191. arrowHeadRB.isKinematic = true;
  192. arrowHeadRB.useGravity = false;
  193. arrowHeadRB.transform.GetComponent<BoxCollider>().enabled = false;
  194. hitTargetSound.Play();
  195. // If the hit item has a parent, dock an empty object to that
  196. // this fixes an issue with scaling hierarchy. I suspect this is not sustainable for a large object / scaling hierarchy.
  197. scaleParentObject = new GameObject( "Arrow Scale Parent" );
  198. Transform parentTransform = collision.collider.transform;
  199. // Don't do this for weebles because of how it has a fixed joint
  200. ExplosionWobble wobble = collision.collider.gameObject.GetComponent<ExplosionWobble>();
  201. if ( !wobble )
  202. {
  203. if ( parentTransform.parent )
  204. {
  205. parentTransform = parentTransform.parent;
  206. }
  207. }
  208. scaleParentObject.transform.parent = parentTransform;
  209. // Move the arrow to the place on the target collider we were expecting to hit prior to the impact itself knocking it around
  210. transform.parent = scaleParentObject.transform;
  211. transform.rotation = prevRotation;
  212. transform.position = prevPosition;
  213. transform.position = collision.contacts[0].point - transform.forward * ( 0.75f - ( Util.RemapNumberClamped( prevVelocity.magnitude, 0f, 10f, 0.0f, 0.1f ) + Random.Range( 0.0f, 0.05f ) ) );
  214. }
  215. //-------------------------------------------------
  216. void OnDestroy()
  217. {
  218. if ( scaleParentObject != null )
  219. {
  220. Destroy( scaleParentObject );
  221. }
  222. }
  223. }
  224. }