using UnityEngine; namespace Code.Lab5 { public class Projectile : MonoBehaviour { private bool _set; private Vector3 _firePos; private Vector3 _direction; private float _speed; private float _timeElapsed; private void Update() { if (!_set) return; _timeElapsed += Time.deltaTime; transform.position = _firePos + _direction * (_speed * _timeElapsed); transform.position += Physics.gravity * (_timeElapsed * _timeElapsed) / 2.0f; if (transform.position.y < -1.0f) { Destroy(gameObject); } } public void Set(Vector3 firePos, Vector3 direction, float speed) { _firePos = firePos; _direction = direction.normalized; _speed = speed; transform.position = firePos; _set = true; _timeElapsed = 0f; } private float GetLandingTime(float height = 0.0f) { var position = transform.position; var valueInt = _direction.y * _direction.y * (_speed * _speed); valueInt -= Physics.gravity.y * 2 * (position.y - height); if (valueInt < 0) return -1.0f; valueInt = Mathf.Sqrt(valueInt); var valueAdd = -_direction.y * _speed; var valueSub = -_direction.y * _speed; valueAdd = (valueAdd + valueInt) / Physics.gravity.y; valueSub = (valueSub - valueInt) / Physics.gravity.y; if (float.IsNaN(valueAdd) && !float.IsNaN(valueSub)) return valueSub; if (!float.IsNaN(valueAdd) && float.IsNaN(valueSub)) return valueAdd; if (float.IsNaN(valueAdd) && float.IsNaN(valueSub)) return -1.0f; var time = Mathf.Max(valueAdd, valueSub); return time; } public Vector3 GetLandingPos(float height = 0.0f) { var landingPos = Vector3.zero; var time = GetLandingTime(height); if (time < 0.0f) return landingPos; landingPos.y = height; landingPos.x = _firePos.x + _direction.x * _speed * time; landingPos.z = _firePos.z + _direction.z * _speed * time; return landingPos; } public static Vector3 GetFireDirection(Vector3 startPos, Vector3 endPos, float speed, bool useHighTrajectory = false) { var direction = Vector3.zero; var delta = endPos - startPos; var a = Vector3.Dot(Physics.gravity, Physics.gravity); var b = -4 * (Vector3.Dot(Physics.gravity, delta) + speed * speed); var c = 4 * Vector3.Dot(delta, delta); var discriminant = b * b - 4 * a * c; if (discriminant < 0) return direction; var sqrtDiscriminant = Mathf.Sqrt(discriminant); var time0 = Mathf.Sqrt((-b + sqrtDiscriminant) / (2 * a)); var time1 = Mathf.Sqrt((-b - sqrtDiscriminant) / (2 * a)); float time; switch (time0) { case < 0.0f when time1 < 0.0f: return direction; case < 0.0f: time = time1; break; default: { if (time1 < 0.0f) time = time0; else { time = useHighTrajectory ? Mathf.Max(time0, time1) : Mathf.Min(time0, time1); } break; } } direction = 2 * delta - Physics.gravity * (time * time); direction /= 2 * speed * time; return direction.normalized; } public static bool IsTargetReachable(Vector3 startPos, Vector3 endPos, float speed) { var delta = endPos - startPos; var a = Vector3.Dot(Physics.gravity, Physics.gravity); var b = -4 * (Vector3.Dot(Physics.gravity, delta) + speed * speed); var c = 4 * Vector3.Dot(delta, delta); return b * b - 4 * a * c >= 0; } public static float GetMinimumSpeed(Vector3 startPos, Vector3 endPos) { var delta = endPos - startPos; var gravityMagnitude = Physics.gravity.magnitude; var deltaMagnitude = delta.magnitude; var deltaY = delta.y; var minSpeedSqr = gravityMagnitude * (deltaY + Mathf.Sqrt(deltaY * deltaY + deltaMagnitude * deltaMagnitude)); return Mathf.Sqrt(minSpeedSqr); } } }