Get random vector in the general direction of another?

Viewed 192

I'm trying to create a wander to target behavior for my enemies so that they wander, but with a purpose. After some research and effort, I determined that I would need to accomplish three things to get it to work:

  • Create a basic wander behavior.
  • Determine general direction to target.
  • Generate random destination in general direction of target.

Now, the first step is complete for sure. I believe the second step is complete, but let me explain what I'm doing, as it may need adjustments to achieve the desired result. Currently, I calculate the dot product between the target's location and a directional vector (i.e. left, right, forward, backward), which ever calculation results in the highest dot product determines the general direction:

private readonly Vector3[] _compass = { Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
private Vector3 GetDirectionOfTarget() {
    var result = Vector3.zero;
    var maximumDotProduct = _negativeInfinity;
    foreach (var direction in _compass) {
        var dotProduct = Vector3.Dot(Target.transform.position, direction);
        if (dotProduct > maximumDotProduct)
        {
            result = direction;
            maximumDotProduct = dotProduct;
        }
    }
    return result;
}

After calculating the general direction, I then get a random point within a unit sphere, multiply that vector by a user specified scalar value for range, and then add that to the general direction. However, after breaking that down mentally, it doesn't sound correct, because I'm going to get a random vector, then scale it out, and finally add a normalized direction to it, which just increases the range in the added direction. My code to get the new destination by applying this is:

var directionOfTarget = GetDirectionOfTarget();
var randomOffset = directionOfTarget + UnityEngine.Random.insideUnitSphere * MaximumRadiusFromSpawnPoint;
NavMesh.SamplePosition(transform.position + randomOffset, out NavMeshHit navHit, MaximumRadiusFromSpawnPoint, SampleNavigationArea);

One thought I have is to get the general direction, then manually create a random vector using that direction:

var directionOfTarget = GetDirectionOfTarget();

// This only works because directionOfTarget is normalized.
var x = _random.Next(0, Math.Abs(directionOfTarget.x)) * directionOfTarget.x;
var y = // same as x for y.
var z = // same as x for z.
var randomInDirection = new Vector3(x, y z);

However, this isn't it either because this will create movement in a box pattern instead of the more realistic approach of including movement along the x and z axes together.


Update 1: I managed to create a more accurate general direction through brute force:

for (float x = -1.0f; x < 1.0f; x += 0.1f) {
    for (float z = -1.0f; z < 1.0f; z += 0.1f) {
        var direction = new Vector3(x, 0, z);
        var dotProduct = Vector3.Dot(Target.transform.position, direction);
        if (dotProduct > maximumDotProduct) {
            result = direction;
            maximumDotProduct = dotProduct;
        }
    }
}

However, even though this produces a better result, it's expensive, and still doesn't focus the wander in the direction of the target (i.e. there's still back tracking going on).


Update 2: After some more tinkering, I removed the brute force attempt and updated my compass to include bidirectional vectors. This significantly reduced the cost of calculating the general direction of the target. Adding to this change, I also ran with the idea of generating a random vector based on the general direction and though it's not a graceful solution in my opinion, it works:

private readonly Vector3[] _compass = {
    new Vector3(-1, 0, -1), new Vector3(-1, 0, 0), new Vector3(-1, 0, 1),
    new Vector3(0, 0, -1), new Vector3(0, 0, 1),
    new Vector3(1, 0, 0), new Vector3(1, 0, 1)
};

var directionOfTarget = GetDirectionOfTarget();
var x = UnityEngine.Random.Range(0, Math.Abs(directionOfTarget.x)) * directionOfTarget.x;
var z = UnityEngine.Random.Range(0, Math.Abs(directionOfTarget.z)) * directionOfTarget.z;
var randomOffset = new Vector3(x, 0, z) * AreaSize;
NavMesh.SamplePosition(transform.position + randomOffset, out NavMeshHit navHit, AreaSize, SampleNavigationArea);

Note that I simply reverted back to the original state described above for the GetDirectionOfTarget method.


How do I get a random vector in the general direction of another?

Note: If there's anything I can do to clarify my question, or otherwise improve it for future readers, please leave a comment and let me know. I will gladly adjust my question as needed.

0 Answers
Related