【问题标题】:Flocking Algorithm Without Vectors?没有向量的植绒算法?
【发布时间】:2018-04-12 01:04:23
【问题描述】:

我正在尝试为我的游戏中的僵尸实现一个集群算法,以便它们聚集在一起,同时避免相互碰撞。到目前为止,它们总是直接向玩家移动,如果玩家移动很多,可能会导致它们堆叠在一起。

这让我得到了一些关于成群结队的小僵尸的反馈,因为玩家射击它们(它们是一击必杀)并且因为它们上面还有更多,所以他们一直告诉我它看起来子弹没有击中。

我一直在研究使用植绒算法,但问题是我的游戏不使用向量来进行敌人的移动。我只是将敌人的位置作为 (x,y) 对,在更新时,他们使用 atan2 计算他们的面对,然后根据这个 theta 的 cos 和 sin 值更新他们的位置。

我可以添加矢量移动,但这需要大量的重构,所以我希望看看是否有某种方法可以在没有它的情况下实现这个算法。这是我现在的算法。我知道有几个缺陷,但我预计至少会看到一些不同的行为——但僵尸的移动方式仍然完全相同。

public void move(GameState gs, int delta) {
    theta = Calculate.Hypotenuse(position, Globals.player.getPosition());

    int neighbors = 0;
    float force = 0.0f;
    for(Enemy e : ((EnemyController)gs.getEntity("enemyController")).getAliveEnemies()) {
        if(!e.equals(this)) {
            float dist = Calculate.Distance(position, e.getPosition());
            if(dist < getSeparationDistance()) {
                // Add separation strength to theta.
                force += Zumby.SEPARATION_STRENGTH;
                neighbors++;
            } else if(dist < getCohesionDistance()) {
                // Add cohesion strength to theta.
                force += Zumby.COHESION_STRENGTH;
                neighbors++;
            }
        }
    }

    if(neighbors > 0) {
        force /= neighbors;
        theta += force;
    }

    if(!moveBlocked) {
        position.x += (float)Math.cos(theta) * Zumby.SPEED * delta;
        position.y += (float)Math.sin(theta) * Zumby.SPEED * delta;
    }

    moveBlocked = false;

    bounds.setCenterX(position.x);
    bounds.setCenterY(position.y);
}

感谢您的帮助。

【问题讨论】:

  • 使用(纯)极角表示而不是笛卡尔向量通常是一个非常糟糕的主意(您经常需要考虑 180 度的不连续性,尤其是在使用增量时)。此外,将正数 force 添加到角度不会很有用 - 您需要考虑力作用的方向。此外,由于您已经将位置存储为 (x, y),因此采用它应该相当方便仅此函数的向量。

标签: java algorithm game-physics


【解决方案1】:

极角距离是一个向量

我对你的问题感到困惑。

没有向量的植绒算法?

但你确实使用了向量数学

position.x += (float)Math.cos(theta) * Zumby.SPEED * delta;
position.y += (float)Math.sin(theta) * Zumby.SPEED * delta;

您正在将极坐标矢量{theta, Zumby.SPEED * delta} 的笛卡尔形式添加到笛卡尔坐标position

您可以随时从极坐标转换为笛卡尔坐标,然后返回如下。

// description of polar and cartesian vector objects
polar = {angle, distance}
cartesian = {x, y}

// From polar to cartesian
cartesian.x = Math.cos(polar.angle) * polar.distance;
cartesian.y = Math.sin(polar.angle) * polar.distance;

// and cartesian to polar
polar.angle = Math.atan2(cartesian.y, cartesian.x);
polar.distance = Math.sqrt(cartesian.y * cartesian.y + cartesian.x * cartesian.x);

还有一些其他的向量函数(对于笛卡尔,缩短名称 c = {x,y},对于极坐标缩短名称 p = {a,d},其中 a 是角度,d 是距离)。

标准化

// cartesian
float length = Math.sqrt(c.x * c.x + c.y * c.y);
if (length > 0) {
    c.x /= length;
    c.y /= length;
} else {
    c.x = 1.0; // default vector or throw an error
    c.y = 0.0; 
}

// polar is too easy
p.d = 1.0;

旋转

// rotate where rotate is the rotation in radians

// cartesian
float dx = Math.cos(rotate);
float dy = Math.cos(rotate);
// c1 is a new vector
c1.x = c.x * dx - c.y * dy;
c1.y = c.x * dy + c.y * dx;


// polar is again too easy
p.a += rotate;

缩放/乘法

// scale is the scaling 

// cartesian scale or multiply
c.x *= scale;
c.y *= scale;

// inverse or divide
c.x /= scale;
c.y /= scale;


// polar 
p.d *= scale;

// inverse
p.d /= scale;

添加

// adding vector 

// cartesian 
c.x += c1.x;
c.y += c1.y;

// polar not too easy
float x = Math.cos(p.a) * p.d + Math.cos(p1.a) * p1.d;
float y = Math.sin(p.a) * p.d + Math.sin(p1.a) * p1.d;
p.a = Math.atan2(y,x);
p.d = Math.sqrt(x * x + y * y);

使用哪个

您可以对极坐标向量进行所有与笛卡尔向量相同的操作。有些会以一种形式比另一种更容易。

这两种形式之间的转换非常容易,因此您无需重构所有代码。如果你有一个操作不知道极坐标的方法,只需转换为笛卡尔,执行操作,然后再转换回极坐标。

【讨论】:

  • 我的意思是我没有使用向量数据结构,所以我没有很多容易获得的向量方法。我希望我可以避免为了这个目的而实施它们。我想我可以按照 meowgoesthedog 的建议单独使用 Vectors。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-10
相关资源
最近更新 更多