using UnityEngine; public class BallShooter : MonoBehaviour { public GameObject ballPrefab; public Transform spawnPoint; public float arcHeight = 2f; void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); Vector3 target; if (Physics.Raycast(ray, out RaycastHit hit)) { target = hit.point; } else { target = ray.GetPoint(10f); // fallback } if (Vector3.Distance(spawnPoint.position, target) < 0.1f) return; // avoid NaN if click too close GameObject ball = Instantiate(ballPrefab, spawnPoint.position, Quaternion.identity); Rigidbody rb = ball.GetComponent(); rb.useGravity = true; Vector3 velocity = CalculateArcVelocity(spawnPoint.position, target, arcHeight); if (float.IsNaN(velocity.x) || float.IsNaN(velocity.y) || float.IsNaN(velocity.z)) { Destroy(ball); // safety: don't throw broken ball return; } rb.velocity = velocity; } } Vector3 CalculateArcVelocity(Vector3 start, Vector3 end, float height) { float gravity = Mathf.Abs(Physics.gravity.y); Vector3 displacementXZ = new Vector3(end.x - start.x, 0, end.z - start.z); float horizontalDist = displacementXZ.magnitude; if (horizontalDist < 0.01f) horizontalDist = 0.01f; // prevent divide-by-zero float heightDifference = end.y - start.y; height = Mathf.Max(height, heightDifference + 0.1f); // ensure height is enough float timeUp = Mathf.Sqrt(2 * height / gravity); float timeDown = Mathf.Sqrt(2 * (height - heightDifference) / gravity); float totalTime = timeUp + timeDown; Vector3 velocityY = Vector3.up * Mathf.Sqrt(2 * gravity * height); Vector3 velocityXZ = displacementXZ / totalTime; return velocityXZ + velocityY; } }