Intro

Abstract

The ProjectileVisualizer class simulates and visualizes projectile motion, line-of-sight, and intercept geometry using Raycast checks.

img

Visualized Elements

ElementEquationColor
Trajectory, Yellow
LOSBlue
Perpendicular to LOSWhite
Missed targetRed

Interpretation/ Debug Heuristics

From the visualizations the following becomes more intuitive:

  • LOS misses target but trajectory hits → Gravity compensation is working

  • LOS hits but trajectory misses low → Underestimating gravity or time

  • LOS too high → Aim direction is fundamentally wrong

  • Early green (collision) → Obstruction → don’t throw

  • Red segments after passing target → Overshoot → too much velocity or wrong timing

description

Calculations

1️⃣ Line of Sight (LOS) – DrawLineOfSight

Goal: Show the projectile’s pointing direction ignoring gravity.

Math:

Where:

  • = initial projectile position
  • = projectile velocity
  • = arbitrary length for visualization (e.g., 100 units)

Purpose:

  • Visualize aiming direction.
  • Draws a straight line ignoring gravity.

2️⃣ Trajectory Simulation – DrawTrajectory

Goal: Show the projectile’s actual path under gravity and detect collisions.

Time discretization:

Euler integration:

Where:

  • = position at step
  • = velocity at step
  • = gravity

Collision detection:

  • Each trajectory segment: check for obstacles using raycast along

Segment colors:

ConditionColorMeaning
Hit obstacleGreenCollision detected
Overshot targetRedMissed target
Normal flightYellowProjectile in air

3️⃣ Perpendicular to LOS – DrawLosToTarget

Goal: Draw shortest distance from target to projectile’s LOS.

Math:

  1. LOS direction:
  1. Vector from projectile to target:
  1. Projection length along LOS:
  1. Closest point on LOS:
  1. Perpendicular vector from LOS to target:

Purpose:

  • Draws a white line from the target to LOS.
  • Shows how high/low the LOS is relative to the target.

Overshoot condition

Where:


Code Implementation

ProjectileVisualizer.cs

using UnityEditor;
using UnityEngine;
 
public static class ProjectileVisualizer
{
    // Visualize where projectile is aiming as a straight blue line
    public static void DrawLineOfSight(Vector3 projectileInitPos, Vector3 projectileVelocity)
    {
        Vector3 direction = projectileVelocity.normalized;
        // Aim/ Line of Sight NO gravity
        Vector3 endPoint = projectileInitPos + 100f*direction;
        Debug.DrawLine(projectileInitPos, endPoint, Color.blue);
    }
    
    // Visualize simulation of projectile trajectory under gravity
    public static void DrawTrajectory(Vector3 projectileInitPos, Vector3 projectileVelocity, Vector3 projectileGravity, Vector3 targetPosition)
    {
 
        Vector3 vf = projectileVelocity;
        Vector3 currentPos = projectileInitPos;
        Vector3 nextPos;
        float t = 0.02f; // timestep
        int n = 50; // 50 * 0.02s timesteps = 1 sec
 
        for (int i = 0; i < 50; i++)
        {
            nextPos = currentPos + vf*t;
            // Get velocity at next timestep: v_f = v0 + a*t
            vf = vf + projectileGravity*t;
            
 
            // Do trajectory raycast
            Vector3 segment = nextPos - currentPos;
            RaycastHit hit;
            if (Physics.Raycast(currentPos, segment.normalized, out hit, segment.magnitude))
            {
                // Debug.DrawLine(currentPos, nextPos, Color.green, 0.1f);
                Debug.DrawLine(currentPos, hit.point, Color.green);
                Debug.Log("Obstacle detected: " + hit.collider.name);
                break;
            }
            // Check whether projection of projectile progress onto target direction > distance to target
            else if (Vector3.Dot((nextPos - projectileInitPos), (targetPosition - 
             projectileInitPos).normalized )  > (targetPosition - projectileInitPos).magnitude)
            {
                Debug.DrawLine(currentPos, nextPos, Color.red, 0.1f); 
                Debug.Log("Missed target!");
 
            }
            else
            {
                Debug.DrawLine(currentPos, nextPos, Color.yellow, 0.1f); // Draw small segment of trajectory
            }
            
            currentPos = nextPos;
        }
    }
 
    // Draw a white line from the LOS to the target
    public static void DrawLosToTarget(Vector3 projectileInitPos ,Vector3 projectileVelocity, Vector3 targetPosition, float interceptT)
    {
        Vector3 direction = projectileVelocity.normalized;
        Vector3 ProjectileToTarget  = targetPosition - projectileInitPos;
        float projectionLength = Vector3.Dot(ProjectileToTarget, direction); // ProjectileToTarget projected onto LOS
        Vector3 endPoint = projectileInitPos + projectionLength*direction;
        // Draw perpendicular line
        Debug.DrawLine(targetPosition, endPoint, Color.white, interceptT*0.01f);
    }
}