- Home /
How to get a game object to lerp as a triggered effect?
Trying to make an object move using lerp for movement and a bool for detecting when the player is close enough to trigger the effect. Preferably would like it to stay at the position once within the detection range but, a reverse lerp variant would also be welcome once leaving detection range. The anchor tagged object is another gameobject on the same initial position as the object i want to move. I have tagged the player and anchor.
public float smooth;
public float detectionRange = 2;
private bool closeEnough;
public float raiseAmount = 1;
void Update () {
Vector3 raise = new Vector3(0,raiseAmount,0);
Vector3 player = GameObject.FindGameObjectWithTag("Player").transform.position;
Vector3 anchor = GameObject.FindGameObjectWithTag("Anchor").transform.position;
closeEnough = false;
if( Vector3.Distance( player, transform.position) <= detectionRange ){
closeEnough = true;
}
else if(closeEnough == true){
transform.position = Vector3.Lerp(transform.position, raise, smooth * Time.deltaTime);
}
if( Vector3.Distance( anchor, transform.position) >= detectionRange ){
closeEnough = false;
}
}
}
Answer by Immanuel-Scholz · Nov 17, 2014 at 08:22 AM
Your main problem is, that Vector3.Lerp is lerping between two world coordinates, but your variable "raise" is a relative vector. This means you are not "lerping upwards the Y-axis" as you probably want to, but lerping towards the world position "0,y,0".
You can fix this, e.g. by using
. Edit: It is not. See in comments. Usetransform.position = transform.position + raise * smooth * Time.deltaTime
instead of the lerp (which is basically a "lerp with relative vector")
raise = transform.position + new Vector3(...)
` instead.
Some other problems with your code:
You are testing a 3D distance for the "detectionRange" from this object to the player. But then you move this object when the player is near. So unless you guarantee that the new position of this object is actually nearer to the player, it will be immediately moved out of detection range, jittering and stuttering every frame a bit. Easiest to fix this is to zero out the Y-axis before comparing the distance (to only calculate 2d distance). If you really need a full 3D distance, use some kind of "Schmidt-Trigger" which means you have a "detectionRange" when you enter the trigger and another "undetectionRange" slightly bigger that you test against for leaving the range.
Next, DO NOT USE ANY GameObject.Find* METHOD IN UPDATE() !!! It WILL kill your performance soon. Move them to Awake(). If you really need to find the player and anchor every frame (in case they might actually change a lot during the game - which I doubt. ;), then better have the player and anchor register/deregister themself in some of their scripts during their OnEnable/OnDisable.
Thank you for that detailed explanation, im still fresh at this so hear me out for this follow up question. How do you zero out the y axis? And will this allow the object to raise up a finite amount by raiseAmount.
How do you zero out the y axis?
To measure "only the XZ distance" of an vector, the easiest way to grasp the idea is: copy them, write 0 to both y coordinates and then calculate the distance (it might be a couple of nanoseconds faster to do the distance math directly, but I leave that for you as a... home lesson! :P)
Vector3 p1 = transform.position;
Vector3 p2 = player;
p1.y = 0;
p2.y = 0;
if (Vector3.Distance(p1, p2) <= detectionRange)
...
You can also move this code to an own function for easier later usage. I recommend an extension function to Vector3. Just create an class somewhere in your Asset folder:
static class Vector3Extensions
{
public static float Distance2D(this Vector3 p1, Vector3 p2)
{
p1.y = 0;
p2.y = 0;
return Vector3.Distance(p1, p2);
}
}
And then you can simple use it by if (transform.position.Distance2D(player) <= ...
will this allow the object to raise up a finite amount by raiseAmount
It should not affect that part of the code.
Thank you again for explaining clearly, i will keep working at it.
This is my code now, which compiles without error. However the game object still does not move within the game. Any recommendations welcome.
using UnityEngine; using System.Collections;
public class Detection : $$anonymous$$onoBehaviour {
public float smooth;
public float detectionRange = 2;
private bool closeEnough;
public float raiseAmount = 1;
public Vector3 player;
public Vector3 anchor;
void Awake ()
{
Vector3 player = GameObject.FindGameObjectWithTag("Player").transform.position;
Vector3 anchor = GameObject.FindGameObjectWithTag("Anchor").transform.position;
}
void Update ()
{
Vector3 raise = new Vector3(0,raiseAmount,0);
Vector3 p1 = player;
Vector3 a1 = anchor;
p1.y = 0;
a1.y = 0;
closeEnough = false;
if( Vector3.Distance( p1, transform.position) <= detectionRange )
{
closeEnough = true;
}
else if(closeEnough == true)
{
transform.position = transform.position + raise * smooth * Time.deltaTime;
}
if( Vector3.Distance( a1, transform.position) >= detectionRange )
{
closeEnough = false;
}
}
}
Two problems: First, you don't want to cache the player/anchor position at Awake. You want to cache the Player / Anchor
And second, you are storing them into local variables and ignore the class variable "player" and "anchor" (because you put an "Vector3" before the "player".
Try this:
public float smooth;
public float detectionRange = 2;
private bool closeEnough;
public float raiseAmount = 1;
private Transform player;
private Transform anchor;
void Awake ()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
anchor = GameObject.FindGameObjectWithTag("Anchor").transform;
}
...
Vector3 p1 = player.position;
Vector3 a1 = anchor.position;
...
Answer by Achillies904 · Nov 24, 2014 at 04:38 PM
regardless of all the other solutions that everybody pointed out, i think there is a small error in the if statements
if i'm not mistaken, by using the "else if" statement your basically canceling the entrance to that if statement. meaning, if the distance is smaller than the detectionRange than you switch closeEnough to true, but than as long as the distance will remain smaller than the detectionRange than the code inside the second if statement will not be performed.
try changing this:
if( Vector3.Distance( p1, transform.position) <= detectionRange )
{
closeEnough = true;
}
else if(closeEnough == true)
{
transform.position = transform.position + raise * smooth * Time.deltaTime;
}
to this:
if( Vector3.Distance( p1, transform.position) <= detectionRange )
{
closeEnough = true;
}
if(closeEnough == true)
{
transform.position = transform.position + raise * smooth * Time.deltaTime;
}
Answer by E_N · Nov 24, 2014 at 10:05 PM
With everyone's help it has accumulated to this. Anchor is now an individual GameObject so each object with this script can have their own anchor.
using UnityEngine; using System.Collections;
public class Detection : MonoBehaviour {
public float smooth;
public float detectionRange = 2;
private bool closeEnough;
public float raiseAmount = 1;
private Transform player;
private Transform anchor;
public GameObject anchorOBJ;
void Awake ()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
anchor = anchorOBJ.transform;
}
void Update ()
{
Vector3 raise = anchor.position + new Vector3(0,raiseAmount,0);
Vector3 p1 = player.position;
Vector3 a1 = anchor.position;
p1.y = 0;
a1.y = 0;
closeEnough = Vector3.Distance(p1, a1) <= detectionRange;
if (closeEnough)
transform.position = Vector3.Lerp(transform.position, raise, smooth * Time.deltaTime);
else
transform.position = Vector3.Lerp(transform.position, anchor.position, smooth * Time.deltaTime);
}
}