2D physics simulation to create trajectory prediction

I have a 2D brick breaker style game in Unity where you get to choose the angle of your shot before each turn with the mouse. I’d like to show an accurate trajectory prediction of what a ball will do while the player is aiming, up to maybe 200 frames of ball movement. This involves potentially bouncing off the top, left, and right walls, and any bricks.

I’m using a physics simulation and line renderer to accomplish this but can’t get it to work correctly.

It’s supposed to copy the gun, walls, and bricks into a virtual Scene, launch a ball in the angle the player is aiming, simulate 200 frames of movement, and plot those in a line renderer.

Here is an abridged version of the game code, reduced to just focus on the aiming and physics simulation for the 1st turn. When I run it, it simply plots the 200 points of the line renderer in the same coordinates as the gun.

Any help is appreciated, thank you.

using UnityEngine;
using UnityEngine.SceneManagement;

public class Aim3 : MonoBehaviour
{

    private Vector3 gunPosScreenV3, aimPointScreenV3,
        aimPointWorldV3, aimDirectionV3;

    private Vector2 ballDirection, ballVelocity;

    [SerializeField] private GameObject gun, walls, bricks;

    Camera cam;
    LineRenderer lr;

    // For Physics Scene
    private int maxPhysicsFrameIterations = 200;
    private Scene simulationScene;
    private PhysicsScene2D physicsScene;
    [SerializeField] private GameObject ballPrefab;
    private GameObject ghostObj;

    void Start()
    {
        lr = GetComponent<LineRenderer>();
        lr.enabled = true;
        cam = Camera.main;
        CreatePhysicsScene();
    }

    void Update()
    {
        DrawTrajectory();
    }

    // This is run once to create a duplicate of the game board for the physics simulation
    private void CreatePhysicsScene()
    {
        simulationScene = SceneManager.CreateScene("Simulation", new CreateSceneParameters(LocalPhysicsMode.Physics2D));
        physicsScene = simulationScene.GetPhysicsScene2D();

        // Copy the gun to the simulated scene
        ghostObj = Instantiate(gun.gameObject, gun.transform.position, gun.transform.rotation);
        ghostObj.GetComponent<SpriteRenderer>().enabled = false;
        SceneManager.MoveGameObjectToScene(ghostObj, simulationScene);

        // Copy all the bricks to the simulated scene. These are stored under a parent Game Object that
        // is attached in the editor
        foreach (Transform obj in bricks.transform)
        {
            ghostObj = Instantiate(obj.gameObject, obj.position, obj.rotation);
            ghostObj.GetComponent<SpriteRenderer>().enabled = false;
            SceneManager.MoveGameObjectToScene(ghostObj, simulationScene);
        }

        // Copy the top, left, and right walls to the simulated scene. These are invisible and don't have sprites.
        // These are stored under a parent Game Object that is attached in the editor
        foreach (Transform obj in walls.transform)
        {
            ghostObj = Instantiate(obj.gameObject, obj.position, obj.rotation);
            SceneManager.MoveGameObjectToScene(ghostObj, simulationScene);
        }
    } // CreatePhysicsScene

    private void DrawTrajectory()
    {
        // Get the starting Screen position of the gun from the World position
        gunPosScreenV3 = cam.WorldToScreenPoint(gun.transform.position);
        gunPosScreenV3.z = 1;

        // Get position of mouse in screen units
        aimPointScreenV3 = Input.mousePosition;

        // Get the position of the mouse in world units for aiming
        aimPointWorldV3 = cam.ScreenToWorldPoint(aimPointScreenV3);
        aimPointWorldV3.z = 1;

        // Calculate the normalized direction of the aim point compared to the gun
        aimDirectionV3 = (aimPointScreenV3 - gunPosScreenV3).normalized;

        Physics2D.simulationMode = SimulationMode2D.Script;

        // Instantiate the ball prefab for the simulation
        ghostObj = Instantiate(ballPrefab, gun.transform.position, Quaternion.identity);

        // Assign the ball's velocity
        ballDirection = new Vector2(aimDirectionV3.x, aimDirectionV3.y);
        ballVelocity = ballDirection * 20f;
        ghostObj.GetComponent<Rigidbody2D>().velocity = ballVelocity;
        SceneManager.MoveGameObjectToScene(ghostObj.gameObject, simulationScene);
       
        lr.positionCount = maxPhysicsFrameIterations;

        for (var i = 0; i < maxPhysicsFrameIterations; i++)
        {
            physicsScene.Simulate(Time.fixedDeltaTime);
            //Physics2D.Simulate(Time.fixedDeltaTime);
            lr.SetPosition(i, ghostObj.transform.position);
        }
       
        Destroy(ghostObj);
       
        Physics2D.simulationMode = SimulationMode2D.Update;

    } // DrawTrajectory

}

How about making prediction shoots with some kind of objects with the sama physics properties as the “real” bullets, that leaves a trail plotting the trajectory? But that is maybe what you already are trying to do when “using a physics simulation and line render”?

You said a lot of words and posted a lot of code but never actually said what was wrong! Apart from “it simply plots the 200 points of the line renderer in the same coordinates as the gun” but is that supposed to be the problem? I have no idea what that means. What are you expectations and what is it doing. For this, I believe an image would help because … what is the “gun” and what should it look like?

You should spend more time describing the problem and less so your solution. :slight_smile:

Oops :slight_smile:

I meant the physics simulation didn’t work. It’s supposed to launch the ball and simulate the physics of it. But it didn’t launch, the ball simply stayed in the same place.

Regardless, it’s now fixed. The velocity needed to be added to the ball after moving it to the simulated scene, not before.

1 Like

Yes, body velocity is a volatile property not part of the serialized state, same as angular velocity.

Glad you got it working.