- Home /
Vector2.MoveTowards giving me a hard time
First of all, it's a 2d "sidescroller" game, where the player is moving towards the right at a constant speed.
I've created an ability for the player to move 5 seconds back in time with the player object.
It seemed to work pretty fine a while ago as far as I can remember. But after I've implemented a bunch of other stuff and came back to test this ability, it's causing problems (sometimes).
Let me explain the timetravelling feature For each 0.5sec I am adding the current position to a list. And through that list I can move to an earlier position, later on in the game. I am moving the object with the Vector2.MoveTowards() feature
The problem is Sometimes it moves REALLY slowly, and sometimes too fast. But sometimes it also feels correct, hard to tell anymore.
The code is as follows: I know it's a lot to take in, but I've cut away most of what seems unnecessary tor this question. I would advise your to start reading from the IENumerator Move() since that is most likely where the problem is.
private PlayerController player;
private bool cooldown;
private List<Vector2> positionList = new List<Vector2>();
void Awake()
{
player = GameObject.Find("Stickman").GetComponent<PlayerController>();
}
void Start()
{
StartCoroutine(Cooldown());
StartCoroutine(Track());
}
public void Ability() // This begins the moving
{
if (!cooldown)
{
StartCoroutine(Move(positionList[positionList.Count - 10]));
positionList.Clear();
}
}
IEnumerator Track() // Adding the position to a list every 0.5sec
{
float timer = 0f;
positionList.Add(player.transform.position);
while (timer < 0.5f)
{
timer += Time.deltaTime;
yield return null;
}
StartCoroutine(Track());
}
IEnumerator Move(Vector2 target)//Here the actual moving happens
{
StartCoroutine(Cooldown());
bool reached = false;
float step = 10f * Time.deltaTime;
player.collider2D.enabled = false;
while (!reached && !player.isDead)
{
player.rigidbody2D.isKinematic = true; // This is needed
player.transform.position = Vector2.MoveTowards(player.transform.position,
target, step); //Here it moves
if (player.transform.position.x < target.x + 0.0001f) // That's good enough for me
{
reached = true;
player.collider2D.enabled = true;
player.rigidbody2D.isKinematic = false;
}
yield return null;
}
}
What is causing this problem here? Also feel free to point at any other stuff that seems "weird".
Thanks for reading!
Track could be InvokeRepeating, which is a bit more performant than Coroutine. Cooldown isn't shown so assu$$anonymous$$g there's nothing in there that might cause this. What's your Frames Per Sec - if it's bouncing around cuz of other stuff, the "step" will be affected.
Answer by Suddoha · Sep 05, 2015 at 03:44 AM
I'm not aware about the difference in speed, so I'll suggest to check two things for now:
Either you happen to get a "huge" difference in Time.deltaTime
in line 42:
float step = 10f * Time.deltaTime;
which is rather unlikely. Note, that this won't make your player move framerate-independantly as you calculate the step value once and use it for many frames. More to that later.
Or, what I suspect to be the reason and this is more likely, is that you probably do not disable the player's movement during the move-back-in-time-phase.
Off Topic due to request by OP
As you also asked for other things to be pointed out:
In order to move the player back in a framerate-independant manner, you should consider to calculate the value in each iteration again, just like for normal movements. As for that, remove the line above and change this line (48)
player.transform.position = Vector2.MoveTowards(player.transform.position, target, step);
to:
player.transform.position = Vector2.MoveTowards(player.transform.position, target, step*Time.deltaTime);
Next, your caching of the positions seems suboptimal if you only need the position that the player had 5 seconds ago (assuming you stick to the constant speed). You can calculate that on the fly (note, that this does not apply to the first 5 seconds):
cachedPosition.x = (player.transform.position.x - playersMoveSpeedPerSecond * seconds)
If you still want to cache the last 10 positions (e.g. speed changes so that the formula above does not apply), think about an array that you iterate over and once you reach the last element, start at the beginning again and overwrite the obsolete entries. You won't have a continuously growing cache then.
And one more thing: is your cooldown supposed to start right over or do you wait in the cooldown coroutine until the player moved back and continues to play?
$$anonymous$$any thanks for this answer.
I have tried setting the player's speed to 0f during the time jump, but it is still moving though, but with a 0f speed. The problem still occurs, don't know if disabling the movement tottaly will help.
But I think the problem might be that I only calculated step once. So doing it in the loop seems to have fixed the problem. I'll test it some more tomorrow, and then I'll come back to accept your answer.
In response to the caching. Yes there will be speed changes, jumping and stuff that will make it impossible for me to calculate the previous position.
But the other thing you're explaining, isn't that kind of what I'm already doing? I'm storing positions in a list, whenever the time-ability is used, I go 10steps back, and then I .clear() it.
And yes the cooldown is supposed to be triggered at once.
Again, thank you very much for this delicate answer!
If you are using this as an opportunity to learn some C#, take a look at the Queue collection ins$$anonymous$$d of the List. It's not discussed too often but it has its uses.
@GimLee Yes you're kind of doing that already, but your list grows larger as time continues until you clear it. I'm not sure what's your cooldown, but imagine someone waits several $$anonymous$$utes to use the ability: you'd currently store 120 positions per $$anonymous$$ute. Let your player just wait 3 $$anonymous$$utes and you'll already have 360 positions from which you only need the 350th.
Thus, you could have an array of 10 positions and once you filled the last element with the recent position, you'll start with the first one again as it's no longer needed for your time reset.
As for the cooldown i meant the following: Imagine you've got a cooldown of 30 seconds. In the beginning, you'll have a total cooldown of 30 seconds, once used the ability, you'll have 'cooldown - ability time' as cooldown as you start the cooldown routine immediately when you use the ability - effectively you'll only have 25 seconds because the player will be moved back in the first 5 seconds. $$anonymous$$aybe you've addressed this by waiting in the cooldown coroutine until the reset time is over, but i just wanted to point this out as a possible small mistake.
@Suddoha I don't know the "cost" of storing values, but I was wondering, wont it cost more performance to remove the entries in the list frequently, rather than leave them be.
Your answer
Follow this Question
Related Questions
Moving an UI element from point A to point B 1 Answer
Making a character move automatically in one direction. 2 Answers
MoveTowards is not working with RectTransform component 0 Answers
Push an object opposite direction of mouse position 0 Answers
How to know if 2D gameobject is moving left or right? 3 Answers