- Home /
Need player to walk in random direction
I'm currently working on a 2D project where I need the player to walk in a random direction (up, down, left or right). Whenever you press space, the direction is chosen at random, and the character then moves 10 units in that direction. I've been trying for several hours and with different solutions, but I just can't seem to wrap my head around how to make this work. I would really appreciate any help I can get!
Answer by marcozakaria · Nov 26, 2019 at 10:24 AM
Random.value will get you a random number between 0 and 1 , then you can use this vector and multiple it by 10 then move character to new position
Vector2 randomDirection = new Vector2(Random.value, Random.value);
Answer by orionsyndrome · Feb 11, 2020 at 01:33 AM
You can think of directions in terms of unit "velocity".
Now bear with me. If I'm moving at 10 km/h, obviously my velocity is 10, and it appears that when I change my direction, my velocity doesn't change or reflect that. By that's a scalar representation of velocity, effectively just a numeric magnitude of it.
If you want to physically implement object velocity, you need to start thinking about it as a measure of positional change.
Now if my whole world is a number line, going at 10 km/h doesn't only capture how quickly my position is changing each hour, but also that I'm moving to the right. Why? Because if we start at the -50 km mark, we'll move to -40, -30, -20, each hour by +10 kms, and soon enough we'll start moving on the positive side of the number line.
Likewise, if you want to move to the left, you need to go at -10 km/h. (Here I'm just assuming that negative numbers go to the left and positive to the right, by common convention.)
Now, what's up with the "unit velocity", you might ask. Well, when we're talking about the direction we don't care that much about the magnitude of the velocity, we just care about the sign. A unit velocity is basically what you need to multiply your colloquial speed factor with, in order to get a working measure of positional change.
If you want to move to the left, your unit velocity is -1, and conversely, +1 for the right direction.
Now, because your problem is in two dimensions, you need two coordinates for your position, and that means that your direction also need two signs.
If you're moving to the top-right, your unit velocity is [+1, +1], and if you're moving to the bottom-left, it's [-1, -1]. Additionally, if you want to move along cardinal axes, you simply disregard a speed factor that would move you away from it. For example, moving to the right changes the X value, but not the Y, so the unit velocity is [+1, 0].
Now you can simply multiply these coordinates with any speed factor to get a proper directional velocity: 10 * [+1, 0] will get you [10, 0] and 25 * [-1, -1] will get you [-25, -25]
No let's consider also that I have simplified this greatly and that there is a problem with these 2D unit velocities -- namely, they aren't completely unit. Unit velocities need to be normalized to be called that, because what they represent should always have a magnitude of exactly 1.
Think of a square 1 m in width and height. If your unit velocity can be [-1, 0] that means you can draw an arrow starting from the center of this square, and pointing to the left. It has a magnitude of exactly 1. But if you have a velocity of [-1, -1], this arrow will point to the lower left corner of the square, and because it's a square diagonal its magnitude is slightly bigger (square-root-of-two bigger, or around +41.5%). In layman terms, you'd move ~41.5% faster diagonally even though your speed factor hasn't changed.
Thus, a unit velocity is always normalized. It's magnitude has to end up being exactly 1. Once you have this, you can multiply it with your speed factor, and you're ready to go. You can represent a 2D direction with a Vector2.
To come up with a random direction, you can simply randomize the two signs, or pick a random angle. Because Random.value returns a number between 0 and 1, you can multiply it by 2 and reduce by 1, to get it in the range -1 and +1. To snap the values to exactly -1 or +1, you can use Mathf.Sign function.
float minusOrPlusOne() {
return Mathf.Sign(Random.value * 2f - 1f);
}
float speedFactor = 10f;
// if we use the normalized property, the resulting vector will be proportionally scaled
// so that its magnitude is exactly 1, solving the requirement of what means to be a 'unit'
Vector2 direction = new Vector2(minusOrPlusOne(), minusOrPlusOne()).normalized;
// now it's just the matter of scaling up this vector by our scalar speed factor
Vector2 finalVelocity = speedFactor * direction;
// finally, we can apply this velocity to the object's world position
// obviously, this needs to go in the Update method, because it should change every frame
Vector2 newPosition = someObject.transform.position + finalVelocity * Time.deltaTime;
someObject.transform.position = newPosition;
This is simple enough, however, this direction vector will also contain diagonals, which is undesirable.
If we remove the diagonals, there are only 4 remaining cardinal directions. We could get a random integer, and get a hardcoded direction that corresponds to this value.
int choice = Random.Range(0, 4); // means we'll get 0, 1, 2, or 3 (4 is exclusive)
float speedFactor = 10f;
Vector2 direction = Vector2.zero;
// here our directions are already normalized by hand, and thus 'unit'
switch(choice) {
case 1: direction = new Vector2(0f, 1f); break;
case 2: direction = new Vector2(-1f, 0f); break;
case 3: direction = new Vector2(0f, -1f); break;
// switch clause needs a default case anyway, so let's make it our case 0
default: direction = new Vector2(1f, 0f); break;
}
Vector2 finalVelocity = speedFactor * direction;
Here's also a generalized version of obtaining a completely random direction, for the reference
Vector2 CalculateUnitDirection(float angleInDegrees) {
// we need this conversion for the sine and cosine below to work properly
float angleInRadians = angleInDegrees * Mathf.Deg2Rad;
Vector2 direction = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians));
return direction.normalized;
}
float angle = Random.Range(0f, 360f); // this variant of Random.Range has inclusive maximum
float speedFactor = 10f;
Vector2 direction = CalculateUnitDirection(angle);
Vector2 finalVelocity = speedFactor * direction;
Alternatively, we could also constrain this general form to get only cardinal directions
float angle = Random.Range(0, 4) * 90f; // do you see what we did here?
float speedFactor = 10f;
Vector2 direction = CalculateUnitDirection(angle);
Vector2 finalVelocity = speedFactor * direction;
I hope this helps you or anyone else starting out with these basic concepts.
to understand more about the unit vectors and why I had to use trigonometry (and the subsequent relationship between the sine, the cosine, and the angle of direction), and how it all relates to Cartesian coordinates, google up 'unit circle'