Sesgo, caminata aleatoria conservadora

13

Tengo un sprite que tiene Velocityy Position, ya sea almacenado como Vector2. En cada Updateciclo, la velocidad se agrega a la posición.

Me gustaría dar el sprite de un tercio del vector, Target. Se pueden dar nuevos objetivos en cualquier iteración. Me gustaría que el sprite se mueva esencialmente en un patrón de caminata aleatorio, sin embargo, se deben exponer dos parámetros:

  1. Una caminata aleatoria típica es igualmente probable que aumente o disminuya la distancia a cualquier punto dado Target(más la pequeña posibilidad de movimiento tangencial). Debo poder sesgar mi caminata aleatoria de tal manera que, aunque todavía sea aleatoria, la dirección en la que el sprite "decide" debería ser más probable que la acerque Target.
  2. La caminata aleatoria debe ser "suave": el sprite no debe cambiar rápidamente de dirección, ya que esto parecerá "parpadeante" o "tembloroso" para el jugador. Debe girar gradualmente de una manera u otra, moviéndose al azar, mientras se acerca lentamente cuando se promedia.

¿Cuál es una manera buena y simple de hacer esto? Si es posible, dé la respuesta como Vector2 RandomWalk(Vector2 target)método.

Ya tengo un NextGaussian(mean, stdev)método disponible, si eso es útil.

Superbest
fuente
¿Le da una oportunidad muy pequeña de cambiar de dirección cada cuadro? ¿Y hacer que la oportunidad sea significativamente mayor si no se mueve en la dirección que quieres?
BlueRaja - Danny Pflughoeft
Esa es una buena solución. Sin embargo, preferiría evitar el cambio repentino y desigual de dirección si es posible.
Excelente
1
Simplemente agregue un factor de aceleración, de modo que en lugar de cambiar las velocidades, cambie la aceleración, que a su vez cambia la velocidad. Esto debería eliminar el jitter del movimiento, y solo puede ajustar la aplicación de la aceleración hasta obtener una caminata suave
skeletalmonkey

Respuestas:

4

Tomaría el comportamiento de dirección "errante" (el código fuente se puede encontrar aquí ) y lo modificaría de una manera para que los números aleatorios estén sesgados hacia su objetivo.

bummzack
fuente
Sí, creo que los comportamientos de dirección son el camino a seguir. Simplemente haga un Wander + Seek y agregue un peso bajo al comportamiento de búsqueda.
Krolth
6

Para obtener una caminata aleatoria suave, puede usar splines Catmull-Rom . Este tipo de spline toma una secuencia de puntos y genera curvas suaves que pasan a través de cada punto. Por lo tanto, podría generar puntos de ruta aleatorios para que el sprite se mueva y animarlo a lo largo de una spline Catmull-Rom a través de los puntos de ruta. Para que la spline funcione, necesitará un total de cuatro puntos de referencia: los dos anteriores y los dos siguientes. Cuando el sprite alcanza un punto de referencia, desecha el más antiguo de tu conjunto de cuatro y genera uno nuevo, luego continúa animando a lo largo de la spline.

En cuanto a eventualmente dirigirse hacia el objetivo, una idea sería compensar la distribución de la caminata aleatoria hacia el objetivo. Por ejemplo, si normalmente elige un waypoint aleatorio usando una distribución gaussiana centrada en la posición actual de su sprite, en su lugar, podría compensar el centro del gaussiano por una distancia predeterminada hacia el objetivo. Los tamaños relativos del desplazamiento y la desviación estándar de Gauss determinan qué tan sesgado es el movimiento.

Nathan Reed
fuente
He pensado en esto, y aunque la recomendación es buena, me gustaría que el sesgo sea hacia la ubicación del jugador. Como el método de spline requiere que genere parte del camino por adelantado, habrá un retraso en la capacidad de seguir al jugador.
Superbest
@Superbest si usa una curva de Bezier solo necesita generar los siguientes dos puntos, y podría hacer el segundo punto en el futuro hacia el jugador mientras se mueve.
Jonathan Dickinson
2

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.

ingrese la descripción de la imagen aquí

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.

Nic Foster
fuente