简体   繁体   English

弹丸弯曲曲面 - Pygame,Python 3

[英]Projectile bouncing off curved surface - Pygame, Python 3

I am currently working on a (top-down) game that requires a code to bounce a projectile off of a circular surface. 我目前正在开发一种(自上而下)游戏,它需要一个代码才能从圆形表面反射抛射物。

The way I would like this to work is that when the projectile hits the surface 我想要这个工作的方式是当射弹击中表面时 在此输入图像描述

a hypothetical line is drawn from the center of the circle 从圆心绘制一条假想线 在此输入图像描述

which is then used as the normal to calculate the angle of reflection of the new trajectory. 然后将其用作法线来计算新轨迹的反射角。 在此输入图像描述

Physics (such as gravity) are completely unnecessary. 物理(如重力)完全没必要。 Is there an more or less simple way of doing that? 是否有一种或多或少的简单方法呢?

Reflect a line segment off a circle. 从圆圈反射线段。

This will get the reflexed line segment off a circle. 这将使反射的线段偏离圆圈。 The length of reflected line segment and incoming line segment to the circle intercept will be equal to the length of the original line segment. 反射线段和进入线段到圆截距的长度将等于原始线段的长度。 If you just want the reflected ray just stop the calculations when the reflected vector has been worked out. 如果您只想要反射光线,只需在计算出反射矢量时停止计算。 This also assumes that the incoming line segment starts outside the circle. 这也假设进入的线段在圆外开始。 If the line starts inside the line then it fails. 如果线在线内开始,则失败。 You can check if the line starts inside the circle by getting the distance from the circle center to the line start if that is less than the circle radius then the line starts inside the circle. 您可以检查线是否在圆内开始,通过获得从圆心到线起点的距离,如果小于圆半径,则线在圆内开始。

This is in pseudo code as I don't do PHP. 这是伪代码,因为我不做PHP。 sqrt() is a function to get the sqrt of a number. sqrt()是一个获取数字sqrt的函数。

The incoming ray as a line segment. 传入的光线作为线段。 Where line.x1, line.y1 are the start and x2,y2 are the end 其中line.x1,line.y1是开始,x2,y2是结束

line x1,y1,x2,y2

The circle with x,y position and r radius 带有x,y位置和r半径的圆

circle x,y,r

First you need to get the line segment circle intercept if any. 首先,你需要获得线段圈截取(如果有的话)。 So find the closest point on the line to the circle center and see if the distance from that point to the circle center is less than the circle radius.If it is then the line segment might intercept. 因此,找到直线上最靠近圆心的点,看看从该点到圆心的距离是否小于圆心半径。如果是,那么线段可能会截取。 If the distance is greater than there is no intercept. 如果距离大于没有拦截。

Convert the line segment to a vector 将线段转换为矢量

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

Get the length of the line segment squared 获取线段的平方长度

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

Get the normalised distance from the start of the line to the point that is closest to the circle center 获取从线的起点到最接近圆心的点的归一化距离

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

Then using the normalised distance get the point closest to the circle in absolute position. 然后使用归一化距离获得绝对位置中最接近圆的点。

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

Now get the distance from that point to the circle center 现在获取从该点到圆心的距离

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

If the distance is less than the circle radius then there is possibility of an intercept. 如果距离小于圆半径,则可能存在截距。 If not then there is no intercept so exit as there is no reflection. 如果没有那么就没有拦截,所以退出,因为没有反射。

if( dist > circle.r) exit;

Now we know that the line will intercept the circle, we need to check if the line segment intercepts, so we find the intercept point towards the line start and see if that point is on the line segment or not. 现在我们知道该线将截取圆,我们需要检查线段是否截取,因此我们找到朝向线开始的截距点并查看该点是否在线段上。

Thus From the closest point on the line to the circle center is length `dist' that we just worked out then out from there to the intercept point is length circle.r and then the distance back to the closest point is unknown, But we know that the triangle the three lengths make is a right triangle the missing side is 因此,从线上最近的点到圆心是我们刚刚计算出的长度'dist'然后从那里到拦截点是长度circle.r然后回到最近点的距离是未知的,但我们知道三个长度所形成的三角形是缺失边的直角三角形

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

This gives us the distance from the closest point on the line to the circle center, back along the line toward the start to get to the intercept point. 这给了我们从线上最近点到圆心的距离,沿着线朝向开始到达截距点。 For convenience I will convert that distance to a unit scaled distance (normalised) by dividing by the length of the line segment 为方便起见,我将通过除以线段的长度将该距离转换为单位缩放距离(标准化)

lenToIntercept = lenToIntercept / sqrt(len2)

Subtract that normalised length from the normalised distance from the start of the incoming line to the closest point on the line to the circle center. 从归入线路起点到线圈中心线上最近点的归一化距离减去归一化长度。

unitDist = unitDist - lenToIntercept;

If the new unitDist to the intercept point is >= 0 and <= 1 then we know that the line segment has intercepted the circle, otherwise the the line segment has not intercepted the circle and we can exit. 如果拦截点的新unitDist> = 0且<= 1,那么我们知道线段截获了圆圈,否则线段没有拦截圆圈而我们可以退出。

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

Now we can calculate the absolute position of the intercept point by adding the vector multiplied by the normalised distance from the start of the incoming line to intercept point 现在我们可以通过将矢量乘以从输入线的起点到截距点的归一化距离来计算截距点的绝对位置

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

Now we can work out the reflection. 现在我们可以解决这个问题了。

Get the vector from the circle center to the intercept point 从圆心到截距点获取矢量

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

We need the tangent to make the calculations easier so get the tangent by rotating clockwise 90 deg 我们需要切线来使计算更容易,因此通过顺时针旋转90度来获得切线

tx = -cy;
ty = cx;

Normalise the tangent vector. 归一化切向量。 We know it is the same length as the circle radius because it is just the rotated line from the circle center to the intercept point so we can normalise the tangent vector using the circle radius 我们知道它与圆半径的长度相同,因为它只是从圆心到截距点的旋转线,因此我们可以使用圆半径对切线矢量进行归一化

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

We also need to normalise the incoming line vector so divide it by its length 我们还需要对传入的线矢量进行标准化,然后将其除以其长度

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

Now get the dot product of the tangent and the line seg. 现在得到切线和线段的点积。 It is the distance from the incoming vector end to the tangent line, squared 它是从传入矢量端到切线的距离,平方

dot = vx * tx + vy * ty;

Double it because we want to get the reflection which is on the other side of the tangent line. 将它加倍,因为我们想要得到切线另一侧的反射。

dot = dot * 2;

Now lengthen the normalised tangent vector by the dot amount 现在将标准化切线矢量延长点数量

tx = tx * dot;
ty = ty * dot;

and subtract the incoming line vector to give us the vector of the outgoing line 并减去传入的线矢量,为我们提供输出线的矢量

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

Normalise that vector by getting its length 通过获取其长度来标准化该向量

lengR = sqrt(reflectedX * reflectedX + reflectedY * reflectedY);

and dividing the reflected line by that length 并将反射线除以该长度

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

Now to calculate the reflected part of the line which is the distance form the circle intercept point to the end of the incoming line. 现在计算线的反射部分,即从圆截距点到输入线末端的距离。 We already have the normalised distance along the line to the intercept point so the remaining distance is 我们已经沿着直线到拦截点的归一化距离,所以剩余的距离是

remainDist = 1-unitDist;

Multiply the normalised distance by the incoming line length 将标准化距离乘以输入线长度

remainDist = remainDist * sqrt(len2);

now multiply the reflected normalised vector by this length 现在将反射的归一化向量乘以该长度

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

A lastly create the new reflected line segment which is the line from the intercept point to that point plus the reflected vector 最后创建新的反射线段,即从截点到该点加上反射矢量的线

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

Assuming you know the origin of the circle (x0,y0) , the collision point (x1,y1) , and the equation of the line, all you have to do is calculate the angle difference between the line and the radius: 假设你知道圆的原点(x0,y0) ,碰撞点(x1,y1)和直线的方程,你所要做的就是计算直线和半径之间的角度差:

  • equation of the line: y = a*x+B (you know this one) 线的方程: y = a*x+B (你知道这个)
    -> angle of the line = theta0 = arctg(a) - >线的角度= theta0 = arctg(a)
  • angle of the radius: theta1 = arctg((y1-y0)/(x1-x0)) 半径角: theta1 = arctg((y1-y0)/(x1-x0))
  • angle of the second line = theta2 = 2*theta0 - theta1 第二行的角度= theta2 = 2*theta0 - theta1
  • equation of the bounced line = y = y1 + tg(theta2)*(x-x1) 弹跳线的方程= y = y1 + tg(theta2)*(x-x1)

This should work although you might need a math lib 虽然您可能需要数学库,但这应该可行

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM