private void CheckCollision()
        {
            RaycastHit hit;

            // send out a ray to see what gets hit
            if (Physics.Raycast(SpellStart.transform.position, Direction, out hit, MaxDistance, CollisionMask))
            {
                // we hit something, set the end object position
                SpellEnd.transform.position = hit.point;

                // additional randomization of end point
                SpellEnd.transform.position += (UnityEngine.Random.insideUnitSphere * EndPointRandomization);

                // play collision sound
                PlayCollisionSound(SpellEnd.transform.position);

                // play the collision particle system
                if (CollisionParticleSystem != null)
                {
                    CollisionParticleSystem.transform.position = hit.point;
                    CollisionParticleSystem.Play();
                }

                ApplyCollisionForce(hit.point);

                // notify listeners of collisions
                if (CollisionCallback != null)
                {
                    CollisionCallback(hit);
                }
            }
            else
            {
                // stop collision particle system
                if (CollisionParticleSystem != null)
                {
                    CollisionParticleSystem.Stop();
                }

                // extend beam to max length
                SpellEnd.transform.position = SpellStart.transform.position + (Direction * MaxDistance);

                // randomize end point a bit
                SpellEnd.transform.position += (UnityEngine.Random.insideUnitSphere * EndPointRandomization);
            }
        }
        private IEnumerator WhipForward()
        {
            const float distanceBack       = 25.0f;
            const float springForwardTime  = 0.10f;
            const float springBackwardTime = 0.25f;
            const float strikeWaitTime     = 0.1f;
            const float recoilWaitTime     = 0.1f;

            // remove the drag from all objects so they can move rapidly without decay
            for (int i = 0; i < WhipStart.transform.childCount; i++)
            {
                GameObject obj = WhipStart.transform.GetChild(i).gameObject;
                Rigidbody  rb  = obj.GetComponent <Rigidbody>();
                if (rb != null)
                {
                    rb.drag            = 0.0f;
                    rb.velocity        = Vector3.zero;
                    rb.angularVelocity = Vector3.zero;
                }
            }

            // activate the spring
            WhipSpring.SetActive(true);

            Vector3 anchor = WhipStart.GetComponent <Rigidbody>().position;

            // calculate the forward position first
            RaycastHit hit;
            Vector3    whipPositionForwards, whipPositionBackwards;

            if (Physics.Raycast(anchor, Direction, out hit, MaxDistance, CollisionMask))
            {
                Vector3 dir = (hit.point - anchor).normalized;
                whipPositionForwards = anchor + (dir * MaxDistance);

                // put the spring behind the whip to yank it back in the opposite of the direction
                whipPositionBackwards = anchor - (dir * distanceBack);
            }
            else
            {
                whipPositionForwards = anchor + (Direction * MaxDistance);

                // put the spring behind the whip to yank it back in the opposite of the direction
                whipPositionBackwards = anchor - (Direction * distanceBack);
            }


            //whipPositionBackwards -= (WhipStart.transform.forward * distanceBack);
            //whipPositionBackwards += (WhipStart.transform.up * 5.0f);

            // set back position
            WhipSpring.GetComponent <Rigidbody>().position = whipPositionBackwards;

            // wait a bit
            yield return(new WaitForSecondsLightning(springBackwardTime));

            // now put the spring in front of the whip to pull it forward
            WhipSpring.GetComponent <Rigidbody>().position = whipPositionForwards;

            yield return(new WaitForSecondsLightning(springForwardTime));

            // play whip crack sound
            if (WhipCrackAudioSource != null)
            {
                WhipCrackAudioSource.Play();
            }

            yield return(new WaitForSecondsLightning(strikeWaitTime));

            // show the strike paticle system
            if (CollisionParticleSystem != null)
            {
                CollisionParticleSystem.Play();
            }

            // create collision wherever the whip hit
            ApplyCollisionForce(SpellEnd.transform.position);

            // turn off the spring
            WhipSpring.SetActive(false);

            if (CollisionCallback != null)
            {
                CollisionCallback(SpellEnd.transform.position);
            }

            // wait a bit longer for the whip to recoil
            yield return(new WaitForSecondsLightning(recoilWaitTime));

            // put the drag back on
            for (int i = 0; i < WhipStart.transform.childCount; i++)
            {
                GameObject obj = WhipStart.transform.GetChild(i).gameObject;
                Rigidbody  rb  = obj.GetComponent <Rigidbody>();
                if (rb != null)
                {
                    rb.velocity        = Vector3.zero;
                    rb.angularVelocity = Vector3.zero;
                    rb.drag            = 0.5f;
                }
            }
        }