Created
October 10, 2021 18:07
-
-
Save Juansero29/f3f5a0a7e6c68588dd97ea1fe5d9b59e to your computer and use it in GitHub Desktop.
Basketball Launch Arc Renderer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
[RequireComponent(typeof(LineRenderer))] | |
public class LaunchArcRenderer : MonoBehaviour | |
{ | |
/* | |
* SUVAT EQUATIONS | |
* S | horizontal distance traveled | |
* U | initial velocity | |
* Ux | initial horizontal velocity | |
* Uy | initial vertical velocity | |
* V | final velocity | |
* A | standard acceleration due to gravity on the surface of the earth (≈ 9.807 m/s^2) | |
* T | travel time | |
* h | initial height | |
* hmax | maximum height | |
* α | release angle relative to horizontal | |
* | |
* | |
* When h = 0: | |
* | |
* Ux = U * cos(α) | |
* Uy = U * sin(α) | |
* T = 2 * Uy / A | |
* hmax = (v^2 sin^2(α))/(2 * A) | |
* hmax = Uy^2 / (2 * A) | |
* S = (v^2 sin(2 α))/A (old) | |
* S = 2 * Ux * Uy / A | |
* | |
* S = Ux * T | |
* Ux = S/T | |
* Uy = (hmax/T + (1/2)) * A * T | |
* T = (2 v sin(α))/A | |
* | |
* When h != 0: | |
* | |
* Ux = U * cos(α) | |
* Uy = U * sin(α) | |
* t = (Uy + sqrt(Uy^2 + 2 * A * h)) / A | |
* S = Ux * [Uy + sqrt(Uy^2 + 2 * A * h)] / A | |
* hmax = (h + Uy²) / (2 * A) | |
* Uy = sqrt( (hmax - h) / (2*A)) | |
* U = sqrt(V - 2A * hmax) | |
*/ | |
#region Private Fields | |
private float _maximumDistance; | |
private float _initialVelocity; | |
private Vector3 _initialVelocityVector; | |
private float _finalVelocity = 0.0f; | |
private float _gravity; | |
private float _initialHeight; | |
private float _maximumHeight; | |
private float _angleInDegrees; | |
private float _angleInRadians; | |
private Vector3 _mouseWorldPosition; | |
private float _distanceBetweenObjectAndMouse; | |
private Vector3 _ArcOriginPoint; | |
private Rigidbody _RigidBody; | |
private Plane _launchArcPlane; | |
private Vector3 _mouseDirection; | |
private LineRenderer _LineRenderer; | |
#endregion | |
#region Public Fields | |
/// <summary> | |
/// How many segments the arc is going to have (smoothness vs. sharpness) | |
/// </summary> | |
public int NumberOfSegments; | |
private bool _hasShot; | |
private Vector3[] _arcPositions; | |
#endregion | |
private void Awake() | |
{ | |
_ArcOriginPoint = transform.position; | |
_RigidBody = GetComponent<Rigidbody>(); | |
_LineRenderer = GetComponent<LineRenderer>(); | |
_gravity = Math.Abs(Physics2D.gravity.y); | |
CreatePlaneThatCutsBallInTwoVertically(); | |
void CreatePlaneThatCutsBallInTwoVertically() | |
{ | |
_launchArcPlane = new Plane(transform.forward, transform.position); | |
} | |
} | |
private void Update() | |
{ | |
if (_LineRenderer == null || !Application.isPlaying) return; | |
_initialHeight = transform.position.y; | |
_angleInDegrees = GetAngleInDegreesBetweenMouseAndObject2(); | |
_angleInRadians = GetAngleInRadiansFromAngleInDegrees(); | |
_maximumHeight = GetMaximumHeightForObject(); | |
CalculateInitialVelocityUsingFinalVelocityMaximumHeightAndAngle(); | |
// CalculateInitialVelocityUsingMaximumHeightAndAngle(); | |
//CalculateInitialVelocityUsingGravityAndMaximumHeight(); | |
// SetVelocityUsingDistanceBetweenObjectAndMouse(); | |
CalculateVelocityVectorUsingInitialVelocityAndAngle(); | |
//CalculateVelocityAndVectorUsingMouseWorldPosition(); | |
//CalculateVelocityAndVectorUsingMousePositionAndObjectPosition(); | |
// _maximumDistance = GetMaximumDistanceUsingVelocityGravityAndAngle(); | |
_maximumDistance = GetMaximumDistanceUsingVelocityVectorInitalHeightAndGravity(); | |
Debug.Log($"_angleInDegrees: {_angleInDegrees} " + | |
$" _maximumHeight: {_maximumHeight}" + | |
$"_initialVelocity: {_initialVelocity}" + | |
$"_initialVelocityVector: {_initialVelocityVector}"); | |
if (Input.GetMouseButtonDown(0)) | |
{ | |
SetVelocityAndGravityForObject(); | |
} | |
if (!_hasShot) | |
{ | |
_arcPositions = CalculateArcArrayPositions(); | |
SetLineRendererPositions(); | |
} | |
} | |
#region Formulas | |
#region Maximum Height | |
private float GetMaximumHeightForObject() | |
{ | |
return _mouseWorldPosition.y - transform.position.y; | |
} | |
#endregion | |
#region Velocity | |
private void CalculateVelocityVector() | |
{ | |
CalculateInitialVelocityUsingGravityAndMaximumHeight(); | |
// SetVelocityUsingDistanceBetweenObjectAndMouse(); | |
CalculateVelocityVectorUsingInitialVelocityAndAngle(); | |
} | |
#region Velocity | |
private void CalculateInitialVelocityUsingGravityAndMaximumHeight() | |
{ | |
// the object should travel _initialVelocity distance units in 1 second | |
_initialVelocity = Mathf.Sqrt(2) * Mathf.Sqrt(_gravity) * Mathf.Sqrt(_maximumHeight); | |
} | |
private void SetVelocityUsingDistanceBetweenObjectAndMouse() | |
{ | |
_initialVelocity = _distanceBetweenObjectAndMouse * 1.2F; | |
} | |
private void CalculateInitialVelocityUsingMaximumHeightAndAngle() | |
{ | |
_initialVelocity = Mathf.Sqrt((2 * _gravity * _maximumHeight) / (Mathf.Sin(_angleInRadians) * Mathf.Sin(_angleInRadians))); | |
} | |
private void CalculateInitialVelocityUsingFinalVelocityMaximumHeightAndAngle() | |
{ | |
_initialVelocity = Mathf.Sqrt(2 * _gravity * _maximumHeight) / Mathf.Sin(_angleInRadians); | |
} | |
#endregion | |
#region Velocity Vector | |
private void CalculateVelocityVectorUsingInitialVelocityAndAngle() | |
{ | |
_initialVelocityVector = new Vector3(_initialVelocity * Mathf.Cos(_angleInDegrees * Mathf.Deg2Rad), _initialVelocity * Mathf.Sin(_angleInDegrees * Mathf.Deg2Rad), 0); | |
} | |
private void CalculateVelocityAndVectorUsingMouseWorldPosition() | |
{ | |
_initialVelocityVector = _mouseWorldPosition - transform.position; | |
_initialVelocity = Mathf.Sqrt(Mathf.Pow(_initialVelocityVector.x, 2) + Mathf.Pow(_initialVelocityVector.y, 2)); | |
} | |
private void CalculateVelocityAndVectorUsingMousePositionAndObjectPosition() | |
{ | |
_initialVelocityVector = (_mouseWorldPosition - transform.position); | |
_initialVelocity = Mathf.Sqrt(Mathf.Pow(_initialVelocityVector.x, 2) + Mathf.Pow(_initialVelocityVector.y, 2)); | |
} | |
#endregion | |
#endregion | |
#region Maximum Distance | |
private float GetMaximumDistanceUsingVelocityGravityAndAngle() | |
{ | |
return (Mathf.Sin(_angleInRadians * 2) * Mathf.Pow(_initialVelocity, 2)) / _gravity; | |
} | |
private float GetMaximumDistanceUsingVelocityVectorInitalHeightAndGravity() | |
{ | |
return _initialVelocityVector.x * (_initialVelocityVector.y + Mathf.Sqrt(Mathf.Pow(_initialVelocityVector.y, 2) + 2 * _gravity * _initialHeight)) / _gravity; | |
} | |
#endregion | |
#region Angle | |
float GetAngleInRadiansFromAngleInDegrees() | |
{ | |
return Mathf.Deg2Rad * _angleInDegrees; | |
} | |
private float GetAngleInDegreesBetweenMouseAndObject() | |
{ | |
_mouseWorldPosition = GetMousePositionFrom3DWorld(); | |
_mouseWorldPosition.z = transform.position.z; | |
_distanceBetweenObjectAndMouse = Vector3.Distance(transform.position, _mouseWorldPosition); | |
_mouseDirection = _mouseWorldPosition - transform.position; | |
var angle = Vector3.Angle(_mouseDirection, transform.right); | |
Debug.Log($"angle: {angle}"); | |
return angle; | |
} | |
private float GetAngleInDegreesBetweenMouseAndObject2() | |
{ | |
_mouseWorldPosition = GetMousePositionFrom3DWorld(); | |
_mouseWorldPosition.z = transform.position.z; | |
_distanceBetweenObjectAndMouse = Vector3.Distance(transform.position, _mouseWorldPosition); | |
_mouseDirection = _mouseWorldPosition - transform.position; | |
var angle = Vector2.SignedAngle(Vector2.left, _mouseDirection); | |
Debug.Log($"angle: {angle}"); | |
return angle; | |
} | |
#endregion | |
#endregion | |
#region Interactions | |
private Vector3 GetMousePositionFrom3DWorld() | |
{ | |
var ray = Camera.main.ScreenPointToRay(Input.mousePosition); | |
if (_launchArcPlane.Raycast(ray, out float distance)) | |
{ | |
return ray.GetPoint(distance); | |
} | |
return transform.position; | |
} | |
private void SetVelocityAndGravityForObject() | |
{ | |
_hasShot = true; | |
_RigidBody.useGravity = true; | |
_RigidBody.velocity = _initialVelocityVector; | |
} | |
#endregion | |
#region Arc | |
private void SetLineRendererPositions() | |
{ | |
_LineRenderer.positionCount = NumberOfSegments + 1; | |
_LineRenderer.SetPositions(_arcPositions); | |
} | |
private Vector3[] CalculateArcArrayPositions() | |
{ | |
var positionArray = CreatePositionsArray(); | |
for (int i = 0; i <= NumberOfSegments; i++) | |
{ | |
var normalizedProgress = (float)i / (float)NumberOfSegments; | |
positionArray[i] = CalculateArcPoint(normalizedProgress, _maximumDistance) + _ArcOriginPoint; | |
} | |
return positionArray; | |
Vector3[] CreatePositionsArray() | |
{ | |
return new Vector3[NumberOfSegments + 1]; | |
} | |
} | |
private Vector3 CalculateArcPoint(float normalizedProgress, float maximumDistance) | |
{ | |
var x = (normalizedProgress * maximumDistance); | |
var y = x * Mathf.Tan(_angleInRadians) - (_gravity * Mathf.Pow(x, 2) / (2 * Mathf.Pow(_initialVelocity, 2) * Mathf.Pow(Mathf.Cos(_angleInRadians), 2))); | |
return new Vector3(x, y); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment