所以我的问题很简单:我如何制作一个鼠标无法穿过的精灵?我一直在尝试,发现一种不可靠的方法也很容易出错。如果有人知道我可能会怎么做,请帮助。

这是我当前正在使用的代码:

import pygame
import pyautogui
import sys
import time

pygame.init()
game_display = pygame.display.set_mode((800,600))
pygame.mouse.set_visible(True)
pygame.event.set_grab(True)
exit = False

class Wall(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((30, 100))
        self.image.fill((255, 255, 255))
        self.rect = self.image.get_rect()
        self.rect.center = (200, 200)

    def collision(self):
        loc = pygame.mouse.get_pos()
        yy = loc[1]
        xx = loc[0]
        if yy >= self.rect.top and yy <= self.rect.bottom and xx >= self.rect.left and xx <= self.rect.right:
            if xx >= 200:
                pyautogui.move(216 - xx, 0)
            if xx <= 200:
                pyautogui.move(-xx + 184, 0)

w = Wall()
all_sprites = pygame.sprite.Group()
all_sprites.add(w)
print(w.rect.top)
print(w.rect.bottom)
while (not exit):
    mouse_move = (0,0)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                exit = True
    w.collision()
    clock = pygame.time.Clock()
    game_display.fill((0, 0, 0))
    clock.tick(30)
    all_sprites.update()
    all_sprites.draw(game_display)
    pygame.display.flip()
pygame.quit()


注意:请忽略我多余的导入语句,我将在以后使用它们。

最佳答案

要执行您想做的事情,您必须检查从先前的鼠标位置到新的鼠标位置的直线与矩形是否相交。编写一个函数IntersectLineRec,以检查相交并使用该交点,并返回按距离排序的交点列表。
该函数返回一个带点和距离的风琴列表:

例如

[((215.0, 177.0), 12.0), ((185.0, 177.0), 42.0)]


prev_loc = pygame.mouse.get_pos()

class Wall(pygame.sprite.Sprite):

    # [...]

    def collision(self):

        global prev_loc

        loc = pygame.mouse.get_pos()
        intersect = IntersectLineRec(prev_loc, loc, self.rect)
        prev_loc = loc

        if intersect:
            ip = [*intersect[0][0]]
            for i in range(2):
                tp = self.rect.center[i] if ip[i] == loc[i] else loc[i]
                ip[i] += -3 if ip[i] < tp else 3
            pyautogui.move(ip[0]-loc[0], ip[1]-loc[1])
            prev_loc = loc = ip


函数IntersectLineRec必须检查矩形的4个角之间的4条外线之一与鼠标位置之间的线是否相交:

def IntersectLineRec(p1, p2, rect):
    iL = [
        IntersectLineLine(p1, p2, rect.bottomleft, rect.bottomright),
        IntersectLineLine(p1, p2, rect.bottomright, rect.topright),
        IntersectLineLine(p1, p2, rect.topright, rect.topleft),
        IntersectLineLine(p1, p2, rect.topleft, rect.bottomleft) ]
    iDist = [(i[1], pygame.math.Vector2(i[1][0] - p1[0], i[1][1] - p1[1]).length()) for i in iL if i[0]]
    iDist.sort(key=lambda t: t[1])
    return iDist


IntersectLineRec检查是否由点定义的无尽线相交。然后,它检查相交点是否在每条线定义的矩形中(线是矩形的对角线):

def IntersectLineLine(l1_p1, l1_p2, l2_p1, l2_p2):
    isect, xPt = IntersectEndlessLineLine(l1_p1, l1_p2, l2_p1, l2_p2)
    isect = isect and PtInRect(xPt, l1_p1, l1_p2) and PtInRect(xPt, l2_p1, l2_p2)
    return isect, xPt


要检查点是否在轴线对齐的矩形中,必须检查该点的两个坐标是否都在矩形坐标的范围内:

def InRange(coord, range_s, range_e):
    if range_s < range_e:
        return coord >= range_s and coord <= range_e
    return coord >= range_e and coord <= range_s

def PtInRect(pt, lp1, lp2):
    return InRange(pt[0], lp1[0], lp2[0]) and InRange(pt[1], lp1[1], lp2[1])


到无尽线的交点可以这样计算:

python - 使光标无法在 Sprite pygame中移动-LMLPHP

def IntersectEndlessLineLine(l1_p1, l1_p2, l2_p1, l2_p2):

    # calculate the line vectors and test if both lengths are > 0
    P = pygame.math.Vector2(*l1_p1)
    Q = pygame.math.Vector2(*l2_p1)
    line1 = pygame.math.Vector2(*l1_p2) - P
    line2 = pygame.math.Vector2(*l2_p2) - Q
    if line1.length() == 0 or line2.length() == 0:
        return (False, (0, 0))

    # check if the lines are not parallel
    R, S = (line1.normalize(), line2.normalize())
    dot_R_nvS = R.dot(pygame.math.Vector2(S[1], -S[0]))
    if abs(dot_R_nvS) < 0.001:
        return (False, (0, 0))

    # calculate the intersection point of the lines
    # t  =  dot(Q-P, (S.y, -S.x)) / dot(R, (S.y, -S.x))
    # X  =  P + R * t
    ptVec = Q-P
    t = ptVec.dot(pygame.math.Vector2(S[1], -S[0])) / dot_R_nvS
    xPt = P + R * t
    return (True, (xPt[0], xPt[1]))


看动画:

python - 使光标无法在 Sprite pygame中移动-LMLPHP

10-07 18:25