如何在小速度下进行鼠标定位时避免使用内置偏置?

时间:2013-03-23 21:41:32

标签: java swing mouse rounding rounding-error

我有一个简单的算法,一个对象在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.sinMath.cos始终返回[-1,1]范围内的值,dydx也将在[-1,1]范围内。通过舍入转换为整数时(因为目标x和y位置定义为int变量),每个位移都舍入为-10或{{1} },有效地将可能的运动限制在相同的八个角度。

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

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

2 个答案:

答案 0 :(得分: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偶尔发生变化的情况下给出一个不错的结果。

我忽略了帧数溢出的影响,但它们应该是最小的(请注意,如果使用减法而不是递增1)。

你必须加入整体部分,标志和有两个维度的事实。如果你以每帧的速度(2.7,-0.4)移动,那么你总是每帧移动(2,0),并使用上面的方法用dx = 0.7来确定你是否另外移动(1,0),并且dx = 0.4以确定是否另外移动(0,-1)。

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

答案 1 :(得分:0)

我在Java上有点生气,但也许你可以使用vector math而不是trig?

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

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