偏った保守的なランダムウォーク


13

とのどちらかとして保存されたスプライトがVelocityありPositionますVector2。各Updateサイクルで、速度が位置に追加されます。

スプライトに3番目のベクトルを与えたいと思いTargetます。新しいターゲットは、どの反復でも指定できます。スプライトが本質的にランダムウォークパターンで移動するようにしたいのですが、2つのパラメーターを公開する必要があります。

  1. 典型的なランダムウォークは、任意の距離までの距離を増加または減少させる可能性がありますTarget(さらに、接線方向の動きのわずかな可能性)。私はランダムウォークにバイアスをかけることができなければなりませんが、それでもランダムではありますが、スプライトが「決定する」方向がより近くなるようにする必要がありますTarget
  2. ランダムウォークは「スムーズ」である必要があります。スプライトの方向が急激に変わることはありません。これは、プレイヤーにとって「ちらつき」または「震え」のように見えるためです。平均化するとゆっくりと近づきながら、ランダムに移動しながら、このように、または徐々に変化します。

これを行うための良い、簡単な方法は何ですか?可能であれば、答えをVector2 RandomWalk(Vector2 target)メソッドとして提供します。

NextGaussian(mean, stdev)役立つ方法があれば、すでに利用可能なメソッドがあります。


フレームごとに方向を変える非常に小さなチャンスを与えますか?そして、あなたが望む方向に動いていない場合、そのチャンスを大幅に増やしますか?
BlueRaja-ダニーPflughoeft

それは素晴らしい解決策です。ただし、可能であれば、急激な、ぎくしゃくした方向の変更は避けたいと思います。
スーパーベスト

1
加速係数を追加するだけで、速度を変更する代わりに、加速度を変更し、それによって速度を変更します。これは、動きからジッタを削除する必要があり、あなたがスムーズに散歩得るまであなただけの加速のアプリケーションを微調整することができます
skeletalmonkey

回答:


4

私はかかるだろう「ワンダ」ステアリングの挙動を(ソースコードを見つけることができるここに)と乱数があなたのターゲットに向けて付勢されているような方法でそれを微調整します。


ええ、私はステアリング行動が進むべき道だと思います。Wander + Seekを実行し、シーク動作に低い重みを追加するだけです。
-krolth

6

スムーズなランダムウォークを取得するには、Catmull-Romスプラインを使用できます。この種のスプラインは一連のポイントを取り、各ポイントを通過する滑らかな曲線を生成します。そのため、スプライトのランダムなウェイポイントを生成して、それを通過させ、ウェイポイントを通るCatmull-Romスプラインに沿ってアニメーション化することができます。スプラインを機能させるには、前の2つと次の2つの合計4つのウェイポイントが必要です。スプライトがウェイポイントに到達したら、4つのセットのうち最も古いものを捨てて新しいものを生成し、スプラインに沿ってアニメーションを続けます。

最終的にターゲットに向かうことに関しては、1つのアイデアはランダムウォークの分布をターゲットに向かって相殺することです。たとえば、通常、スプライトの現在の位置を中心とするガウス分布を使用してランダムなウェイポイントを選択する場合、代わりにターゲットに向かって事前設定された距離だけガウスの中心をオフセットできます。オフセットの相対サイズとガウスの標準偏差により、動きの偏りが決まります。


私はこれについて考えました、そして、推薦は素晴らしいですが、私は偏見がプレーヤーの場所の方にあることを望みます。スプライン方式では、事前にパスの一部を生成する必要があるため、プレーヤーをフォローする能力に遅れが生じます。
スーパーベスト

@Superbest Bezierカーブを使用する場合、次の2つのポイントを生成するだけで済みます。また、プレイヤーが動き回るときに、2番目のポイントをプレイヤーに向かって移動させることができます。
ジョナサンディキンソン

2

これは私が約20分でホイップしたものです。歩行者から標的への方向を取り、その方向からある程度の範囲内の方向(歩行者が標的に近づくにつれて減少する量)を選びます。このアルゴリズムは、ターゲットまでの距離を考慮して、ターゲットを通り過ぎないようにします。簡単に言えば、基本的には左右に少しランダムに揺れ、ターゲットに近づくにつれてターゲットに戻ります。

このアルゴリズムをテストするために、ウォーカーを(10、0、10)に、ターゲットを(0、0、0)に配置しました。アルゴリズムを初めて実行したとき、歩行者が歩く位置をランダムに選択しました(3.73f、0、6.71f)。ウォーカーがその位置に到達した後、(2.11f、0、3.23)、(0.96f、0、1.68f)、(0.50f、0、0.79f)を選択し、ターゲット内にいるため、まっすぐにターゲットまで歩いた最小許容距離。

鳥瞰図からグラフ化すると、パスは下の画像のポイントのようになり、「W」(歩行者)から「T」(ターゲット)で終わります。より自然な動きが必要な場合は、事前にいくつかのポイントを事前に計算し、スプラインを作成して、ウォーカーに追従させることができるポイントをさらに多く与えます。私はこのパスがスプラインにされた後にどのように見えるかを推定しました、そして、それはイメージの線によって表されます。

ここに画像の説明を入力してください

次に、サンプルコードを示します。

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

特定のゲームに基づいて、距離、FOV、ランダム性、および実行する頻度を、ニーズに合うまで調整できます。アルゴリズムを少しクリーンアップして最適化できると確信しています。あまり時間をかけず、読みやすくしたかっただけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.