- Home /
How to create a 2D parabolic trajectory prediction line with javascript?
I am trying to create the effect seen in this video:
Basically, it's a line that predicts where an object will be launched when using slingshot-style mechanics using a mouse. I have the slingshot script already working perfectly, which I have shown below.
I know that I can use LineRenderer to trace an objects path and I need an array that predicts several points along the trajectory. I have seen many questions similar to this one, but nothing exactly like what I am looking for. I am also very new to javascript and trying to learn so if you could explain each step as simply as possible I would greatly appreciate it.
Here's what I have for the slingshot mechanics:
var gravity : 20.0f;
//Applies gravity.
moveDirection.y -= gravity * Time.deltaTime;
//get mouse point in world space
var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pos.z = 0; //makes sure Z = 0
//drag squirrel while button pressed
if(drag)
{
squirrel.transform.position = pos;
}
if(Input.GetMouseButtonDown(0))
{
drag = true;
startPos = pos; //save initial position
}
if(Input.GetMouseButtonUp(0))
{
drag = false;
var dir = startPos - pos; //calculate direction and intensity
moveDirection = dir * launchForce; //launches with force input
isLoaded = false; //frees squirrel from launch zone clamps
squirrelActive = true;//allows squirrel to begin moving freely
}
I also have an example file that someone made for a 3D game using keyboard controls for aiming. This is like what I am looking for in 2D so I know it can be done and all the code is here. I am just unsure of what parts of this I need to apply to my script to make it work in 2D with mouse controls.
https://www.dropbox.com/s/b98xbl7s4kqrtb8/MyTester.rar
Thanks for any help in advance!
Yep, pretty much. $$anonymous$$ore like Angry Birds Space though since I don't think the original had a predictive trajectory line. It just showed a trail left behind by the previous bird. Peggle also using something similar to what I'm looking for.
hello robertu, sorry to open this thread after 2 years but just a question...how to put in a sprite ins$$anonymous$$d of the GameObject.CreatePrimitive(PrimitiveType.Sphere) and limiting the line arc in 2d world..thank you
Answer by robertbu · Sep 17, 2013 at 11:05 AM
Your gravity equation does not mimic a real falling object. Here is a bit of sample code that uses a Rigidbody and gives a path using a set of spheres. The script could be easily rewritten to do the gravity and velocity calculation in script rather than using a Rigidbody.
Create a sphere
Set the local scale to (0.3, 0.3, 0.3)
Add a rigidbody
Set the isKinematic flag to true
Attach the following script
When running, drag the sphere to show the line of objects
pragma strict
var force = 4.0; var samples = 15; var spacing = 0.1; // Time between samples
private var offset : Vector3; private var home : Vector3; private var rb : Rigidbody; private var argo : GameObject[];
function Start () { home = transform.position; rb = rigidbody; argo = new GameObject[samples]; for (var i = 0; i < argo.Length; i++) { var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); go.collider.enabled = false; go.transform.localScale = Vector3(0.2, 0.2, 0.2); argo[i] = go; } }
function ReturnHome() { transform.position = home; rb.velocity = Vector3.zero; rb.isKinematic = true; }
function OnMouseDown() { var v3 = Input.mousePosition; v3.z = transform.position.z - Camera.main.transform.position.z; v3 = Camera.main.ScreenToWorldPoint(v3); offset = transform.position - v3; }
function OnMouseDrag() { var v3 = Input.mousePosition; v3.z = transform.position.z - Camera.main.transform.position.z; v3 = Camera.main.ScreenToWorldPoint(v3); transform.position = v3 + offset; DisplayIndicators(); }
function OnMouseUp() { rb.isKinematic = false; rb.velocity = force * (home - transform.position); Invoke("ReturnHome", 2.0); }
function DisplayIndicators() { argo[0].transform.position = transform.position; var v3 = transform.position; var y = (force (home - transform.position)).y; var t = 0.0; v3.y = 0.0; for (var i = 1; i < argo.Length; i++) { v3 += force (home - transform.position) spacing; t += spacing; v3.y = y t + 0.5 Physics.gravity.y t * t + transform.position.y; argo[i].transform.position = v3; } }
Note you can find the equation used to project the vertical movement (gravity) in the answer to this question:
http://answers.unity3d.com/questions/467939/basic-questions-for-moon-landing-game.html
Also note that I'm marking the path with a series of spheres. If you are trying to reduce draw calls for a mobile application, consider purchasing Vectrosity to draw the line.
And in the video, there is movement on the 'z' axis when aiming. The code above should work, but you will need to figure out how to specify a 3D position for the aiming the object given only 2D mouse inputs.
Oh right. I should have mentioned that the object I'm launching is a characterController, because the player needs to be able to control it after it lands. That might make things in the physics department a bit tricky. But you said it could be done using script rather than rigidbody physics right?
And oh, I didn't even notice that they were moving along the z-axis in the video. I am just looking for the x and y axis. So thanks for pointing that out.
I'm a little confused by your script, but it's definitely close. I've never seen a lot of the lines you're using in there so I'm not really sure what most of that does. But I tried what you said though and created a sphere and applied that code to it. Unfortunately, when I ran the game, the sphere just dropped through the stage. If I take the rigidbody off (or turn off the gravity so I am able to click it) it works awesome! (But it does give me an error when I release the mouse.) Hopefully I can get the line to match up to the objects actual travel path since one is using physics and the other is using the script. I will have to play around with it a bit tonight and see if I can get it to work.
Sorry. Forgot to mention that you need to set the Is$$anonymous$$inematic flag to true on the Rigidbody attached to the sphere. Then you should be able to play with the code.
Here is a different version of the script that does not use a Rigidbody. This moves an object directly in the transform. For a character controller, you will like want to use CharacterController.$$anonymous$$ove() rather than directly manipulating the transform.
#pragma strict
var force = 4.0;
var samples = 15;
var spacing = 0.1; // Time between samples
private var offset : Vector3;
private var home : Vector3;
private var argo : GameObject[];
private var velocity = Vector3.zero;
private var freeze = true;
function Start () {
home = transform.position;
argo = new GameObject[samples];
for (var i = 0; i < argo.Length; i++) {
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.collider.enabled = false;
go.transform.localScale = Vector3(0.2, 0.2, 0.2);
argo[i] = go;
}
}
function FixedUpdate() {
if (!freeze) {
velocity.y += Physics.gravity.y * Time.fixedDeltaTime;
transform.position += velocity * Time.fixedDeltaTime;
}
}
function ReturnHome() {
transform.position = home;
velocity = Vector3.zero;
freeze = true;
ShowHideIndicators(true);
}
function ShowHideIndicators(show : boolean) {
for (var i = 0; i < argo.Length; i++) {
argo[i].renderer.enabled = show;
argo[i].transform.position = home;
}
}
function On$$anonymous$$ouseDown() {
var v3 = Input.mousePosition;
v3.z = transform.position.z - Camera.main.transform.position.z;
v3 = Camera.main.ScreenToWorldPoint(v3);
offset = transform.position - v3;
}
function On$$anonymous$$ouseDrag() {
var v3 = Input.mousePosition;
v3.z = transform.position.z - Camera.main.transform.position.z;
v3 = Camera.main.ScreenToWorldPoint(v3);
transform.position = v3 + offset;
DisplayIndicators();
}
function On$$anonymous$$ouseUp() {
Invoke("ReturnHome", 2.0);
velocity = force * (home - transform.position);
freeze = false;
ShowHideIndicators(false);
}
function DisplayIndicators() {
argo[0].transform.position = transform.position;
var v3 = transform.position;
var y = (force * (home - transform.position)).y;
var t = 0.0;
v3.y = 0.0;
for (var i = 1; i < argo.Length; i++) {
v3 += force * (home - transform.position) * spacing;
t += spacing;
v3.y = y * t + 0.5 * Physics.gravity.y * t * t + transform.position.y;
argo[i].transform.position = v3;
}
}
Thank you so much robertbu! I've been messing around with your script for the past couple days and have been figuring out how it works and it has helped me out tremendously. There are still a few things that I can't figure out how to do though.
This isn't a huge issue if I can't get it to work, but I can't figure out how to attach a LineRenderer to the spheres. Not super important since it works great the way it is, but might help it look nicer.
I can't figure out how to get the spheres to disappear after I launch the character. I tried using:
renderer.enabled = false;
but that makes the character disappear since the script is attached to the character. I see that the spheres are created via:
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
but I'm not sure how I can manipulate them. Ideally, I would want them to fade out after launching, but if I can just get them to disappear and reappear as I need them to that would work. However if I can attach the LineRenderer to them, this wouldn't even be an issue since I could just shrink them down to microscopic size in that case.
I edited the second body of code and added a ShowHideIndicator() function which is called on launch and when the sphere is returned to its home position. It just makes them appear/disappear, not fade. Fading is more complicated. There are many posts on fading if you want to make them fade. As for the LineRenderer, take a look at the code to show/hide the indicator. It walks the array and hides each object. For line rendering you would need to do a similar walk and extract the transform.position for each indicator to use for the positions in the LineRenderer.