Aquí hay algo que preparé en unos 20 minutos. Tomamos la dirección del caminante hacia el objetivo, elegimos una dirección dentro de una cierta cantidad de grados de esa dirección (una cantidad que disminuye a medida que el caminante se acerca a su objetivo). Este algoritmo también tiene en cuenta la distancia al objetivo para que no pase por el objetivo. En pocas palabras, básicamente se mueve hacia la izquierda y hacia la derecha una pequeña cantidad aleatoria y se dirige hacia el objetivo a medida que se acerca.
Para probar este algoritmo puse el andador en (10, 0, 10) y el objetivo en (0, 0, 0). La primera vez que el algoritmo lo ejecutó, eligió al azar una posición para que caminara el caminante (3.73f, 0, 6.71f). Después de que el caminante alcanzó esa posición, eligió (2.11f, 0, 3.23), luego (0.96f, 0, 1.68f), luego (0.50f, 0, 0.79f), luego caminó directamente hacia el objetivo porque estaba dentro Una distancia mínima de tolerancia.
Graficado a vista de pájaro, el camino se vería como los puntos en la imagen a continuación, comenzando en 'W' (caminante) y terminando en 'T' (objetivo). Si desea un movimiento más natural, debe calcular previamente algunos puntos con anticipación y crear una spline, que le dará muchos más puntos que puede hacer que el caminante siga. He estimado cómo se vería este camino después de convertirlo en una spline, y eso está representado por la línea en la imagen.
Y aquí está el código de ejemplo:
Vector3 WalkerPosition = new Vector3(10, 0, 10);
Vector3 TargetPosition = Vector3.Zero;
public Game1()
{
// Each time you reach the next walk-to position, call this again.
// Eventually you'll reach your target, assuming the target isn't moving away
// from the walker faster than the walker can reach them.
Vector3 NextWalkToPosition = PickRandomTarget();
}
public Vector3 PickRandomTarget()
{
// For this code sample we'll assume that our two targets are on
// the same horizontal plane, for simplicity.
Vector3 directionToTarget = ( TargetPosition - WalkerPosition );
float distance = directionToTarget.Length();
directionToTarget.Normalize();
float distanceThisIteration = distance * 0.5f;
// We should never walk too little or too far, to make this more realistic
// you could randomize the walking distance each iteration a bit.
distanceThisIteration = MathHelper.Clamp(distanceThisIteration, 1.0f, 10.0f);
// We're within minimum distance to the target, so just go straight to them
if (distanceThisIteration > distance)
{
return TargetPosition;
}
directionToTarget *= distanceThisIteration; // Walk roughly halfway to the target
// Now we pick a new walking direction within an FOV that gets smaller as
// we get closer to the target. We clamp the FOV between 0 and 90 degrees (45 degrees in either direction).
const float walkerAggroRadius = 30.0f; // Walker aggros when within 30 units of target
// Any distance outside of 30 we'll just treat as 30.
float distanceMod = MathHelper.Clamp(distance, 0.0f, walkerAggroRadius);
// We need a percentage value representing the current distance between the min 0, and max, 30
float percentageAlongDistance = distanceMod / walkerAggroRadius;
// We want FOV from center, so we cut the final FOV result in half
float maxFOVAtThisDistance = MathHelper.Lerp(0.0f, MathHelper.PiOver2, percentageAlongDistance) * 0.5f;
// Now we pick a random FOV from center within our maxFOV based on how far we are
// from the target
Random rand = new Random(System.DateTime.Now.Second);
float randFOV = (float)(rand.NextDouble() * maxFOVAtThisDistance);
// Right now our FOV value is an FOV from a vector pointing directly at our target, we now
// need to randomly choose if we're going to aim to the left or right of the target. We'll
// treat a result of 0 as left, and 1 as right
int randDirection = rand.Next(2);
if (randDirection == 0) // Left
{
// Rotate our direction vector left by randFOV radians
return WalkerPosition + RotateAroundPoint(directionToTarget, Vector3.Zero, Vector3.UnitY, -randFOV);
}
else // Right
{
return WalkerPosition + RotateAroundPoint(directionToTarget, Vector3.Zero, Vector3.UnitY, randFOV);
}
}
// Generic helper function to rotate a vector by a specific amount of degrees
public Vector3 RotateAroundPoint( Vector3 point, Vector3 originPoint, Vector3 rotationAxis, float radiansToRotate )
{
Vector3 diffVect = point - originPoint;
Vector3 rotatedVect = Vector3.Transform(diffVect, Matrix.CreateFromAxisAngle(rotationAxis, radiansToRotate));
rotatedVect += originPoint;
return rotatedVect;
}
Según su juego específico, puede ajustar las distancias, el FOV, la aleatoriedad y la frecuencia con la que se ejecuta, hasta que se adapte a sus necesidades. Estoy seguro de que el algoritmo podría limpiarse un poco y optimizarse, no pasé mucho tiempo en eso, solo quería que fuera fácil de leer.