약 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, 임의성 및 실행 빈도를 원하는대로 조정할 수 있습니다. 알고리즘을 약간 정리하고 최적화 할 수 있다고 확신합니다. 많은 시간을 소비하지 않았으며 읽기 쉽기를 원했습니다.