弹丸弯曲曲面 - Pygame,Python 3

时间:2016-01-07 19:57:57

标签: python python-3.x pygame game-physics

我目前正致力于一项(自上而下)游戏,该游戏需要使用代码将弹丸从圆形表面弹回。

我希望这种方式起作用的方式是当弹丸击中表面时enter image description here

从圆圈enter image description here

的中心绘制一条假想线

然后用作法线来计算新轨迹的反射角。enter image description here

物理(如重力)完全没必要。是否有一种或多或少的简单方法呢?

2 个答案:

答案 0 :(得分:2)

反射一个圆圈的线段。

这将使反射的线段偏离圆圈。反射线段和进入线段到圆截距的长度将等于原始线段的长度。如果您只想要反射光线,只需在计算出反射矢量时停止计算。这也假设进入的线段在圆外开始。如果线在线内开始,则失败。您可以通过获取圆心与线起点之间的距离来检查线是否在圆内开始,如果小于圆半径,则线在圆内开始。

这是伪代码,因为我不做PHP。 sqrt()是一个获取数字sqrt的函数。

传入光线为线段。其中line.x1,line.y1是开头,x2,y2是结尾

line x1,y1,x2,y2

具有x,y位置和r半径的圆

circle x,y,r

首先,你需要获得线段圈截取(如果有的话)。因此,找到直线上最靠近圆心的点,看看从该点到圆心的距离是否小于圆心半径。如果是,那么线段可能会截取。如果距离大于没有拦截。

将线段转换为矢量

vx = line.x2 - line.x1;
vy = line.y2 - line.y1;

获取线段平方的长度

len2 = (vx * vx + vy * vy);

获取从线的起点到最接近圆心的点的归一化距离

unitDist =  ((circle.x - x1) * vx + (circle.y - y1) * vy) / len2;

然后使用归一化距离获得绝对位置最接近圆的点。

x = x1 + vx * unitDist;
y = y1 + vy * unitDist;

现在获取从该点到圆心的距离

dist =  sqrt((x - circle.x)*(x - circle.x)+ (y - circle.y)*(y - circle.y));    

如果距离小于圆半径,则可能存在截距。如果没有那么就没有拦截,所以退出,因为没有反射。

if( dist > circle.r) exit;

现在我们知道该线将截取圆,我们需要检查线段是否截取,因此我们找到朝向线开始的截距点并查看该点是否在线段上。

因此,从线上最近的点到圆心是长度'dist'我们刚刚从那里开始到拦截点是长度circle.r然后回到最近点的距离是未知的,但是我们知道三个长度所做的三角形是一个直角三角形,缺少的一边是

lenToIntercept = sqrt(circle.r * circle.r - dist * dist);

这给出了从线上最近点到圆心的距离,沿着线向后朝向开始点到达截距点。为方便起见,我将通过除以线段的长度将该距离转换为单位缩放距离(标准化)

lenToIntercept = lenToIntercept / sqrt(len2)

从归入线起点到线圈中心线上最近点的归一化距离减去归一化长度。

unitDist = unitDist - lenToIntercept;

如果拦截点的新unitDist是> = 0且< = 1,那么我们就知道线段拦截了圆圈,否则线段没有拦截圆圈而我们可以退出。

if(unitDist < 0 or unitDist > 1) exit; // no intercept

现在我们可以通过将矢量乘以从输入线的起点到截距点的归一化距离来计算截距点的绝对位置

x = line.x1 + unitDist * vx;
y = line.y1 + unitDist * vy;

现在我们可以解决这个问题。

从圆心到截距点获取矢量

cx = x - circle.x;
cy = y - circle.y;

我们需要切线使计算更容易,因此通过顺时针旋转90度来获得切线

tx = -cy;
ty = cx;

标准化切线向量。我们知道它与圆半径的长度相同,因为它只是从圆心到截距点的旋转线,因此我们可以使用圆半径对切线矢量进行归一化

tx = tx / circle.r;
ty = ty / circle.r;

我们还需要对传入的线矢量进行标准化,然后将其除以其长度

vx = vx / sqrt(len2);
vy = vy / sqrt(len2);

现在获得切线和线段seg的点积。它是从传入矢量端到切线的距离,平方

dot = vx * tx + vy * ty;

加倍,因为我们想得到切线另一侧的反射。

dot = dot * 2;

现在用标记量

延长标准化切线向量
tx = tx * dot;
ty = ty * dot;

并减去传入的线矢量,为我们提供传出线的矢量

reflectedX = tx - vx;
reflectedY = ty - vy;

通过获取长度

来规范化该向量
lengR = sqrt(reflectedX * reflectedX + reflectedY * reflectedY);

并将反射线除以该长度

reflectedX = reflectedX / lengR;
reflectedY = reflectedY / lengR;

现在计算线的反射部分,即从截距点到输入线末端的距离。我们已经沿着直线到拦截点的归一化距离,所以剩余的距离是

remainDist = 1-unitDist;

将归一化距离乘以进线长度

remainDist = remainDist * sqrt(len2);

现在将反射的归一化向量乘以此长度

reflectedX = reflectedX * remainDist;
reflectedY = reflectedY * remainDist;

最后创建新的反射线段,即从截取点到该点加上反射矢量的线

reflectedLine.x1 = x;
reflectedLine.y1 = y;
reflectedLine.x2 = x + reflectedX;
reflectedLine.y2 = y + reflectedY;

答案 1 :(得分:1)

假设您知道圆(x0,y0)的起源,碰撞点(x1,y1)以及线的方程,您所要做的就是计算直线与半径之间的角度差:

  • 线的等式:y = a*x+B(你知道这一个)
    - &GT;线的角度= theta0 = arctg(a)
  • 半径角:theta1 = arctg((y1-y0)/(x1-x0))
  • 第二行的角度= theta2 = 2*theta0 - theta1
  • 反弹线的等式= y = y1 + tg(theta2)*(x-x1)

虽然您可能需要数学库

,但这应该有效