偏向保守的随机游走


13

我有一个精灵,它具有VelocityPosition,它们存储为Vector2。在每个Update循环中,将速度加到该位置。

我想给精灵一个第三向量Target。可以在任何迭代中给出新的目标。我希望子画面本质上以随机行走模式移动,但是必须公开两个参数:

  1. 典型的随机游走同样有可能增加或减少到任何给定距离Target(加上切向运动的机会很小)。我必须能够偏向我的随机游走,以便尽管仍然是随机的,但子画面“决定”的方向应该更可能使其接近Target
  2. 随机游走应该是“平稳的”-子画面不应迅速改变方向,因为对玩家而言,这看起来像是“忽悠”或“颤抖”。它应该以这种方式逐渐转向,随机移动,而平均时则逐渐靠近。

有什么好的简单方法呢?如果可能,给出答案作为Vector2 RandomWalk(Vector2 target)方法。

我已经有一个NextGaussian(mean, stdev)可用的方法,如果有帮助的话。


给它很小的机会来改变每一帧的方向吗?如果没有按照您希望的方向移动,那么使机会大大增加吗?
BlueRaja-Danny Pflughoeft 2012年

那是很好的解决方案。但是,如果可能的话,我宁愿避免方向突然变化。
极好的

1
只需添加一个加速度因数,即可更改加速度,而不是更改速度,从而更改速度。这应该可以消除运动中的抖动,您可以调整加速度的应用,直到获得平稳的行走为止
skeletalmonkey 2012年

Answers:


4

我将采用“徘徊”的转向行为(可在此处找到源代码),并进行某种方式的调整,以使随机数偏向您的目标。


是的,我认为转向行为是解决之道。只需执行“漫游+搜索”,然后为搜索行为添加低权重即可。
krolth

6

要获得平滑的随机游走,可以使用Catmull-Rom样条曲线。这种样条曲线采用一系列点,并生成通过每个点的平滑曲线。因此,您可以为精灵生成随机的航路点,以使其沿Catmull-Rom样条线移动并对其进行动画处理。为了使样条线起作用,您总共需要四个航路点:前两个和后两个。当精灵到达某个航路点时,扔掉四个中最旧的一个并生成一个新的,然后继续沿样条进行动画处理。

至于最终走向目标,一个想法是抵消随机游走走向目标的分布。例如,如果您通常使用以精灵当前位置为中心的高斯分布来选择随机航路点,则可以将高斯的中心向目标偏移一定的预设距离。偏移量的相对大小和高斯标准偏差将确定运动的偏差程度。


我已经考虑过了,虽然推荐的建议不错,但我还是希望偏向玩家的位置。由于样条方法要求我提前生成一些路径,因此跟随玩家的能力将有所滞后。
极好的

@Superbest,如果您使用贝塞尔曲线,则只需要生成接下来的两个点即可,并且可以使未来的第二点随着玩家的移动而朝着玩家移动。
乔纳森·迪金森

2

这是我在大约20分钟内完成的事情。我们采用从步行者到目标的方向,在该方向的一定角度范围内选择一个方向(随着步行者接近其目标,该方向的数量会减少)。该算法还考虑了到目标的距离,因此它不会走过目标。长话短说,它基本上会在左右随机晃动少量,然后随着距离的临近而回到目标。

为了测试该算法,我将助步器放在(10,0,10),将目标放在(0,0,0)。该算法第一次运行时,随机选择了步行者要行走的位置(3.73f,0,6.71f)。步行者到达该位置后,选择(2.11f,0,3.23f),然后选择(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;
}

根据您的特定游戏,您可以调整距离,视场,随机性和运行频率,直到满足您的需求为止。我确信算法可以进行一些清理和优化,我并没有花太多时间,我只是想让它易于阅读。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.