You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			314 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			314 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C#
		
	
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
 | 
						|
//
 | 
						|
// Purpose: The arrow for the longbow
 | 
						|
//
 | 
						|
//=============================================================================
 | 
						|
 | 
						|
using UnityEngine;
 | 
						|
using System.Collections;
 | 
						|
 | 
						|
namespace Valve.VR.InteractionSystem
 | 
						|
{
 | 
						|
	//-------------------------------------------------------------------------
 | 
						|
	public class Arrow : MonoBehaviour
 | 
						|
	{
 | 
						|
		public ParticleSystem glintParticle;
 | 
						|
		public Rigidbody arrowHeadRB;
 | 
						|
		public Rigidbody shaftRB;
 | 
						|
 | 
						|
		public PhysicMaterial targetPhysMaterial;
 | 
						|
 | 
						|
		private Vector3 prevPosition;
 | 
						|
		private Quaternion prevRotation;
 | 
						|
		private Vector3 prevVelocity;
 | 
						|
		private Vector3 prevHeadPosition;
 | 
						|
 | 
						|
		public SoundPlayOneshot fireReleaseSound;
 | 
						|
		public SoundPlayOneshot airReleaseSound;
 | 
						|
		public SoundPlayOneshot hitTargetSound;
 | 
						|
 | 
						|
		public PlaySound hitGroundSound;
 | 
						|
 | 
						|
		private bool inFlight;
 | 
						|
		private bool released;
 | 
						|
		private bool hasSpreadFire = false;
 | 
						|
 | 
						|
		private int travelledFrames = 0;
 | 
						|
 | 
						|
		private GameObject scaleParentObject = null;
 | 
						|
 | 
						|
		private float initialMass;
 | 
						|
		private float initialDrag;
 | 
						|
        private float initialAngularDrag;
 | 
						|
        private RigidbodyInterpolation initialInterpolation;
 | 
						|
        private CollisionDetectionMode initialCollisionDetection;
 | 
						|
        private bool initialUseGravity;
 | 
						|
 | 
						|
 | 
						|
        private void Awake()
 | 
						|
        {
 | 
						|
            initialMass = shaftRB.mass;
 | 
						|
            initialDrag = shaftRB.drag;
 | 
						|
            initialAngularDrag = shaftRB.angularDrag;
 | 
						|
            initialInterpolation = shaftRB.interpolation;
 | 
						|
            initialCollisionDetection = shaftRB.collisionDetectionMode;
 | 
						|
            initialUseGravity = shaftRB.useGravity;
 | 
						|
            Destroy(this.GetComponent<Rigidbody>());
 | 
						|
        }
 | 
						|
 | 
						|
        //-------------------------------------------------
 | 
						|
        void Start()
 | 
						|
        {
 | 
						|
            Physics.IgnoreCollision(this.GetComponent<Collider>(), Player.instance.headCollider);
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		//-------------------------------------------------
 | 
						|
		void FixedUpdate()
 | 
						|
		{
 | 
						|
			if ( released && inFlight )
 | 
						|
			{
 | 
						|
				prevPosition = transform.position;
 | 
						|
				prevRotation = transform.rotation;
 | 
						|
				prevVelocity = shaftRB.velocity;
 | 
						|
				prevHeadPosition = arrowHeadRB.transform.position;
 | 
						|
				travelledFrames++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		public void StartRelease()
 | 
						|
        {
 | 
						|
            Rigidbody rb = this.gameObject.AddComponent<Rigidbody>();
 | 
						|
            rb.isKinematic = true;
 | 
						|
            if (shaftRB == null)
 | 
						|
                shaftRB = rb;
 | 
						|
 | 
						|
            shaftRB.mass = initialMass;
 | 
						|
            shaftRB.drag = initialDrag;
 | 
						|
            shaftRB.angularDrag = initialAngularDrag;
 | 
						|
			shaftRB.interpolation = initialInterpolation;
 | 
						|
			shaftRB.collisionDetectionMode = initialCollisionDetection;
 | 
						|
            shaftRB.useGravity = initialUseGravity;
 | 
						|
 | 
						|
			arrowHeadRB.GetComponent<FixedJoint>().connectedBody = rb;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
		//-------------------------------------------------
 | 
						|
		public void ArrowReleased( float inputVelocity )
 | 
						|
        {
 | 
						|
            inFlight = true;
 | 
						|
			released = true;
 | 
						|
 | 
						|
			airReleaseSound.Play();
 | 
						|
 | 
						|
			if ( glintParticle != null )
 | 
						|
			{
 | 
						|
				glintParticle.Play();
 | 
						|
			}
 | 
						|
 | 
						|
			if ( gameObject.GetComponentInChildren<FireSource>().isBurning )
 | 
						|
			{
 | 
						|
				fireReleaseSound.Play();
 | 
						|
			}
 | 
						|
 | 
						|
			// Check if arrow is shot inside or too close to an object
 | 
						|
			RaycastHit[] hits = Physics.SphereCastAll( transform.position, 0.01f, transform.forward, 0.80f, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore );
 | 
						|
			foreach ( RaycastHit hit in hits )
 | 
						|
			{
 | 
						|
				if ( hit.collider.gameObject != gameObject && hit.collider.gameObject != arrowHeadRB.gameObject && hit.collider != Player.instance.headCollider )
 | 
						|
				{
 | 
						|
					Destroy( gameObject );
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			travelledFrames = 0;
 | 
						|
			prevPosition = transform.position;
 | 
						|
			prevRotation = transform.rotation;
 | 
						|
			prevHeadPosition = arrowHeadRB.transform.position;
 | 
						|
			prevVelocity = GetComponent<Rigidbody>().velocity;
 | 
						|
 | 
						|
            SetCollisionMode(CollisionDetectionMode.ContinuousDynamic);
 | 
						|
 | 
						|
			Destroy( gameObject, 30 );
 | 
						|
		}
 | 
						|
 | 
						|
        protected void SetCollisionMode(CollisionDetectionMode newMode, bool force = false)
 | 
						|
        {
 | 
						|
            Rigidbody[] rigidBodies = this.GetComponentsInChildren<Rigidbody>();
 | 
						|
            for (int rigidBodyIndex = 0; rigidBodyIndex < rigidBodies.Length; rigidBodyIndex++)
 | 
						|
            {
 | 
						|
                if (rigidBodies[rigidBodyIndex].isKinematic == false || force)
 | 
						|
                    rigidBodies[rigidBodyIndex].collisionDetectionMode = newMode;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
		//-------------------------------------------------
 | 
						|
		void OnCollisionEnter( Collision collision )
 | 
						|
		{
 | 
						|
			if ( inFlight )
 | 
						|
			{
 | 
						|
				Rigidbody rb = GetComponent<Rigidbody>();
 | 
						|
				float rbSpeed = rb.velocity.sqrMagnitude;
 | 
						|
				bool canStick = ( targetPhysMaterial != null && collision.collider.sharedMaterial == targetPhysMaterial && rbSpeed > 0.2f );
 | 
						|
				bool hitBalloon = collision.collider.gameObject.GetComponent<Balloon>() != null;
 | 
						|
 | 
						|
				if ( travelledFrames < 2 && !canStick )
 | 
						|
				{
 | 
						|
					// Reset transform but halve your velocity
 | 
						|
					transform.position = prevPosition - prevVelocity * Time.deltaTime;
 | 
						|
					transform.rotation = prevRotation;
 | 
						|
 | 
						|
					Vector3 reflfectDir = Vector3.Reflect( arrowHeadRB.velocity, collision.contacts[0].normal );
 | 
						|
					arrowHeadRB.velocity = reflfectDir * 0.25f;
 | 
						|
					shaftRB.velocity = reflfectDir * 0.25f;
 | 
						|
 | 
						|
					travelledFrames = 0;
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				if ( glintParticle != null )
 | 
						|
				{
 | 
						|
					glintParticle.Stop( true );
 | 
						|
				}
 | 
						|
 | 
						|
				// Only play hit sounds if we're moving quickly
 | 
						|
				if ( rbSpeed > 0.1f )
 | 
						|
				{
 | 
						|
					hitGroundSound.Play();
 | 
						|
				}
 | 
						|
 | 
						|
				FireSource arrowFire = gameObject.GetComponentInChildren<FireSource>();
 | 
						|
				FireSource fireSourceOnTarget = collision.collider.GetComponentInParent<FireSource>();
 | 
						|
 | 
						|
				if ( arrowFire != null && arrowFire.isBurning && ( fireSourceOnTarget != null ) )
 | 
						|
				{
 | 
						|
					if ( !hasSpreadFire )
 | 
						|
					{
 | 
						|
						collision.collider.gameObject.SendMessageUpwards( "FireExposure", gameObject, SendMessageOptions.DontRequireReceiver );
 | 
						|
						hasSpreadFire = true;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					// Only count collisions with good speed so that arrows on the ground can't deal damage
 | 
						|
					// always pop balloons
 | 
						|
					if ( rbSpeed > 0.1f || hitBalloon )
 | 
						|
					{
 | 
						|
						collision.collider.gameObject.SendMessageUpwards( "ApplyDamage", SendMessageOptions.DontRequireReceiver );
 | 
						|
						gameObject.SendMessage( "HasAppliedDamage", SendMessageOptions.DontRequireReceiver );
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if ( hitBalloon )
 | 
						|
				{
 | 
						|
					// Revert my physics properties cause I don't want balloons to influence my travel
 | 
						|
					transform.position = prevPosition;
 | 
						|
					transform.rotation = prevRotation;
 | 
						|
					arrowHeadRB.velocity = prevVelocity;
 | 
						|
					Physics.IgnoreCollision( arrowHeadRB.GetComponent<Collider>(), collision.collider );
 | 
						|
					Physics.IgnoreCollision( shaftRB.GetComponent<Collider>(), collision.collider );
 | 
						|
				}
 | 
						|
 | 
						|
				if ( canStick )
 | 
						|
				{
 | 
						|
					StickInTarget( collision, travelledFrames < 2 );
 | 
						|
				}
 | 
						|
 | 
						|
				// Player Collision Check (self hit)
 | 
						|
				if ( Player.instance && collision.collider == Player.instance.headCollider )
 | 
						|
				{
 | 
						|
					Player.instance.PlayerShotSelf();
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		//-------------------------------------------------
 | 
						|
		private void StickInTarget( Collision collision, bool bSkipRayCast )
 | 
						|
		{
 | 
						|
			Vector3 prevForward = prevRotation * Vector3.forward;
 | 
						|
 | 
						|
			// Only stick in target if the collider is front of the arrow head
 | 
						|
			if ( !bSkipRayCast )
 | 
						|
			{
 | 
						|
				RaycastHit[] hitInfo;
 | 
						|
				hitInfo = Physics.RaycastAll( prevHeadPosition - prevVelocity * Time.deltaTime, prevForward, prevVelocity.magnitude * Time.deltaTime * 2.0f );
 | 
						|
				bool properHit = false;
 | 
						|
				for ( int i = 0; i < hitInfo.Length; ++i )
 | 
						|
				{
 | 
						|
					RaycastHit hit = hitInfo[i];
 | 
						|
 | 
						|
					if ( hit.collider == collision.collider )
 | 
						|
					{
 | 
						|
						properHit = true;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if ( !properHit )
 | 
						|
				{
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			Destroy( glintParticle );
 | 
						|
 | 
						|
			inFlight = false;
 | 
						|
 | 
						|
            SetCollisionMode(CollisionDetectionMode.Discrete, true);
 | 
						|
 | 
						|
            shaftRB.velocity = Vector3.zero;
 | 
						|
			shaftRB.angularVelocity = Vector3.zero;
 | 
						|
			shaftRB.isKinematic = true;
 | 
						|
			shaftRB.useGravity = false;
 | 
						|
			shaftRB.transform.GetComponent<BoxCollider>().enabled = false;
 | 
						|
 | 
						|
			arrowHeadRB.velocity = Vector3.zero;
 | 
						|
			arrowHeadRB.angularVelocity = Vector3.zero;
 | 
						|
			arrowHeadRB.isKinematic = true;
 | 
						|
			arrowHeadRB.useGravity = false;
 | 
						|
			arrowHeadRB.transform.GetComponent<BoxCollider>().enabled = false;
 | 
						|
 | 
						|
			hitTargetSound.Play();
 | 
						|
 | 
						|
 | 
						|
			// If the hit item has a parent, dock an empty object to that
 | 
						|
			// this fixes an issue with scaling hierarchy. I suspect this is not sustainable for a large object / scaling hierarchy.
 | 
						|
			scaleParentObject = new GameObject( "Arrow Scale Parent" );
 | 
						|
			Transform parentTransform = collision.collider.transform;
 | 
						|
 | 
						|
			// Don't do this for weebles because of how it has a fixed joint
 | 
						|
			ExplosionWobble wobble = collision.collider.gameObject.GetComponent<ExplosionWobble>();
 | 
						|
			if ( !wobble )
 | 
						|
			{
 | 
						|
				if ( parentTransform.parent )
 | 
						|
				{
 | 
						|
					parentTransform = parentTransform.parent;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			scaleParentObject.transform.parent = parentTransform;
 | 
						|
 | 
						|
			// Move the arrow to the place on the target collider we were expecting to hit prior to the impact itself knocking it around
 | 
						|
			transform.parent = scaleParentObject.transform;
 | 
						|
			transform.rotation = prevRotation;
 | 
						|
			transform.position = prevPosition;
 | 
						|
			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 ) ) );
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		//-------------------------------------------------
 | 
						|
		void OnDestroy()
 | 
						|
		{
 | 
						|
			if ( scaleParentObject != null )
 | 
						|
			{
 | 
						|
				Destroy( scaleParentObject );
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |