如何沿着另一个对象的圆周移动一个对象?


11

我太过数学了,很难受,但对你们中的某些人来说应该是小菜一碟。我想在一个简单的圆形路径上沿对象的年龄或周长围绕另一个对象移动对象。目前,我的游戏算法知道如何移动精灵并将其放置在障碍物的边缘,现在它会根据各种条件等待下一个点的移动。

因此,这里的数学问题是如何得到(AX,AY)(BX,BY)位置,当我知道中心(CX,CY),目标位置(OX,OY)和需要移动的距离(d)

在此处输入图片说明


1
d直线距离还是弧线?
MichaelHouse

它是一个以像素为单位的线性距离
Lumis

您对向量和它们的基本操作完全熟悉吗?
Patrick Hughes 2012年

@Patrick不,我想我将不得不学习向量课程。由于这是一帧一帧的动画,因此代码应该是快速且优化的。
Lumis 2012年

Answers:


8

CAVEAT:我在这里使用两个近似值:第一个近似值d为圆弧长度,第二个近似值为正交长度。这两个近似值对于d的较小值都应该是好的,但它们不能满足注释中阐明的确切问题。)

幸运的是,关于这一点的数学相对简单。首先,我们可以找到从中心位置到当前位置的相对矢量:

deltaX = oX-cX;
deltaY = oY-cY;

一旦有了这个相对矢量,我们就可以通过找到圆的长度来知道我们正在加工的圆的半径:

radius = sqrt(deltaX*deltaX+deltaY*deltaY);

此外,从我们的相对矢量中,我们可以找到从cX到oX的直线的精确角度:

curTheta = atan2(deltaX, deltaY);

现在事情变得有些棘手了。首先,请了解圆的周长(即,角度为2π的圆弧的“弧长”)为2πr。通常,沿半径为r的圆的角度为θ的弧的弧长仅为θr。如果我们将图表中的d用作圆弧长度,并且由于我们知道半径,我们可以通过除以找到theta的变化以将我们带到新位置:

deltaTheta = d/radius; // treats d as a distance along the arc

对于d需要为线性距离的情况,情况会稍微复杂一些,但幸运的是,情况并不多。d是等腰三角形的一侧,另一侧是圆的半径(分别从cX / cY到oX / oY和aX / aY),将等腰三角形二等分可以得到两个直角三角形,每个直角三角形d / 2为一侧,半径为斜边;这意味着我们一半角度的正弦为(d / 2)/半径,因此整个角度仅为此的两倍:

deltaTheta = 2*asin(d/(2*radius)); // treats d as a linear distance

请注意,如果您从该公式中删除了asin并取消了2s,它将与最后一个公式相同;这与说x(x)较小时sin(x)近似为x相同,这是一个有用的近似值。

现在我们可以通过添加或减去找到新角度:

newTheta = curTheta+deltaTheta; // This will take you to aX, aY. For bX/bY, use curTheta-deltaTheta

一旦有了新的角度,就可以使用一些基本的trig来查找更新的相对矢量:

newDeltaX = radius*cos(newTheta);
newDeltaY = radius*sin(newTheta);

从中心位置和相对矢量我们可以(最终)找到目标点:

aX = cX+newDeltaX;
aY = cY+newDeltaY;

现在,所有这些都需要注意一些重大警告。首先,您会注意到该数学运算主要是浮点运算,实际上几乎必须是浮点运算。尝试使用此方法循环更新并在每一步取整为整数值可以完成从使圆不闭合(每次绕圈向内或向外盘旋)到第一次不开始时的所有操作地点!(如果d太小,则可能会发现aX / aY或bX / bY的舍入版本恰好是起始位置oX / oY所在的位置。)另一方面,这非常昂贵,尤其是对于它试图做; 通常,如果您知道角色将沿圆弧运动,则应提前计划整个弧度,而不是像这样逐帧打勾,因为这里许多最昂贵的计算都可以预先加载以降低成本。如果您确实想像这样逐步进行更新,则另一种降低成本的好方法是不要首先使用trig。如果d很小,并且您不需要精确而又非常接近,则可以通过向oX / oY添加长度为d的矢量(正交于朝向您的中心的矢量)来进行“欺骗”与(dX,dY)正交的向量由(-dY,dX)给出,然后将其缩小到正确的长度。我不会一步一步地解释此代码,但是希望您到目前为止已经看到的内容对它有意义。请注意,我们在最后一步中隐式“缩小”了新的增量矢量,

deltaX = oX-cX; deltaY = oY-cY;
radius = sqrt(deltaX*deltaX+deltaY*deltaY);
orthoX = -deltaY*d/radius;
orthoY = deltaX*d/radius;
newDeltaX = deltaX+orthoX; newDeltaY = deltaY+orthoY;
newLength = sqrt(newDeltaX*newDeltaX+newDeltaY*newDeltaY);
aX = cX+newDeltaX*radius/newLength; aY = cY+newDeltaY*radius/newLength;

1
史蒂文·史蒂文(Steven)我想我将首先尝试近似法,因为这只是一款让游戏变得自然而有趣而不是精确的游戏。速度也很重要。谢谢您冗长而出色的教程!
Lumis 2012年

哇,史蒂文,您的逼近如梦似幻!您能告诉我如何更改您的代码以获得bX,bY。我尚不清楚您的正交概念...
Lumis

2
当然!您确实会在某个时候理解向量数学,一旦您怀疑这将是第二自然。要获得bX / bY,您只需要“另辟around径”即可-换句话说,无需添加(特定的)正交向量,只需将其减去即可。根据上面的代码,这将是'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY;”,然后对newLength进行相同的计算,然后“ bX = cX + newDeltaX radius / newLength; bY = cY + newDeltaY radius / newLength;'。
Steven Stadnicki 2012年

基本上,该代码会将newDeltaX / newDeltaY指向bX / bY方向(而不是aX / aY方向),然后进行修剪以适合并添加到中心,就像获取aX / aY一样。
Steven Stadnicki 2012年

9

使用已有的两个边形成一个三角形(一侧从'c'到'o',另一侧从'o'到'a'),第三侧从'a'到'c'。您不知道“ a”在哪里,只需想象现在有一个点。您将需要三角函数来计算与“ d”面相对的角度。您具有边c <-> o和c <-> a的长度,因为它们都是圆的半径。

现在,您已经知道了该三角形的三个边的长度,您可以确定与三角形的“ d”侧相反的角度。如果需要,这是SSS(侧面-侧面)公式: http //www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

使用SSS公式,您可以得到与边“ d”相对的角度(我们将其称为“ j”)。因此,现在我们可以计算(aX,aY)。

// This is the angle from 'c' to 'o'
float angle = Math.atan2(oY - cY, oX - cX)

// Add the angle we calculated earlier.
angle += j;

Vector2 a = new Vector2( radius * Math.cos(angle), radius * Math.sin(angle) );

确保您要计算的角度始终为弧度。

如果需要计算圆的半径,可以使用向量减法,从点“ o”中减去点“ c”,然后获得所得向量的长度。

float lengthSquared = ( inVect.x * inVect.x
                      + inVect.y * inVect.y
                      + inVect.z * inVect.z );

float radius = Math.sqrt(lengthSquared);

我相信,这样的事情应该可以做。我不懂Java,所以我猜到了确切的语法。

这是用户给出的图像,Byte56用于说明此三角形的外观: 曹三角


1
我正在回答,但是仅此而已。欢迎您使用我做了:)图像i.imgur.com/UUBgM.png
MichaelHouse

@ Byte56:谢谢,我没有任何图像编辑器句柄可以说明。
Nic Foster

注意,半径也必须计算。因为我们有一个等腰三角形,所以应该有比完整的SSS计算更简单的方法来获得j。)
Steven Stadnicki 2012年

是的,即使对我来说,这似乎也很简单!Android没有Vector2,所以我想我可以单独使用这些值。有趣的是,我在这里找到了为Android手动创建的Vector2类:code.google.com/p/beginning-android-games-2/source/browse/trunk/…–
Lumis

(我已经对自己的答案进行了调整,以找到正确的线性距离-在那里计算deltaTheta的第二次计算,如2 * asin(d /(2 * radius)),这是在这里找到j的方法。)
Steven Stadnicki

3

要使obj2围绕obj1旋转,可以尝试:

float angle = 0; //init angle

//call in an update
obj2.x = (obj1.x -= r*cos(angle));
obj2.y = (obj1.y += r*sin(angle));
angle-=0.5;

这首先没有显示如何获得角度,并且您似乎正在显示如何轨道运行,而不是像问题所要求的那样找到坐标。
MichaelHouse

1
刘易斯(Lewis),感谢您展示如何绕点旋转物体。它可能有用...
Lumis
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.