Marche aléatoire biaisée et conservatrice

13

J'ai un sprite qui a Velocityet Position, soit stocké en tant que Vector2. À chaque Updatecycle, la vitesse est ajoutée à la position.

Je voudrais donner l'image - objet un troisième vecteur, Target. De nouvelles cibles peuvent être données à n'importe quelle itération. Je voudrais que le sprite se déplace essentiellement selon un modèle de marche aléatoire, mais deux paramètres doivent être exposés:

  1. Une marche aléatoire typique est également susceptible d'augmenter ou de diminuer la distance jusqu'à un point donné Target(plus la petite chance de mouvement tangentiel). Je dois être capable de biaiser ma marche aléatoire de telle sorte que, tout en restant aléatoire, la direction dans laquelle le sprite "décide" devrait être plus susceptible de le rapprocher Target.
  2. La marche aléatoire doit être "douce" - le sprite ne doit pas changer rapidement de direction, car cela ressemblera à un "scintillement" ou "tremblement" pour le joueur. Il devrait progressivement virer de cette façon ou de cela, se déplaçant au hasard, tout en se rapprochant lentement lors de la moyenne.

Quelle est la bonne manière simple de procéder? Si possible, donnez la réponse comme Vector2 RandomWalk(Vector2 target)méthode.

J'ai déjà une NextGaussian(mean, stdev)méthode disponible, si cela est utile.

Superbest
la source
Lui donner une toute petite chance de changer de direction à chaque image? Et augmenter considérablement cette chance si elle ne se déplace pas dans la direction souhaitée?
BlueRaja - Danny Pflughoeft
C'est une bonne solution. Cependant, je préférerais éviter le changement de direction soudain et saccadé si possible.
Superbe
1
Ajoutez simplement un facteur d'accélération, donc au lieu de changer les vitesses, vous changez l'accélération, qui à son tour change la vitesse. Cela devrait éliminer la gigue du mouvement, et vous pouvez simplement modifier l'application de l'accélération jusqu'à ce que vous obteniez une marche en douceur
skeletalmonkey

Réponses:

4

Je prendrais le comportement de direction "vagabond" (le code source peut être trouvé ici ) et le modifier de manière à ce que les nombres aléatoires soient biaisés vers votre cible.

bummzack
la source
Oui, je pense que les comportements de pilotage sont la voie à suivre. Faites simplement un Wander + Seek et ajoutez un faible poids au comportement de recherche.
krolth
6

Pour obtenir une marche aléatoire en douceur, vous pouvez utiliser des splines Catmull-Rom . Ce type de spline prend une séquence de points et génère des courbes douces qui passent par chaque point. Ainsi, vous pouvez générer des waypoints aléatoires pour que l'image-objet se déplace et l'animer le long d'une spline Catmull-Rom à travers les waypoints. Pour que la spline fonctionne, vous aurez besoin d'un total de quatre points de cheminement: les deux précédents et les deux suivants. Lorsque le sprite atteint un waypoint, jetez le plus ancien de votre ensemble de quatre et générez-en un nouveau, puis continuez à animer le long de la spline.

Quant à éventuellement se diriger vers la cible, une idée serait de compenser la distribution de la marche aléatoire vers la cible. Par exemple, si vous choisissez habituellement un point de cheminement aléatoire en utilisant une distribution gaussienne centrée sur la position actuelle de votre sprite, vous pouvez plutôt décaler le centre du gaussien d'une certaine distance prédéfinie vers la cible. Les tailles relatives de l'offset et de l'écart-type gaussien détermineraient la polarisation du mouvement.

Nathan Reed
la source
J'y ai pensé, et bien que la recommandation soit agréable, j'aimerais que le biais soit orienté vers l'emplacement du joueur. Comme la méthode spline me demande de générer une partie du chemin à l'avance, il y aura un décalage dans la capacité à suivre le joueur.
Superbest
@Superbest si vous utilisez une courbe de Bézier, il vous suffit de générer les deux points suivants - et vous pouvez déplacer le deuxième point dans le futur vers le joueur alors qu'il se déplace.
Jonathan Dickinson
2

Voici quelque chose que j'ai fouetté en 20 minutes environ. Nous prenons la direction du marcheur vers la cible, choisissons une direction à un certain degré de cette direction (une quantité qui diminue à mesure que le marcheur se rapproche de sa cible). Cet algorithme tient également compte de la distance jusqu'à la cible afin qu'il ne dépasse pas la cible. Pour faire court, il oscille essentiellement à gauche et à droite une petite quantité aléatoire et se rapproche de la cible à mesure qu'elle se rapproche.

Pour tester cet algorithme, j'ai placé le déambulateur à (10, 0, 10) et la cible à (0, 0, 0). La première fois que l'algorithme a fonctionné, il a choisi au hasard une position vers laquelle le marcheur doit marcher (3.73f, 0, 6.71f). Une fois que le marcheur a atteint cette position, il a choisi (2.11f, 0, 3.23), puis (0.96f, 0, 1.68f), puis (0.50f, 0, 0.79f), puis il s'est dirigé directement vers la cible car il était à l'intérieur une distance de tolérance minimale.

Représenté graphiquement à vol d'oiseau, le chemin ressemblerait aux points de l'image ci-dessous, commençant à «W» (marcheur) et se terminant à «T» (cible). Si vous voulez un mouvement plus naturel, vous devez précalculer quelques points à l'avance et créer une spline, vous donnant beaucoup plus de points que le marcheur peut suivre. J'ai estimé à quoi ressemblerait ce chemin après avoir été transformé en spline, et cela est représenté par la ligne dans l'image.

entrez la description de l'image ici

Et voici l'exemple de code:

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

En fonction de votre jeu spécifique, vous pouvez modifier les distances, le champ de vision, le caractère aléatoire et la fréquence d'exécution, jusqu'à ce qu'il réponde à vos besoins. Je suis sûr que l'algorithme pourrait être un peu nettoyé et optimisé, je n'y ai pas passé beaucoup de temps, je voulais juste qu'il soit facile à lire.

Nic Foster
la source