편견적이고 보수적 인 랜덤 워크


13

VelocityPosition로 저장된 스프라이트가 있습니다 Vector2. 각 Update사이클에서 속도가 위치에 추가됩니다.

스프라이트에 세 번째 벡터 인을주고 싶습니다 Target. 반복 할 때마다 새로운 목표를 부여 할 수 있습니다. 스프라이트가 본질적으로 임의의 보행 패턴으로 움직이기를 원하지만 두 개의 매개 변수를 노출해야합니다.

  1. 전형적인 임의의 보행은 주어진 거리까지 거리를 늘리거나 줄일 수 있습니다 Target(접선 운동의 가능성이 적음). 나는 무작위로 걷는 것을 편향시킬 수 있어야하는데, 여전히 무작위이지만, 스프라이트가 "결정"하는 방향이 더 근접 할 가능성이 높아야한다 Target.
  2. 무작위 보행은 "부드럽게"되어야합니다. 스프라이트는 플레이어에게 "깜박 거림"또는 "떨림"처럼 보이기 때문에 방향이 빠르게 바뀌지 않아야합니다. 평균적으로 보면 점점 가까워지면서 무작위로 움직이면서 이런 식으로 또는 점차적으로 방향을 바꿔야합니다.

이를위한 좋고 간단한 방법은 무엇입니까? 가능하면 Vector2 RandomWalk(Vector2 target)방법으로 답변하십시오 .

NextGaussian(mean, stdev)도움이된다면 이미 사용 가능한 방법이 있습니다.


매 프레임마다 방향을 바꿀 수있는 아주 작은 기회가 있습니까? 그리고 원하는 방향으로 움직이지 않는다면 그 기회를 크게 늘리겠습니까?
BlueRaja-대니 Pflughoeft

좋은 해결책입니다. 그러나 가능하면 갑작스럽고 급격한 방향 변경을 피하는 것이 좋습니다.
Superbest

1
가속 계수를 추가하기 만하면 속도를 변경하는 대신 가속도를 변경하여 속도를 변경합니다. 이것은 운동에서 지터를 제거해야하며, 부드럽게 걸을 때까지 가속의 적용을 조정할 수 있습니다
skeletalmonkey

답변:


4

나는 "방랑자"조향 동작 (소스 코드는 여기 에서 찾을 수 있음 ) 을 취하여 임의의 숫자가 목표를 향해 편향되도록 방식을 조정합니다.


그래, 나는 조향 행동이 갈 길이라고 생각한다. 방랑자 + 탐색을 수행하고 탐색 동작에 낮은 가중치를 추가하십시오.
krolth

6

부드러운 무작위 걷기를 위해 Catmull-Rom 스플라인을 사용할 수 있습니다 . 이러한 종류의 스플라인은 일련의 점을 취하고 각 점을 통과하는 부드러운 곡선을 생성합니다. 따라서 스프라이트가 이동하는 중간 웨이 포인트를 생성하여 웨이 포인트를 통해 Catmull-Rom 스플라인을 따라 애니메이션 할 수 있습니다. 스플라인이 작동하려면 총 2 개의 웨이 포인트가 필요합니다 : 이전 2 개와 다음 2 개. 스프라이트가 웨이 포인트에 도달하면 네 세트 중 가장 오래된 것을 버리고 새로운 것을 생성 한 다음 스플라인을 따라 계속 애니메이션합니다.

결국 목표를 향하여 향하는 한 가지 아이디어는 목표를 향한 무작위 보행의 분포를 상쇄하는 것입니다. 예를 들어, 스프라이트의 현재 위치를 중심으로 한 가우시안 분포를 사용하여 임의의 웨이 포인트를 선택하는 경우 대상을 향한 미리 설정된 거리만큼 가우스 중심을 오프셋 할 수 있습니다. 오프셋의 상대적 크기와 가우스 표준 편차는 동작의 편향 정도를 결정합니다.


나는 이것에 대해 생각했고, 추천이 좋으면서 편견이 플레이어의 위치를 ​​향하도록하고 싶습니다. 스플라인 방법을 사용하려면 경로를 미리 생성해야하므로 플레이어를 따라갈 수있는 기능이 지연됩니다.
Superbest

@Superbest 베 지어 곡선을 사용하는 경우 다음 두 점만 생성하면되며 미래의 두 번째 점은 선수를 향해 움직일 수 있습니다.
Jonathan Dickinson

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.