Camminata casuale di parte, conservatrice


13

Ho uno sprite che ha Velocitye Position, o memorizzato come Vector2. Ad ogni Updateciclo, la velocità viene aggiunta alla posizione.

Vorrei dare lo sprite un terzo vettore, Target. Nuovi obiettivi possono essere forniti a qualsiasi iterazione. Vorrei che lo sprite si muovesse essenzialmente in un modello di camminata casuale, tuttavia devono essere esposti due parametri:

  1. È altrettanto probabile che una tipica camminata casuale aumenti o riduca la distanza da qualsiasi dato Target(più la piccola possibilità di movimento tangenziale). Devo essere in grado di influenzare la mia camminata casuale in modo tale che, mentre è ancora casuale, la direzione su cui "lo sprite" decide "dovrebbe essere più probabile avvicinarla Target.
  2. La camminata casuale dovrebbe essere "regolare" - lo sprite non dovrebbe cambiare rapidamente direzione, poiché sembrerà "tremolio" o "tremore" al giocatore. Dovrebbe gradualmente virare in questo o quello modo, muovendosi casualmente, mentre si avvicina lentamente quando viene mediata.

Qual è un buon modo semplice per farlo? Se possibile, fornire la risposta come Vector2 RandomWalk(Vector2 target)metodo.

Ho già un NextGaussian(mean, stdev)metodo disponibile, se è utile.


Dagli una minima possibilità di cambiare direzione in ogni fotogramma? E rendere questa possibilità significativamente più grande se non si sta muovendo nella direzione desiderata?
BlueRaja - Danny Pflughoeft,

Questa è una bella soluzione. Tuttavia, preferirei evitare l'improvviso, brusco cambio di direzione, se possibile.
Superbo

1
Basta aggiungere un fattore di accelerazione, quindi invece di cambiare velocità, si cambia accelerazione, che a sua volta cambia velocità. Questo dovrebbe rimuovere il jitter dal movimento e puoi semplicemente modificare l'accelerazione fino a ottenere una camminata regolare
skeletalmonkey,

Risposte:


4

Vorrei prendere il comportamento di guida "errante" (il codice sorgente può essere trovato qui ) e modificarlo in modo tale che i numeri casuali siano distorti verso il tuo obiettivo.


Sì, penso che i comportamenti di guida siano la strada da percorrere. Basta fare un Wander + Seek e aggiungere un peso ridotto al comportamento di ricerca.
Krolth,

6

Per ottenere una camminata casuale regolare, è possibile utilizzare spline Catmull-Rom . Questo tipo di spline prende una sequenza di punti e genera curve morbide che attraversano ogni punto. Quindi, è possibile generare waypoint casuali per lo sprite per spostarsi e animarlo lungo una spline Catmull-Rom attraverso i waypoint. Per far funzionare la spline avrai bisogno di un totale di quattro waypoint: i due precedenti e i successivi due. Quando lo sprite raggiunge un waypoint, getta via il più vecchio del tuo set di quattro e generane uno nuovo, quindi continua l'animazione lungo la spline.

Per quanto riguarda infine la direzione verso il bersaglio, un'idea sarebbe quella di compensare la distribuzione della camminata casuale verso il bersaglio. Ad esempio, se di solito scegliessi un waypoint casuale usando una distribuzione gaussiana centrata sulla posizione corrente del tuo sprite, potresti invece compensare il centro del gaussiano di una distanza predefinita verso il bersaglio. Le dimensioni relative dell'offset e la deviazione standard del gaussiano determinerebbero quanto il movimento sia distorto.


Ci ho pensato, e mentre la raccomandazione è buona, vorrei che il pregiudizio fosse verso la posizione del giocatore. Poiché il metodo spline mi richiede di generare in anticipo parte del percorso, ci sarà un ritardo nella capacità di seguire il giocatore.
Superbo

@Superbest se usi una curva di Bezier devi solo generare i prossimi due punti - e potresti muovere il secondo punto nel futuro verso il giocatore mentre si muove.
Jonathan Dickinson,

2

Ecco qualcosa che ho montato in circa 20 minuti. Prendiamo la direzione dal deambulatore verso il bersaglio, scegliamo una direzione entro una certa quantità di gradi di quella direzione (una quantità che diminuisce man mano che il deambulatore si avvicina al bersaglio). Questo algoritmo rappresenta anche la distanza dal bersaglio in modo che non passi oltre il bersaglio. Per farla breve, fondamentalmente oscilla a sinistra e a destra una piccola quantità casuale e si insedia sul bersaglio mentre si avvicina.

Per testare questo algoritmo ho posizionato il walker su (10, 0, 10) e il target su (0, 0, 0). La prima volta che l'algoritmo è stato eseguito, ha scelto casualmente una posizione per la quale il walker deve camminare (3.73f, 0, 6.71f). Dopo che il camminatore ha raggiunto quella posizione ha scelto (2.11f, 0, 3.23), quindi (0.96f, 0, 1.68f), quindi (0.50f, 0, 0.79f), quindi ha camminato dritto verso il bersaglio perché era all'interno una distanza minima di tolleranza.

Rappresentato graficamente da una vista a volo d'uccello, il percorso sarebbe simile ai punti nell'immagine qui sotto, a partire da "W" (camminatore) e terminando a "T" (bersaglio). Se vuoi un movimento più naturale, dovresti precalcare alcuni punti in anticipo e creare una spline, dandoti molti più punti che puoi far seguire al camminatore. Ho stimato che aspetto avrebbe questo percorso dopo essere stato trasformato in una spline e che è rappresentato dalla linea nell'immagine.

inserisci qui la descrizione dell'immagine

Ed ecco il codice di esempio:

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;
}

In base al tuo gioco specifico puoi modificare le distanze, il FOV, la casualità e la frequenza con cui viene eseguito, fino a quando non soddisfa le tue esigenze. Sono sicuro che l'algoritmo potrebbe essere ripulito un po 'e ottimizzato, non ci ho passato molto tempo, volevo solo che fosse facile da leggere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.