- Home /
Vector3 movement problems
I have a problem when moving a Vector3 and detecting when it hits a certain position. The problem with this example code
Vector3 myVector = new Vector3();
Vector3 targetVector = new Vector3(20, 0, 0);
void Update()
{
myVector += new Vector3(5, 0, 0) * Time.deltaTime;
if(myVector == targetVector)
{
//do something
}
}
is that the if-condition will only trigger sometimes, or almost never. I have tried this:
myVector = new Vector3(Mathf.Round(myVector.x * 10) / 10, Mathf.Round(myVector.y * 10) / 10, Mathf.Round(myVector.z * 10) / 10);
It works with a higher success rate, but still not as I wish.
Thanks in advance,
Answer by Dave-Carlile · Jun 18, 2015 at 09:28 PM
Since you're adding unpredictable floating point values to position vector, chances are it will never get exactly to the position you're comparing it to. A better approach is to see if it gets really close to that position. You can do that by comparing the distance between the two positions using Vector3.Distance.
if (Vector3.Distance(myVector, targetVector) < 0.01f)
{
// the positions are really close
}
Depending on the maximum possible speed, you may need to play around with the 0.01 value. Faster max speeds would require a larger threshold.
Another option (that someone else just answered) if you know a specific target position is to just move the object toward that position using some sort of lerp function. You know when it's done that it will be at that position - no comparisons necessary.
Answer by Doomatel · Jun 18, 2015 at 09:45 PM
That's not the best practice, it's much better to use something like path-definition an follow path scripts.
There is very clear and well explained video tutorial https://www.youtube.com/watch?v=ZkGPoZOQE5Q
The most valuable advantage of this method - You (or Your level designer) can do it easily in Unity editor without any scripting.
So I've add my scripts below. You can easily modify them and trigger events on reaching the key points of the path. Take look on MaxDistanceToGoal parameter in follow path script. The main algorithm to calculate distances between to points, and actually the answer to your question - is comparison of squared vector magnitudes:
float MaxDistanceToGoal = .1f;
...
var distanceSquared = (transform.position - _currentPoint.Current.position).magnitude;
if (distanceSquared < MaxDistanceToGoal * MaxDistanceToGoal) {
// do something here
YouHaveReachedTheTarget();
}
In addition to features that implemented in tutorial:
Construction of path manually in Unity editor - no hard code.
Following types: MovingTowards and Lerp.
Visual interpretation of paths in Unity editor.
i implemented some more logics, such as:
Two types of path: Circle and "Ping-pong".
Reversing scale on X-axis (we did a 2D-platformer, so it was enough for our purposes... nevertheless You can implement reversing for other axis).
Destroying moving objects (or their parents) on stop (it's suitable for example for static canon that is making shots in some restricted area).
Countdown counter for iterations (-1 endless iteration).
Maybe it's off-topic but I think that You should take a look deeply on this stuff.
Path definition script:
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
public class PathDefenition : MonoBehaviour {
public Transform[] Points;
public enum PathType {
PingPong,
Circle
}
public PathType Type = PathType.PingPong;
public IEnumerator<Transform> GetPathEnumerator()
{
if (Points == null || Points.Length < 1)
yield break;
var direction = 1;
var index = 0;
while (true) {
yield return Points[index];
if (Points.Length == 1)
continue;
if (Type == PathType.PingPong) {
if (index <= 0)
direction = 1;
else if (index >= Points.Length - 1)
direction = -1;
index = index + direction;
}
else if (Type == PathType.Circle)
if (index == Points.Length - 1)
index = 0;
else
index += 1;
}
}
public void OnDrawGizmos() {
if (Points == null || Points.Length < 2)
return;
for (var i = 1; i < Points.Length; i++)
Gizmos.DrawLine (Points [i - 1].position, Points [i].position);
if (Type == PathType.Circle)
Gizmos.DrawLine (Points [0].position, Points [Points.Length - 1].position);
}
}
Follow path script:
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
public class FollowPath : MonoBehaviour {
public enum FollowType {
MoveTowards,
Lerp
}
public FollowType Type = FollowType.MoveTowards;
public PathDefenition Path;
public float Speed = 1;
public float MaxDistanceToGoal = .1f;
public bool reversexScale;
public bool AlwaysFromStart = false;
public bool DestroyOnStop = false;
public bool DestroyParentOnStop = false;
public int Delay = 1;
public int Repeat = -1;
private bool stop = false;
private bool temp_stop = false;
private IEnumerator<Transform> _currentPoint;
private Vector3 old_position;
public bool IsStop() {
return stop;
}
public bool IsTempStop() {
return temp_stop;
}
void MoveNextPointHandler() {
_currentPoint.MoveNext ();
if (
(_currentPoint.Current.position.x - old_position.x > 0 && transform.localScale.x < 0 && reversexScale) ||
(_currentPoint.Current.position.x - old_position.x < 0 && transform.localScale.x > 0 && reversexScale)
) {
transform.localScale = new Vector3 (transform.localScale.x * -1, transform.localScale.y, transform.localScale.z);
}
old_position = _currentPoint.Current.position;
}
bool MoveNextPoint() {
if (Repeat > 0) {
Repeat -= 1;
}
if (Repeat >= 0) {
if (Repeat == 0) {
return false;
}
}
MoveNextPointHandler();
return true;
}
void Start () {
if (Path == null) {
Debug.LogError ("Path cannot be null", gameObject);
return;
}
_currentPoint = Path.GetPathEnumerator ();
_currentPoint.MoveNext ();
old_position = _currentPoint.Current.position;
if (_currentPoint.Current == null)
return;
transform.position = _currentPoint.Current.position;
MoveNextPointHandler ();
}
void Update () {
if (!stop) {
if (!temp_stop) {
if (_currentPoint == null || _currentPoint.Current == null)
return;
if (Type == FollowType.MoveTowards)
transform.position = Vector3.MoveTowards (transform.position, _currentPoint.Current.position, Time.deltaTime * Speed);
else if (Type == FollowType.Lerp)
transform.position = Vector3.Lerp (transform.position, _currentPoint.Current.position, Time.deltaTime * Speed);
var distanceSquared = (transform.position - _currentPoint.Current.position).magnitude;
if (distanceSquared < MaxDistanceToGoal * MaxDistanceToGoal) {
if (AlwaysFromStart) {
StartCoroutine (WaitForAnotherRunCoroutine ());
} else {
StartCoroutine (WaitForDelayCoroutine());
}
}
}
}
else {
if (transform.parent.gameObject && DestroyParentOnStop){
Destroy(transform.parent.gameObject);
} else if (DestroyOnStop){
Destroy(transform.gameObject);
}
}
}
void stopAnimation() {
if (gameObject.GetComponent<Animation>() != null) {
gameObject.GetComponent<Animation>().Stop();
}
}
IEnumerator WaitForAnotherRunCoroutine() {
temp_stop = true;
transform.GetComponent<Renderer>().enabled = false;
MoveNextPointHandler();
transform.position = _currentPoint.Current.position;
stop = !MoveNextPoint ();
if (Delay > 0) {
stopAnimation();
yield return new WaitForSeconds(Delay);
stopAnimation();
}
transform.GetComponent<Renderer>().enabled = true;
temp_stop = false;
}
IEnumerator WaitForDelayCoroutine() {
temp_stop = true;
stop = !MoveNextPoint ();
if (Delay > 0) {
yield return new WaitForSeconds(Delay);
}
if (!stop) {
temp_stop = false;
}
yield break;
}
}
Hope that it will help. Have fun!
$$anonymous$$ight be better for OP to start off a little slower and learn about Vectors and such before jumping into the deep end.
As Dave Carlile said, I would prefer starting off with simple vector movements. Thanks for your detailed answer tho! I will definately use it as a reference later on when facing similar problems.
Answer by YoungDeveloper · Jun 18, 2015 at 09:14 PM
The problem is that you are checking if its the same, but it most likely never be the same, will go over and move infinitely.
You might want to check or lerp or movetowards.
public float speed = 5f;
private Vector3 target = new Vector3(20, 0, 0);
private void Update(){
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
Answer by chrismkhill · Jun 18, 2015 at 11:08 PM
Try putting this in your Update function
Debug.Log (myVector.x);
And seeing if it ever equals 20. My guess is that you can do
if (myVector.x >= targetVector.x)
{
//Reached point
}
What are you trying to do with this code?
-Chill
I use it for a snake-like movement algorithm. The first object can be controlled via up, down, left and right keys. $$anonymous$$y goal was that multiple GameObjects follow it and i tried to do that with a Vector3 that gets set at the turn position of the leading object so whenever a following object passes it, it turns in the appropriate direction. There could be an easier way to solve this but I am pretty new to unity and this was my first approach to the problem. So far i got along well with it, exept of the problem mentioned here.
Answer by tyxman · Jun 18, 2015 at 09:44 PM
Have you tried using Vector3.Distance. You set a float to the 2 Vector3s and in an if statement if (distance <= 0.5f) {Do Something}
or something like that. That might work better.
http://docs.unity3d.com/ScriptReference/Vector3.Distance.html
Your answer
Follow this Question
Related Questions
Pushing a ball in a direction -1 Answers
Vector3.distance is always returning 0 4 Answers
What is difference between local and global coord Vector3. 1 Answer
How to Clamp LookAt on the Y Axis 1 Answer