- Home /
Slerp, lerp or something else?
So I'm trying to achieve the same effect that launching plants achieve in Plants versus Zombies. Where an object targets a moving enemy...get's tossed in the air and eventually hits it's target.
The hieght of the arc is dictated by the distance the object needs to travel to get to it's target. At first I thought this was a slerp, but perhaps this is something else.
here's a video showcasing the type of dynamic arch I'm trying to achieve.
My limited experience of a slerp has the speed the object travels varied to have it hit the target at the desired time. In the video, the speed is constant, and the arc of the object is adjusted to insure that the object hit's the target at the desired time.
Answer by robertbu · Oct 14, 2013 at 04:12 PM
It wouldn't take much to simulate the throw with some simple physics calculations, but for what is done in the video, I think a simple Sin() function will do the job. It is not only that the object is flying in an arc, but that the movement up/down under the influence of gravity need to look right. Here is a sample script. I've calculated the height as half of the distance traveled. You can adjust this for the specific feel of your throws. In looking at the video, it seemed that objects had a speed rather than a time. That is an object thrown from a further distance took more time. It was hard for me to tell. I've implement the code with speed, but you can just set 'time' to a fixed value if you want the object to travel in a fixed amount of time no matter what the distance. To test:
Create a new scene
Create a sphere at the origin
Scale the sphere down (0.4, 0.4 0,4)
Attach the following script
While running the app, click the left mouse button
var speed = 4.0; private var lobbing = false;
function Lob(dest : Vector3) { lobbing = true; var start = transform.position; var dist = (dest - transform.position).magnitude; var height = 0.5 * dist; var timer = 0.0; var time = dist / speed; var fraction = 0.0;
do { fraction = timer / time; var pos = Vector3.Lerp(start, dest, fraction); pos.y += Mathf.Sin(Mathf.PI * fraction) * height; transform.position = pos; timer += Time.deltaTime; yield; } while (fraction < 1.0); transform.position = dest; lobbing = false; } function Update () { if (!lobbing && Input.GetMouseButtonDown(0)) { var pos = Input.mousePosition; pos.z = 10; pos = Camera.main.ScreenToWorldPoint(pos); Lob(pos); } }
Hey Rob, thanks for looking into this!
I tried your code, and it's an interesting little trick, but the behaviour is different to what is happening in the video. In the video I count roughly 2 seconds from the time launched to impact. The height of the arch get's visibly taller the closer the target gets to the plant. The height of the arch is the variable, the time to impact is the constant. I'd like to say the speed is also constant, but it's hard to say.
Watching it again, I also noticed that the apex of the arc is roughly 2/3rds into the arc (favoring the target's side)
Did a quick sketch to show what it is I think I'm seeing here.
I also tried changing the variable variable "timer" to a fixed number like you suggested and the results had the spere snapping to the destination.
A bezier curve with the control point closer to the enemy any1? ;)
Your sketch is not working. It is 'time' you need to set to get a fixed time travel. So on line 10, just make 'time' equal some constant. As for the arc and where it is favored, you can skew the time used in the curve using another curve. Change line 16 to:
pos.y += $$anonymous$$athf.Sin($$anonymous$$athf.PI * $$anonymous$$athf.Pow(fraction, power)) * height;
Declare a 'power' variable in the top of the file and set it to 1.25 to start. Adjust it as you see fit.
I made the changes as you instructed and in this code I'm still seeing the arc change height the further the distance from origin to the place you clicked.
In the video the arc gets taller as the distance traveled gets shorter. I'll try to upload the pic again.
Both the red and blue travel time is roughly 2 seconds.
To accommodate that the zombie is closer to the plant, the plant tosses the projectile higher in the air, creating a taller arc allowing it to hit the target at the 2 second mark.
In the blue line, the zombie is further away so the arc is shorter, to allow the projectile to hit it's target again at the 2 second mark.
I appreciate all the help you've given me on this so far!
It is really hard for me to see what is going on in the video. Different plants are shooting and new zombies are not necessarily on the same 'y' value of the board. To me, it almost looked like the height was the same (i.e. just set to a constant). But let's say you want the different size arcs as you describe. Change the height calculation in line 8 to:
var height = 2.0 - heightFactor * dist;
$$anonymous$$ake heightFactor a variable and and set it 0.1. The 2.0 probably should be a variable as well (maxHeight), and set to 2.0 to start.
Answer by Calum-McManus · Oct 14, 2013 at 02:16 PM
I've done something similar with making a character walk to a way point but walking in a curve to get there.
First you set up a vector 3 to give a direction from the spawn of the projectile to the enemy;
Vector3 vDir = (enemy.transform.position - spawn.transform.position).normalized;
Then give the projectile speed;
float fMovementDelta = (speed * Time.deltaTime) * transform.root.localScale.x;
Now tell it to move;
projectile.transform.position += projectile.transform.forward * fMovementDelta;
Vector3 vLocalPosition = projectile.transform.localPosition;
vLocalPosition.y = 0;
projectile.transform.localPosition = vLocalPosition;
Now the part you want, tell it to always have one of its axis facing the enemy;
Quaternion qTo = Quaternion.LookRotation(vDir, transform.up);
Now tell it to curve using Quaternion.Lerp;
projectile.rotation = Quaterion.Lerp(projectile.transform, qTo, (speed*4)*Time.deltaTime);
Im sorry this is in C# but that's all i know in this depth, hopefully if you really cant use this someone can convert it for you. If there is any part of this you don't understand leave a comment and i will help as soon as i can :)
I like this as a possible solution. It sounds like allot of calculations, which has me scared if I wanna have many projectiles use this per frame. But perhaps their's just no avoiding this.
Can someone translate this to Java? I'm going to give it a shot, but I'm still learning Java, and I have zero experience with C#
Trying to translate this, this is what I have so far but it's coughing out a ton of errors so obviously it's way off...
var vDir :Vector3;
var speed :float = 5.0;
var enemy :Transform;
var spawn :Transform;
var f$$anonymous$$ovementDelata :float;
var qTo :Quaternion;
function Update ()
{
vDir = (enemy.transform.position - spawn.transform.position).normalized;
f$$anonymous$$ovementDelta = (speed * Time.deltaTime) * transform.root.localScale.x;
projectile.transform.position += projectile.transform.forward * f$$anonymous$$ovementDelta;
Vector3.vLocalPosition = projectile.transform.localPosition;
vLocalPosition.y = 0;
projectile.transform.localPosition = vLocalPosition;
qTo = Quaternion.LookRotation (vDir, transform.up);
projectile.rotation = Quaternion.Lerp(projectile.transform, qTo, (speed*4)*Time.deltaTime);
}
simples, there you go
using UnityEngine;
using System.Collections
public class myClass : $$anonymous$$onoBehaviour
{
public Vector3 vDir;
public float speed = 5.0f;
public Transform enemy;
public Transform spawn;
public float f$$anonymous$$ovementDelta;
private Quaternion qTo :;
void Update ()
{
vDir = (enemy.transform.position - spawn.transform.position).normalized;
f$$anonymous$$ovementDelta = (speed * Time.deltaTime) * transform.root.localScale.x;
projectile.transform.position += projectile.transform.forward * f$$anonymous$$ovementDelta;
Vector3.vLocalPosition = projectile.transform.localPosition;
vLocalPosition.y = 0;
projectile.transform.localPosition = vLocalPosition;
qTo = Quaternion.LookRotation (vDir, transform.up);
projectile.rotation = Quaternion.Lerp(projectile.transform, qTo, (speed*4)*Time.deltaTime);
}
}
Answer by sed · Oct 14, 2013 at 02:24 PM
Maybe you would be better off with moving your objects along a bezier curve?
http://answers.unity3d.com/questions/12689/moving-an-object-along-a-bezier-curve.html
http://en.wikipedia.org/wiki/B%C3%A9zier_curve
With one control point (position of which you can make slightly random and dependant upon the distance between shooter and the target) you can achieve a similar effect.
Your answer
Follow this Question
Related Questions
How to Lerp or Slerp Time.timeScale change 1 Answer
How to use Lerp? 3 Answers
Moving player in an arc, from startPoint to endPoint 2 Answers
Use Lerp Position and Slerp Rotation together 0 Answers
Object lerping at start 1 Answer