//========= Copyright 2016-2018, HTC Corporation. All rights reserved. =========== using UnityEngine; using HTC.UnityPlugin.Utility; namespace HTC.UnityPlugin.Pointer3D { public class ProjectileGenerator : BaseRaySegmentGenerator { public float velocity = 2f; public Vector3 gravity = Vector3.down; private float m_velocity; private Vector3 m_gravity; private float maxHalfJourney;// maximum distance if projectile angle equals to 45 degree using given velocity private float accY;// vertical accelerate private Vector3 systemY; private Vector3 systemX; private Vector3 v0;// initial velocity vector private float v0X;// initial horizontal velocity private float v0Y;// initial vertical velocity private bool isHeighPeek;// if included angle between v0 and systemX is larger then 45 degree private float halfJourney;// half maximum distance of projectile private float contactPointTime; private float nextContactPointTime; private float rayDistance; private float nextRayDistance; private void CalculateAcc() { accY = -gravity.magnitude; } private void CalculatePeekDistanceMax() { maxHalfJourney = 0.5f * velocity * velocity / Mathf.Abs(accY); } protected override void Start() { base.Start(); CalculateAcc(); CalculatePeekDistanceMax(); } public override void ResetSegments() { var velocityChanged = ChangeProp.Set(ref m_velocity, velocity); var gravityChanged = ChangeProp.Set(ref m_gravity, gravity); if (gravityChanged) { CalculateAcc(); } if (velocityChanged || gravityChanged) { CalculatePeekDistanceMax(); } systemY = -gravity; systemX = transform.forward; Vector3.OrthoNormalize(ref systemY, ref systemX); v0 = transform.forward * velocity; v0X = Vector3.Dot(v0, systemX); v0Y = Vector3.Dot(v0, systemY); isHeighPeek = Mathf.Abs(v0Y) > Mathf.Abs(v0X); contactPointTime = v0Y / accY; halfJourney = Mathf.Abs(contactPointTime); rayDistance = nextRayDistance = 0f; } public override bool NextSegment(out Vector3 direction, out float distance) { if (isHeighPeek) { if (contactPointTime < 0f) { nextContactPointTime = 0f; } else { nextContactPointTime = contactPointTime + halfJourney; } } else { if (contactPointTime < 0f) { nextContactPointTime = 0f; } else if (contactPointTime == 0f) { if (halfJourney == 0f) { nextContactPointTime = contactPointTime + maxHalfJourney * 0.3f; } else { nextContactPointTime = contactPointTime + halfJourney; } } else { // cap to maxHalfJourney to avoid small segment //nextContactPointTime = contactPointTime + maxHalfJourney; nextContactPointTime = contactPointTime + Mathf.Lerp(halfJourney, maxHalfJourney, 0.3f); } } var lastDistance = rayDistance; GetTangentLineIntersectDistance(contactPointTime, nextContactPointTime, out rayDistance, out nextRayDistance, out direction); distance = rayDistance + lastDistance; // shift for next iteration rayDistance = nextRayDistance; contactPointTime = nextContactPointTime; if (distance <= Pointer3DRaycaster.MIN_SEGMENT_DISTANCE) { distance = Pointer3DRaycaster.MIN_SEGMENT_DISTANCE; //NextSegment(out direction, out distance); } return true; } private void GetTangentLineIntersectDistance(float tA, float tB, out float dA, out float dB, out Vector3 directionA) { if (tA == tB) { dA = dB = 0f; directionA = (systemY * (accY * tA / v0X) + systemX).normalized; } else { var vA = new Vector2(v0X * tA, 0.5f * accY * tA * tA); var vB = new Vector2(v0X * tB, 0.5f * accY * tB * tB); var mA = accY * tA / v0X; var mB = accY * tB / v0X; // C is intersect point between line through A and line through B var vC = default(Vector2); vC.x = (mA * vA.x - mB * vB.x - vA.y + vB.y) / (mA - mB); vC.y = mA * (vC.x - vA.x) + vA.y; dA = (vC - vA).magnitude; dB = (vC - vB).magnitude; directionA = (systemY * mA + systemX).normalized; } } } }