Trajectory Line in Unity (This ain't your mothers trajectory line)
Trajectory prediction lines are everywhere in puzzle games and you even see them in action games to simulate grenade throw arcs etc.
This is a new method recently made possible with the addition of Multi-Scene Physics. We now have the ability to simulate frames into the future using the in-built physics system, taking into account the physics material of each object and even external forces like 2D force effectors... Making this significantly more powerful than your mothers trajectory line.
Scripts and assets: https://bit.ly/3C7tG4G
MORE TUTORIALS: https://www.youtube.com/tarodev
let's make a trajectory line which calculates rebounds and even takes the bounciness and friction level of your object's materials into account let's get into it so first let's start a new script let's call this projection okay open it up and the first thing we're going to do is create a new scene to perform our simulations on uh which will be overlaid on the top of our ones so let's create a new method here called uh creates we'll call it physics scene and then in here we will create the same so uh simulation scene let's call it will be equal to and we need scene manager here so unity engine scene management will be able to scene manager dot create saying and let's call this just simulation it's just the name of the scene and it really doesn't matter and for the parameters we will do new create scene parameters and we'll use uh 3d um actually we will make this a private variable as we'll be needing it and then we'll also make another private variable called um physics scene and this will be equal to simulation scene.get physics scene so the top one here is the actual saying and then this one is specifically the physics of that same okay next step is to actually put objects in the simulation scene so for example i've got these objects here which all have colliders on them and they're all children of this obstacles uh object here so i'm just going to loop through these children and then add them into this new scene so let's create a serialized field here so we can see this in inspector private transform and this will be the obstacle's parent okay now let's loop through these objects so this will be a an object and this will be oops i'm looping over my obstacle parents and i'll type transform cool okay so now we're looking through all the children and i'm going to spawn a new one so let's call this a ghost dodge and we'll instantiate it and the prefab for this will just be the object uh this child's game object just grabbing the object the object and just spawning them and then actually we need to spawn in the same location so we'll say obj dot transform dot position and then obj uh rotation it's already transformed so we don't need to access it like that and now we need to throw it over to the new scene so scene manager move object to scene and we'll send in the ghost objects and then the scene will be the simulation scene cool so now we are creating these scenes sorry the same grabbing the physics scene and then chucking all the objects that we want in that scene over to it um and also uh when we do this because both of the scenes are actually overlaid on top of each other we can actually see these objects that we're spawning in the other scene so what we can do here is ghost object get component and grab the renderer whether it's a mesh renderer or a skinned mesh renderer or whatever this is the base class so it should work enabled equals false so we're just turning that off cool so in our start function let's actually call this creat holy moly create physics scene okay so before we continue let's actually see if that's working so go across to our scene and on my canon i'm going to attach the projection script and it's asking for the obstacle's parents i'm just gonna put that there um yep press play and we'll say the simulation scene has been uh created and it's got all of our game objects okay so now that we've got our physics same let's actually run a simulation on it now i've got this ball script here uh and basically it's just you know it's doing some sound some uh particle effects when it hits something uh but most importantly it's got this init function here that as soon as we spawn we call this and we send in our velocity and we'll just uh launch the ball we'll grab our rigid body and add force to it so let's make a function here on our projection let's call this public void uh simulates trajectory it's a pretty fitting name and this will take in my ball ball prefab it will take in the position that we're wanting to start it so this will be the uh the barrel of my cannon for example and also the velocity that we're shooting it cool okay so the first thing we need to do is spawn the ghost object actually so we can just pretty much copy this and just send in the uh details so the position and it's a cannonball i don't really care about the rotation so quaternion rotation i'm gonna grab the renderer and disable it and then send in adjust the game object like that cool so now we've got our cannonball in the other script and this is actually very repetitive so uh probably best to put in a function uh for another time though so now that we've got it let's uh shoot our ball so uh as i said i've got a function on my ball a net that just adds the force so we can just say whoops ghost object dot init and send in our velocity okay so now that we've got this ghost cannonball and it's flying through the physics scene what we're going to do is shoot forward x amount of physics frames and on each one of those physics frames we're going to take note of where the ball was and then we can actually plot that on our line renderer so let's grab a few uh variables here so this will be a private line renderer and let's call this line and then another one called um this will be in an in sorry and this will be max physics frame iterations pretty self-explanatory name so then now let's set our line um position count and this will be equal to our max max iterations okay so now let's loop over for the amount of our physics frames that we're going to do and we're going to now use our physics same that we created a reference to earlier and we're going to say simulate now we want to simulate whatever we've got our max frame iteration set to we want to we want to simulate that many physics frames in the future so we can just say time dot fixed delta time so uh every single time this is called it's going one uh physics update in the future for example now we've done one physics frame update so now we can set our very first line position to wherever the ball is at this very moment so we can say line dot set position and it's going to be uh i for the uh position index and our ghost object.transform.position so let's say that this is defaulted to 100 for example so every single time that this is called we're going to predict 100 frames into the future and set our point there and then uh even though that this poor little ball only just arrived into the world we are going to destroy it straight away so it's not even going to to be honest we don't even need to do this because it's not even visible for a whole scene uh for a whole frame we're just going to delete it straight away um and i think that should be good so let's head back into unity and on our canon we now need a line so let's make a line renderer and let's reduce the thickness just a little and for the material i have actually got a line material here which will i i'll include in the uh in the description um i think that should be good let's just see yeah yep cool okay so our canon let's just remove the position counts um send that in set the iteration count to 100. okay so now we need to actually call this simulate trajectory because right now nothing's calling it so it's not doing anything so on my canon script so i haven't shown you this but i've got this hidden here because it's not it's out of scope basically it's just you know turning left and right up and down and pressing spacebar to shoot i will include it in the description but yeah out of scope of this tutorial let's call our projection so let's make a serialized field private projection grab a reference to it and then let's call it so projection.simulate trajectory and this takes in the prefab so down here i've got this prefab reference so let's send that in and where we want to actually start the uh simulation so on my canon i'll just show you i've got this uh barrel and spawn right here so that's where i want to spawn my ball so let's send in the bolt spawn start position and the force will be uh ball spawn all spawned up forward obviously we're wanting to um shoot the ball forward uh times our force which i've got here okay pretty simple stuff let's go back and try that out so press play and once it's saying object reference not set to an instance because we haven't set our trajectory so let's send that into the canon and press play and there we go aha so as you can see uh it's like there's some phantom cannon balls right now and hitting the ground we obviously don't want these to spawn so let's clean this up a little bit um on our ball let's actually send in boolean is ghost and if they are indeed a ghost is ghost equals is ghost just make that field there if is ghost damn it is ghost then let's just return we don't we don't want to spawn these effects here and so then in our ghost we'll say is ghost true but then in our canon we'll say is ghost false okay let's try that out press play and there we go so no more um pucks on the ground and the ball follows our projection line perfectly obviously until it hits something that's not in the simulation so these broken boxes are not in the simulation um but yeah so the good thing about this is that it actually takes into account the uh bounciness of the materials or the friction of the materials and puts that into the simulation whereas if you were doing this uh in just a matthew way which i have done in the past you would not well you could but it would be a significantly larger effort to try and allocate for all the different material types so that's one definitely good thing about this and to be honest it's pretty damn easy it's not it's not that much code pretty much just that and then calling it and you've got yourself a prediction line so i hope this came in handy i hope you had fun watching it and i'll see you next time bye okay so i've just had another thought what if you want to actually move your objects around in the scene uh if you're moving your main object here that does not mean that your physics scene object is moving so what you could do is uh when you create these objects uh let's say let's make this a private uh dictionary and this will be of type game objects and the key will be game object and the values will also be game object and this will be sponge spawned objects because new dictionary now what we're going to do here is actually update the physics object location every like whenever the uh the actual object moves so that could actually probably be pretty expensive so what i'm going to do is only grab the objects that are not marked static so let's go here if ghost object is static yeah that is not static sorry spawned objects add and here we're going to put in as the key the real object and then in here the ghost object cool and then i'm going to make an update function now we want to actually loop through these objects and update the values position according to the keys position so for each um it's going to be that collection and we're going to say um item dot value dot position no transform.position equals item dot key dot transform.position um and that's actually horrible so let's let's change this to transform transform transform transform transform just that and this one transform so now we don't have to say that every time uh dot position and then this will be the rotation just in case you're rotating it as well so now that should um update the locations and we could just simulate that pretty simply actually we need to make uh something static so let's actually make these all static and then let's just turn this one to non-static so now if we press play and we put that up there if we actually move this one now yep there we go but as all the other ones are static if we move this one uh we can't because it's static so that uh put a damper on that one so there we go um yeah just a little extra chip just in case