两个分段之间的交叉算法错过了交叉点

时间:2016-06-20 09:14:42

标签: python algorithm python-2.7 pygame shadow

我目前正在研究为小型2D游戏制作一个简单的阴影投射算法。在使用优化库之前,我想尽可能自己尝试一下。

我觉得我几乎在那里,但由于某种原因,我用来检测来自我的线源和一个线段(障碍物或屏幕边缘的任何一侧)的光线之间的交叉点的方法似乎并不能捕获所有碰撞。

我将错误缩小到此函数,但我找不到错误。我经历过很多次,仍然无法弄清楚为什么它并不总是有效。我所有的光线都伸出屏幕,所以我至少应该检测到与屏幕边缘的碰撞。我还检查了算法循环遍历所有光线和段。

这是检查碰撞的方法:

def check_col(self, ray, source):
    ''' 
    segment1 = (self.start,self.end)
    segment2 = (source.pos,ray.far_pt)'''
    X1,Y1 = self.start #start pt of segment 1
    X2,Y2 = self.end #end pt of segment 1
    X3,Y3 = source.pos #start pt of segment 2
    X4,Y4 = ray.far_pt #end pt of segment 2

    '''we are looking to express the segments as:
    A*x + b = y '''

    '''ensuring mutual abscisse exists'''
    if (max(X1,X2) < min(X3,X4)) or (min(X1,X2) > max(X3,X4))\
    or (max(Y1,Y2) < min(Y3,Y4)) or (min(Y1,Y2) > max(Y3,Y4)):
        return False,0 #no mutual abscisse, 0 added to return a tulpe

    '''ensures no division by 0
    when calculating the segment
    slopes A1 and A2'''
    if float(X1-X2) == 0:
        X1 +=0.1 #add a small increment to avoid difference to be null
    if  float(X3-X4) == 0:
        X3 += 0.1 #add a small increment to avoid difference to be null

    '''calculating segment slopes'''
    A1 = (Y1-Y2)/float(X1-X2) # Pay attention to not dividing by zero
    A2 = (Y3-Y4)/float(X3-X4) # Pay attention to not dividing by zero

    b1 = Y1-A1*X1# = Y2-A1*X2
    b2 = Y3-A2*X3# = Y4-A2*X4    

    '''if slopes are the same, offsets one slightly
    to avoid dividing by 0 later on'''
    if (A1 == A2):
        A1 += 0.0001

    '''finding out intersect between the two segments at (Xa,Ya)'''    
    #A1 * Xa + b1 = A2 * Xa + b2
    Xa = int((b2 - b1) / (A1 - A2))# Once again, pay attention to not dividing by zero
    Ya = int(A1 * Xa + b1)
    #Ya = int(A2 * Xa + b2)
    col_pt = (Xa,Ya)      

    '''make sure intersection is within bounds'''
    if max(min(X1,X2),min(X3,X4)) <= Xa <= min(max(X1,X2),max(X3,X4)):
            '''calculates distance between the light source and the collision point'''
            dist = sqrt((source.pos[0]-col_pt[0])**2+(source.pos[1]-col_pt[1])**2)
            return True,col_pt,dist
    else:
        return False,0 #0 added to return a tulpe

这是一个屏幕截图,显示一些光线在明显应该与蓝色障碍物或墙壁碰撞时不会碰撞:

Screenshot

1 个答案:

答案 0 :(得分:1)

您的功能有缺陷 - 像X1 +=0.1这样的碎片会导致奇怪的行为(垂直段的近端很明显)。使用一些强大的实现,如this simple one
(Alexander Hristov。Java但很容易理解)。

/**
 * Computes the intersection between two segments. 
 * @param x1 Starting point of Segment 1
 * @param y1 Starting point of Segment 1
 * @param x2 Ending point of Segment 1
 * @param y2 Ending point of Segment 1
 * @param x3 Starting point of Segment 2
 * @param y3 Starting point of Segment 2
 * @param x4 Ending point of Segment 2
 * @param y4 Ending point of Segment 2
 * @return Point where the segments intersect, or null if they don't
 */
public Point intersection(
    int x1,int y1,int x2,int y2, 
    int x3, int y3, int x4,int y4
) {
    int d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
    if (d == 0) return null;

    int xi = ((x3-x4)*(x1*y2-y1*x2)-(x1-x2)*(x3*y4-y3*x4))/d;
    int yi = ((y3-y4)*(x1*y2-y1*x2)-(y1-y2)*(x3*y4-y3*x4))/d;

    Point p = new Point(xi,yi);
    if (xi < Math.min(x1,x2) || xi > Math.max(x1,x2)) return null;
    if (xi < Math.min(x3,x4) || xi > Math.max(x3,x4)) return null;
    return p;
}

SO的另一个very long discussion

注意有一些特殊的(更有效的)方法,用于查找与轴对齐框(Line clipping)边缘的线的交点。我推荐Liang-Barski one