Đây là thứ tôi đã đánh lên trong khoảng 20 phút. Chúng tôi đi theo hướng từ người đi bộ đến mục tiêu, chọn một hướng trong một mức độ nhất định của hướng đó (một lượng giảm khi người đi bộ đến gần mục tiêu của họ hơn). Thuật toán này cũng chiếm khoảng cách đến mục tiêu để nó không đi qua mục tiêu. Câu chuyện dài, về cơ bản, nó lắc lư trái và phải một lượng nhỏ ngẫu nhiên và tập trung vào mục tiêu khi nó tiến gần hơn.
Để kiểm tra thuật toán này, tôi đặt walker tại (10, 0, 10) và mục tiêu tại (0, 0, 0). Lần đầu tiên thuật toán chạy nó đã chọn ngẫu nhiên một vị trí cho người đi bộ đi bộ đến (3.73f, 0, 6.71f). Sau khi người đi bộ đạt đến vị trí đó, nó đã chọn (2.11f, 0, 3.23), sau đó (0.96f, 0, 1.68f), sau đó (0,50f, 0, 0,79f), sau đó nó đi thẳng đến mục tiêu vì nó nằm trong một khoảng cách dung sai tối thiểu.
Vẽ đồ thị từ tầm nhìn của một con chim, con đường sẽ trông giống như các điểm trong hình ảnh bên dưới, bắt đầu từ 'W' (walker) và kết thúc tại 'T' (mục tiêu). Nếu bạn muốn có một chuyển động tự nhiên hơn, bạn sẽ định trước một vài điểm trước thời hạn và tạo ra một spline, cung cấp cho bạn nhiều điểm hơn bạn có thể có người đi bộ đi theo. Tôi đã ước tính con đường này sẽ trông như thế nào sau khi được tạo thành một spline và được thể hiện bằng đường thẳng trong hình ảnh.
Và đây là mã ví dụ:
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;
}
Dựa trên trò chơi cụ thể của bạn, bạn có thể điều chỉnh khoảng cách, FOV, tính ngẫu nhiên và tần suất mà trò chơi này được chạy, cho đến khi nó phù hợp với nhu cầu của bạn. Tôi chắc rằng thuật toán có thể được dọn dẹp một chút và tối ưu hóa, tôi đã không dành nhiều thời gian cho việc đó, tôi chỉ muốn nó dễ đọc.