1
\$\begingroup\$

I have a 2D vector where the x and y values are randomly generated using a range between -1 and 1 respectively. I use this as the direction to move an object in which works great.

var direction = Vector2(Random.Range(-1f, 1f), Random.Range(-1f, 1f));

What I would like to do is after some period of time, randomly generate a new direction vector, but this time keep the same "relative" direction that it currently has. Probably within 45 degrees or so on either side of the current direction.

So I'd like to randomly generate a new direction vector within a range of 45 degrees on either side of it's current direction vector, and start moving in that new direction. Does that make sense?

I'm not quite sure how to go about this, but I'm sure this isn't too difficult if someone could help get me started. Thanks!

\$\endgroup\$
  • 3
    \$\begingroup\$ Note that choosing your direction by randomly assigning a value to x and y will introduce a bias: there are more ways to generate a vector along a diagonal than there are exactly along the x or y axis. You can use Random.insideUnitCircle (includes short/zero vectors) or take the cosine & sine of a random angle (ensures vector is length 1) if you want your initial direction to be uniformly distributed. \$\endgroup\$ – DMGregory Dec 7 '17 at 0:12
  • \$\begingroup\$ Thanks! I hadn't thought about those points... Good to know. I switched to using Random.insideUnitCircle. Appreciate it! \$\endgroup\$ – DRiFTy Dec 7 '17 at 0:22
  • \$\begingroup\$ @DRiFTy Also, while both methods are very fast in the context of a game in general, and optimizing this is pretty much guaranteed to be engaging in premature optimization (google it) for most, if not all projects you will ever work on. If, and only if, you ever work on a project that needs every bit of performance it can get, no matter how small, then doing the calculation "by hand", as per DMGregory's second method (using cosine & sine) avoids some overhead, and can be faster. --- The same is true for Quaternion in my answer: Doing the rotation matrices "manually" is probably faster. \$\endgroup\$ – XenoRo Dec 8 '17 at 1:38
4
\$\begingroup\$

Random direction relative to current direction:

Assuming you already have an initial direction vector prepared, as per DMGregory's comment on the OP:

  1. Generate a random float that will represent the angle to change the direction by. Since the angle should be constrained to a 90 degree arc "forward" of the initial direction, we restrain the range of the random value to be from -45 to 45:
    • var angle = Random.Range(-45f, 45f);
  2. Since you're working with 2D rather than 3D, the axis used for the rotation necessary for the new angle is already known: Z. Because of this, a cross-product calculation is unnecessary.
  3. Unity already implements rotation matrices, in the form of the Quaternion class. Since the rotation is done through the Z axis, we can generate the quaternion for the rotation using the Euler() static method.
    • var quaternion = Quaternion.Euler(0, 0, angle);
  4. Finally, we multiply the quaternion by the original direction vector, to get a new, rotated vector:
    • var newDirection = quaternion * direction;
    • Important note: To rotate a vector by a quaternion, it's important that the quaternion always comes first in the multiplication.
  5. Now that each step of the operation is clear, we can simplify it by making it inline:
    • direction = Quaternion.Euler(0, 0, Random.Range(-45f, 45f)) * direction;

Extra:

If you intend to "stack" these operations on top of each-other, it's important to note that the constraint will be relative to the current direction in the current step, and so, overall, the 90 degree cone might not be respected; in fact it's hypothetically possible to get a 90 degree turn (180 cone) with two operations, a full "U-turn" with 4, and a circle with 8.

To get consecutive operations to always generate new angles within the initial 90 degree cone, you need to make the constraint be determined relative to the current step's angle with respect to an "ideal" direction (the initial direction, in this case):

var point = Vector2.zero;
var idealDirection = Vector2.up;
var direction = idealDirection;
for(var i = 0; i < 10; i++){
    var constraint = Vector2.SignedAngle(direction, idealDirection);
    direction = Quaternion.Euler(0, 0, Random.Range(-45f + constraint, 45f +
 constraint)) * direction;
    var newPoint = point + direction;
    Debug.DrawLine(point, newPoint, Color.yellow, 1f);
    point = newPoint;
}
//Done this in cellphone, from memory, but hopefully creates something resembling a directional "lighting bolt".
//It should work, but if I made any mistakes due to lacking VS's intellisense, do tell!
//And regardless of that, the most important: Have fun!
\$\endgroup\$
  • 1
    \$\begingroup\$ @DRiFTy "That worked perfectly. Thanks! Feel free to post that, and I can mark it as the answer." - Just did. Included an extra as a related tip, just #ForFun. Now give me cookie! =P \$\endgroup\$ – XenoRo Dec 8 '17 at 1:23
0
\$\begingroup\$

I've done this, and its not too hard.

  1. Firstly, generate a random non 0 length vector. Normalise it.
  2. Cross product with your current direction vector to create a rotation axis.
  3. Generate a random value in your -45 to 45 degree range (radians usually I do!)
  4. Depending on your flavour, I usually generate a matrix with the angle and the rotation matrix.
  5. Multiply your direction vector with said matrix.

Im sure someone has a better method, but this is what I have done in past.

\$\endgroup\$
  • \$\begingroup\$ Ok, I think I'm getting hung up on #4. I'm not sure how to create that matrix. Do you have any further details on that? The rest of the steps make sense. Thanks! \$\endgroup\$ – DRiFTy Dec 7 '17 at 0:20
  • \$\begingroup\$ Matrix.RotationAxis(RotationAxis, m_instanceAngle) Assume this is Sharpdx ! Matrix3x3 class is probably the one you want. That creates a Matrix you can use to multiply your direction vector by \$\endgroup\$ – ErnieDingo Dec 7 '17 at 1:29
  • \$\begingroup\$ This works for 3d vectors, in your case your rotation vector is pretty simple, its 0,0,1 (as you are working in the xy plane). So no need for cross product at all. Known axis, just use the Matrix3x3. That way in future, you can reuse for your 3d projects also. \$\endgroup\$ – ErnieDingo Dec 7 '17 at 1:32
  • \$\begingroup\$ Hmm yeah I'm having trouble translating this to Unity... I don't use matrices too much, only Quaternions and Vectors so I'm not quite sure how to get this to work. I'll keep trying though. \$\endgroup\$ – DRiFTy Dec 7 '17 at 1:57
  • \$\begingroup\$ You will be fine, you know the premise, just the right code and done! \$\endgroup\$ – ErnieDingo Dec 7 '17 at 2:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.