Home > Enterprise >  Struggling to create a trajectory prediction in Unity with 2D physics simulation
Struggling to create a trajectory prediction in Unity with 2D physics simulation

Time:04-23

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 (for now). 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.

What am I doing wrong? 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

}

CodePudding user response:

You use aimPointScreenV3 but it appears to never be initialized/calculated.

CodePudding user response:

Found the answer. Just needed to add the velocity to the ball after moving it to the simulated scene.

SceneManager.MoveGameObjectToScene(ghostObj.gameObject, simulationScene);
ghostObj.GetComponent<Rigidbody2D>().velocity = ballVelocity;
  • Related