Unity Trajectory Prediction (Simulation Method)
Trajectory prediction is a feature that is used so often in many different applications and games, and so regularly that one would think its a pretty simple process to implement. At least I did. When it came time to create the feature in a small mobile game I’m working on, I jumped on google and took a look at how it was done. The search results were, both daunting and confusing. Math. That’s what all of these answers had in common. More specifically trigonometry and physics. Two subjects that are not my strong suit. After looking at a few formulas that I couldn’t wrap my head around. I asked my good friend Dan Schatzeder to give the mathematical method a shot and started with a different approach: Simulation. By the end we both came up with solutions to the problem with using methods. This article will outline the process of creating the trajectory simulation system. Dan Schatzeder has an article detailing the mathematical method as well that will be linked at the end of this article. I highly suggest taking a look at both of them to weigh the pros and cons of both methods.
Trajectory Simulation:
Pros:
- Very Accurate
- Can include collisions and bounces
- Can predict very complex physics interaction fairly easily
Cons:
- Less performant than the math route.
How Does It Work?
Rather than using math to predict where the object will go, we will instead create a scene in the background that simulates the launch (much faster) and record the points that it traveled. Unity supports running multiple scenes at once, so we can manipulate the physics on the second scene at a different time step than our first scene. Meaning, we can figure out exactly where our object is going to go in about a frame. This sounds complicated but its actually fairly simple to set up.
I originally had the idea of launching a second object with the trail renderer, that didn't interact with the original object. The issue I was having was that it was moving at the regular launch speed, and didn’t look very good when it was updating. I needed it to move through its physics faster. So after some research I found the method from a video on the “TNTC” channel on YouTube. While the concept was there, the explanation was a little difficult to grasp so I did some research and found Unity’s Multi-Scene Physics tutorial. Between the two and some experimenting I was able to put together the following.
Let’s go through the process step by step. This will all take place on a script called Trajectory Manager.
Step 1: Declare Variables
Let’s start with declaring some and take a look at what we will use them for.
PhysicsScene2d _physicsSim — will be assigned to the new scenes physics. We will us this to manipulate the physics system.
GameObject _simulatedObject — will be the object we are simulating the launch with. This should be a duplicate of your launch object with the mesh render turned off.
LineRenderer line — will be used to plot out the points we later record.
Scene _simScene — will be the second scene we create.
int _steps — will be how many physics steps we run through in a frame. The more steps, the more data we collect.
Vector3[] points — will be what we use to record the points as the simulated object travels.
Step 2: Create a simulation scene
In order to do this you will need to be using the UnityEngine.SceneManagement namespace. Scenes automatically run physics at a fixed time step, but if you create a scene and assign its parameters at runtime it no longer adheres to the fixed time step, and we can simulate it on our own time. Let’s take a look at how to do this.
We start by creating new scene parameters in the first line. We then create a new scene, name it “Simulation” and assign the new parameter we just created. This will overwrite the physics in that scene with a new physics system. We then get a hold of this physics system and assign it to _PhysicsSim. Our new physics system, _physicsSim, will not advance unless we tell it with _physicsSim.simulate();. If you save and run you will see that a second scene is loaded into your scene.
We will go over the CreateSimObjects() in the next section, and lastly we fill the line position and points array with our steps variable.
Step 3: Send Objects to the Simulation Scene
We have a new scene, now the next obstacle is referencing objects across scenes. Luckily for us, unity has a function called move to scene, so we can grab reference in our first scene, move them to our second, and still access them. Let’s take a look at how we do this.
We’re first going to move our _simulatedObject over, since we already have reference to in we can now control it in the other scene. Next we’re going to send over any collidables we want the simulation to interact with. We simply check our scene for anything we tagged “collidable” and duplicate it by instantiation and send it over. Now we have an exact copy of our level on a separate scene and we are ready to simulate.
Step 4: Simulate the Launch
All thats left is to simulate the physics. We will create a method with two parameters, your original objects transform, and the force you want to enact on it.
First we set the simulated objects position and rotation to zero, as well as the velocity. My object is has a locked rotation, but you may want to set the angular velocity to zero as well in yours. Next we do a quick check to see if the force has changed since the last frame, this way we don’t have to be running constantly and can save a bit of performance. If it is in fact a new force, then we will add force to the simulated object, and with a for loop, we will step through the physics system the specified amount of times. When we simulate is is important you step through at Time.fixedDeltaTime, trying to skip steps results in sporadic behavior and is unreliable. This means we are getting the exact path of the launch in about a frame. Lastly we record its position, and feed it into the line renderer.
I used a singleton pattern for the script so its easily accessed, and call the SimulateLaunch function in the update loop in my balls script, and the prediction line appears. The accuracy is the main advantage of this method. Trying to calculate the amount of bounces you see above would take some serious math work, and theres still not guarantee you would get it correct due to deviations in unity physics from real life physics. So if you are looking for very precise prediction this is the method for you. The mathematical prediction is quite a bit lighter on performance. If you are just looking to quickly get the launch trajectory without bounces, then that will be a-lot more performant and get the job done for you. You can read more about the mathematical method in Dan Schatzeder’s medium post on the same topic.
At the end of the day both methods of prediction are viable depending on your application of the of the feature. Its always awesome to see the same feature implemented in such diffrent ways, and I wouldn't be surprised if there are even more ways out there to implement this mechanic.
Below is the full script for this trajectory manager, complete with instructions and explanation’s. While this will get you up and running quickly I would advise playing around a bit with using multiple physics scenes to really wrap your head around what is going on.
TrajectoryManager: https://gist.github.com/biggmackk17/63b95a6a565191f14b61094397a4ac74
Dan Schatzeder’s Article: https://schatzeder.medium.com/8537b52e1b34
Special Thanks to: TNTC’s video https://www.youtube.com/watch?v=4VUmhuhkELk for getting the ball rolling, and Unity’s tutorial https://learn.unity.com/tutorial/multi-scene-physics for getting the ball rolling.