Bias, jalan acak konservatif

13

Saya memiliki sprite yang telah Velocitydan Position, disimpan sebagai Vector2. Pada setiap Updatesiklus, kecepatan ditambahkan ke posisi.

Saya ingin memberikan sprite vektor ketiga Target,. Target baru dapat diberikan pada setiap iterasi. Saya ingin sprite pada dasarnya bergerak dalam pola berjalan acak, namun dua parameter harus diekspos:

  1. Jalan acak tipikal memiliki kemungkinan yang sama untuk menambah atau mengurangi jarak ke setiap Targetgerakan yang diberikan (ditambah sedikit peluang gerakan tangensial). Saya harus bisa membiasakan jalan acak saya sehingga, sementara masih acak, arah sprite "memutuskan" harus lebih cenderung membawanya lebih dekat Target.
  2. Jalan acak harus "mulus" - sprite tidak boleh dengan cepat mengubah arah, karena ini akan terlihat seperti "berkedip" atau "gemetar" ke pemain. Seharusnya secara bertahap membelok ke arah ini atau itu, bergerak secara acak, sementara perlahan semakin dekat ketika rata-rata.

Apa cara yang baik dan sederhana untuk melakukan ini? Jika memungkinkan, berikan jawabannya sebagai Vector2 RandomWalk(Vector2 target)metode.

Saya sudah memiliki NextGaussian(mean, stdev)metode yang tersedia, jika itu membantu.

Hebat
sumber
Berikan kesempatan yang sangat kecil untuk mengubah arah setiap frame? Dan buat peluang itu jauh lebih besar jika tidak bergerak ke arah yang Anda inginkan?
BlueRaja - Danny Pflughoeft
Itu solusi yang bagus. Namun saya lebih suka menghindari perubahan arah yang tiba-tiba dan tersentak jika memungkinkan.
Biasa
1
Cukup tambahkan faktor akselerasi, jadi alih-alih mengubah kecepatan, Anda mengubah akselerasi, yang pada gilirannya mengubah kecepatan. Ini akan menghapus jitter dari gerakan, dan Anda bisa men-tweak aplikasi akselerasi sampai Anda mendapatkan jalan yang mulus
skeletalmonkey

Jawaban:

4

Saya akan mengambil kemudi-perilaku "berkeliaran" (kode sumber dapat ditemukan di sini ) dan men-tweak dengan cara sehingga angka acak bias terhadap target Anda.

bummzack
sumber
Ya, saya pikir perilaku menyetir adalah jalan yang harus ditempuh. Lakukan saja Pengembaraan + Carilah dan tambahkan bobot rendah untuk perilaku pencarian.
krolth
6

Untuk mendapatkan jalan acak yang mulus, Anda bisa menggunakan splines Catmull-Rom . Spline semacam ini mengambil urutan titik dan menghasilkan kurva halus yang melewati setiap titik. Jadi, Anda bisa membuat titik arah acak agar sprite dapat bergerak dan menghidupkannya di sepanjang garis spline Catmull-Rom melalui titik arah. Agar spline berfungsi, Anda membutuhkan total empat titik arah: dua sebelumnya dan dua berikutnya. Saat sprite mencapai titik lewat, buang yang tertua dari empat set Anda dan hasilkan yang baru, kemudian lanjutkan animasi di sepanjang spline.

Adapun akhirnya menuju target, satu ide adalah untuk mengimbangi distribusi jalan acak menuju target. Misalnya, jika Anda biasanya memilih titik jalan acak menggunakan distribusi Gaussian yang berpusat pada posisi sprite Anda saat ini, Anda dapat mengimbangi pusat Gaussian dengan jarak yang telah ditentukan sebelumnya ke target. Ukuran relatif dari offset dan standar deviasi Gaussian akan menentukan seberapa bias gerakannya.

Nathan Reed
sumber
Saya sudah memikirkan hal ini, dan walaupun rekomendasinya bagus, saya ingin biasnya menuju lokasi pemain. Karena metode spline mengharuskan saya untuk membuat beberapa lintasan di muka, akan ada kelambatan dalam kemampuan untuk mengikuti pemain.
Biasa
@Superbest jika Anda menggunakan kurva Bezier, Anda hanya perlu menghasilkan dua poin berikutnya - dan Anda bisa membuat titik kedua di masa depan bergerak ke arah pemain saat ia bergerak.
Jonathan Dickinson
2

Ini adalah sesuatu yang saya hasilkan dalam 20 menit. Kami mengambil arah dari walker ke target, memilih arah dalam jumlah derajat tertentu dari arah itu (jumlah yang menurun ketika walker semakin mendekati target mereka). Algoritma ini juga memperhitungkan jarak ke target sehingga tidak akan melewati target. Singkatnya, pada dasarnya goyangan ke kiri dan kanan sedikit acak dan rumah pada target karena semakin dekat.

Untuk menguji algoritma ini, saya menempatkan walker di (10, 0, 10), dan target di (0, 0, 0). Pertama kali algoritme dijalankan, secara acak memilih posisi walker untuk berjalan ke (3.73f, 0, 6.71f). Setelah walker mencapai posisi yang dipilihnya (2.11f, 0, 3.23), lalu (0.96f, 0, 1.68f), lalu (0.50f, 0, 0.79f), lalu berjalan lurus ke target karena berada dalam jangkauan. jarak toleransi minimum.

Dipetakan dari pandangan mata burung, jalan akan terlihat seperti titik-titik pada gambar di bawah ini, mulai dari 'W' (walker) dan berakhir pada 'T' (target). Jika Anda menginginkan gerakan yang lebih alami, Anda akan melakukan precalcuate beberapa poin sebelumnya, dan membuat spline, memberi Anda lebih banyak poin yang bisa diikuti oleh walker. Saya telah memperkirakan seperti apa jalan ini setelah dibuat menjadi spline, dan itu diwakili oleh garis dalam gambar.

masukkan deskripsi gambar di sini

Dan inilah contoh kode:

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

Berdasarkan permainan spesifik Anda, Anda dapat mengubah jarak, FOV, keacakan, dan frekuensi bahwa ini dijalankan, sampai sesuai dengan kebutuhan Anda. Saya yakin algoritme dapat dibersihkan sedikit dan dioptimalkan, saya tidak menghabiskan banyak waktu untuk itu, saya hanya ingin itu mudah dibaca.

Nic Foster
sumber