1

我有一个简单的算法,让对象在 Java 中以给定的最大速度跟随鼠标指针。该算法的关键在于:

// Set up displacement trackers for later.
// (These are used in another part of the algorithm, which rotates
//  the object to face along its path tangent with a moving average.)
double dx = 0, dy = 0;

// ... more code

// Find the angle of travel.
double angle = Math.atan2(m.y - target.getY(), m.x - target.getX());

// Set displacements with polar coordinate transforms.
dx = maxSpeed * Math.cos(angle);
dy = maxSpeed * Math.sin(angle);

// Now go there.
target.setX((int) Math.round(target.getX() + dx));
target.setY((int) Math.round(target.getY() + dy));

这是以每秒 30 帧的速度运行的。性能不是问题。

代码在中等到大的值maxSpeed(5 及以上都可以)下运行良好,但在非常低的值下,代码会导致对象仅以特定角度移动。例如,在 处maxSpeed = 1,目标只能以 45° 角移动。


以下是我对问题的理解:

maxSpeed相等1。因此,因为Math.sinandMath.cos总是返回 [-1, 1] 范围内的值,dy并且dx也将在 [-1, 1] 范围内。当通过四舍五入转换为整数时(因为目标 x 和 y 位置被定义为int变量),位移均被四舍五入为-101,有效地将可能的移动限制在相同的八个角度。

因此,例如,如果对象从 (0, 0) 开始并且我将鼠标定位在 (300, 100),则对象将首先完美地水平移动,然后以 -45° 的角度移动。我希望物体以(大约)恒定角度移动,从起点到目的地的(大约)直线。

除了将基础 x 和 y 坐标转换为值之外,最好的方法是什么double

4

2 回答 2

1

我觉得你的最后一句话是这里的主要线索。您的解决方案越“正确”,它就越会开始近似地简单地跟踪缓慢移动物体位置的小数部分。为什么不简单地这样做呢?

但是,如果这绝对不可能,那么总会有黑客攻击。

让我们通过在一维中减少到一个方向来简化问题,消除速度和符号的整数部分。您有一个对象以每帧 dx 个像素的速度移动,dx 介于 0.0 和 1.0 之间。

您正在执行帧更新。您将对象移动了多少个整数像素?零还是一?

我们不仅希望以正确的概率更新,而且或多或少地平滑(等间距)。此外,由于我们的目标是可预测的视觉行为,我们不想使用随机性。

如果我们不能使用它,我们需要的是跟踪帧号,知道我们在一系列等距更新中的位置。

我认为以相当简单的方式获得“正确”结果的好方法是这样的:

int last_frame = (int) (((double)(current_frame_number-1)) * dx);
int this_frame = (int) (((double)current_frame_number) * dx);
if( this_frame != last_frame ) {
   ++x; // advance one pixel
}

对于任何给定的(常数!)dx,这应该给出正确的结果。在实践中,我希望它会给出一个不错的结果,dx 偶尔会发生变化。

我忽略了帧号溢出的影响,但它们应该是最小的(但要注意,如果你使用减法而不是加一)。

您必须重新添加不可分割的部分、符号以及存在两个维度的事实。如果你以每帧 (2.7,-0.4) 的速度移动,那么你总是每帧移动 (2,0),你使用上面的方法 dx=0.7 来确定你是否额外移动 (1,0),并使用dx=0.4 来确定你是否额外移动 (0,-1)。

但是,尽管从理论上考虑这是一个有趣的问题,但对于实际实现,我真的建议您只使用双精度值作为基础值。除了上述更难理解的事实之外,面对非恒定速度也不能保证正确 - 所以它比明显的解决方案更复杂和更糟糕。

于 2013-03-23T22:33:21.550 回答
0

我对 Java 有点生疏,但也许你可以使用向量数学而不是三角函数?

double xPos, yPos; // should be initialized with the starting position and updated with each change in position - rounding on each update will accumulate errors and produce odd behavior as you described
// start of update
Vector2d direction = (new Vector2d(m.x - target.getX(), m.y - target.getY())).normalize(); // create unit vector in the direction of the target
Vector2d velocity = direction.scale(maxSpeed); // set the scalar speed in the direction of the target
xPos += velocity.x;
yPos += velocity.y; 
target.setX((int) xPos); // move (maxSpeed)*cos(angle) units in the x direction
target.setY((int) yPos); // move (maxSpeed)*sin(angle) units in the y direction
// end of update

...应该工作 - 比触发方法更快:)。

于 2013-03-23T22:05:49.640 回答