在 Python Pygame 中检测线条和图像之间的碰撞
Detecting Collisions Between Line and Image in Python Pygame
我正在 python pygame 中重新创建 Fruit Ninja 游戏。到目前为止,我已经添加了切片功能,它是从鼠标第一次按下时的位置到鼠标按下并移动时的位置绘制的一条线。现在我正致力于检测水果和水果切片线之间的碰撞。我正在尝试为此使用 y=mx+b 方程。使用直线的起点和终点,我计算斜率 (m) 并使用斜率计算 b。最后,我检查线是否穿过水果的坐标。在尝试实施之后,我遇到了一个问题。尝试切片水果时,未检测到碰撞。我想我知道是什么导致了这个问题:水果的宽度和高度如何?毕竟,我只是在检查直线是否穿过水果的 x 和 y 位置,尽管水果不仅仅是一个点,而是一个充满点的图像。现在我的问题是:如何检测线是否穿过水果的 image 而不仅仅是它的位置?我不确定如何使用 y=mx+b 来做到这一点。
代码:
import pygame
import random
import os
pygame.init()
WINDOW_SIZE = (550, 550)
window = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Fruit Ninja")
score_font = pygame.font.SysFont("Arial", 30, "bold")
# List of fruit images
fruitsImages = [os.listdir("Fruits/" + path) for path in os.listdir("Fruits")]
# Images
heartImg = pygame.image.load("heart.png")
game_bg = pygame.image.load("BGs/woodenBG.png")
game_bg = pygame.transform.scale(game_bg, WINDOW_SIZE)
menuBG = pygame.image.load("BGs/menuBG.png")
menuBG = pygame.transform.scale(menuBG, WINDOW_SIZE)
gameLogo = pygame.image.load("fruitNinjaLogo.png")
gameLogo = pygame.transform.scale(gameLogo, (318, 197))
bombImg = pygame.image.load("bomb.png")
class Player:
def __init__(self):
self.score = 0
self.lives = 3
self.font = score_font
self.heart = heartImg
def update(self):
window.blit(self.font.render(f"SCORE:{self.score}", True, (255, 255, 255)), (10, 10))
x = 480
for i in range(self.lives):
window.blit(self.heart, (x, 10))
x -= 60
def init(self):
self.score = 0
self.lives = 3
player = Player()
class Fruit:
def __init__(self, fruit, x, y, upSpeed, downSpeed, fruitsList:list):
self.fruit = fruit
self.x = x
self.y = y
self.upSpeed = upSpeed
self.downSpeed = downSpeed
self.fruitsList = fruitsList
def spawn(self):
window.blit(self.fruit, (self.x, self.y))
def throwUp(self):
self.y -= self.upSpeed
def drop(self):
if self.y <= WINDOW_SIZE[1]:
self.y += self.downSpeed
else:
self.fruitsList.remove(self)
def checkIfCut(self, starting_point, ending_point):
try:
m = (starting_point[1] - ending_point[1]) / (starting_point[0] - ending_point[0])
if starting_point[0] < 0:
b = starting_point[1] + starting_point[0] * m
else:
b = starting_point[1] - starting_point[0] * m
if self.y == m * self.x + b:
print("Fruit Sliced")
else:
print("Fruit Not Sliced")
except ZeroDivisionError:
pass
def swipe(starting_point, ending_point):
pygame.draw.line(window, (255, 255, 255), starting_point, ending_point, 5)
def blit_bg():
window.blit(game_bg, (0, 0))
def main():
clock = pygame.time.Clock()
mouseDown = False
fruit = Fruit(pygame.image.load("Fruits/RedApple/redApple.png"), 200, 200, 20, 50, [])
while True:
window.fill((0, 0, 0))
blit_bg()
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseDown = True
starting_point = pygame.mouse.get_pos()
if mouseDown and event.type == pygame.MOUSEMOTION:
ending_point = pygame.mouse.get_pos()
swipe(starting_point, ending_point)
fruit.checkIfCut(starting_point, ending_point)
if event.type == pygame.MOUSEBUTTONUP:
mouseDown = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_q]:
player.init()
menu()
fruit.spawn()
player.update()
pygame.display.update()
def menu():
font = pygame.font.SysFont("cooperblack", 90)
while True:
window.fill((0, 0, 0))
window.blit(menuBG, (0, 0))
window.blit(gameLogo, (120, 80))
playButton = pygame.draw.rect(window, "#ff5c70", (130, 335, 300, 120))
window.blit(font.render("PLAY", True, (0, 0, 0)), (150, 340))
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if playButton.collidepoint(event.pos):
main()
pygame.display.update()
if __name__ == "__main__":
menu()
我希望我说的很清楚。如果有什么不清楚的地方,请告诉我。
在@user16038533的帮助下,问题解决了!他还链接了 this 页面,这有助于我理解他的解决方案。
代码:
import pygame
import random
import os
pygame.init()
WINDOW_SIZE = (550, 550)
window = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Fruit Ninja")
score_font = pygame.font.SysFont("Arial", 30, "bold")
# List of fruit images
fruitsImages = [os.listdir("Fruits/" + path) for path in os.listdir("Fruits")]
# Images
heartImg = pygame.image.load("heart.png")
game_bg = pygame.image.load("BGs/woodenBG.png")
game_bg = pygame.transform.scale(game_bg, WINDOW_SIZE)
menuBG = pygame.image.load("BGs/menuBG.png")
menuBG = pygame.transform.scale(menuBG, WINDOW_SIZE)
gameLogo = pygame.image.load("fruitNinjaLogo.png")
gameLogo = pygame.transform.scale(gameLogo, (318, 197))
bombImg = pygame.image.load("bomb.png")
class Player:
def __init__(self):
self.score = 0
self.lives = 3
self.font = score_font
self.heart = heartImg
def update(self):
window.blit(self.font.render(f"SCORE:{self.score}", True, (255, 255, 255)), (10, 10))
x = 480
for i in range(self.lives):
window.blit(self.heart, (x, 10))
x -= 60
def init(self):
self.score = 0
self.lives = 3
player = Player()
class Fruit:
def __init__(self, fruit, x, y, upSpeed, downSpeed, fruitsList:list):
self.fruit = fruit
self.x = x
self.y = y
self.upSpeed = upSpeed
self.downSpeed = downSpeed
self.fruitsList = fruitsList
self.width, self.height = fruit.get_size()
def spawn(self):
window.blit(self.fruit, (self.x, self.y))
def throwUp(self):
self.y -= self.upSpeed
def drop(self):
if self.y <= WINDOW_SIZE[1]:
self.y += self.downSpeed
else:
self.fruitsList.remove(self)
def checkIfCut(self, starting_point, ending_point):
x1, y1 = starting_point
x2, y2 = ending_point
if self.lineRectIntersecton(x1, y1, x2, y2):
print("Fruit Sliced")
else:
print("Fruit Not Sliced")
def lineRectIntersecton(self, startingX, startingY, endingX, endingY):
left = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x, self.y, self.x,
self.y + self.height)
right = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x + self.width,
self.y, self.x + self.width, self.y + self.height)
top = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x, self.y,
self.x + self.width, self.y)
bottom = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x,
self.y + self.height, self.x + self.width, self.y + self.height)
return left or right or top or bottom
@staticmethod
def lineLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
uAd = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
uBd = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
if uAd != 0 and uBd != 0:
uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / uAd
uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / uBd
return 0 <= uA <= 1 and 0 <= uB <= 1
return False
def swipe(starting_point, ending_point):
pygame.draw.line(window, (255, 255, 255), starting_point, ending_point, 5)
def blit_bg():
window.blit(game_bg, (0, 0))
def main():
clock = pygame.time.Clock()
mouseDown = False
fruit = Fruit(pygame.image.load("Fruits/RedApple/redApple.png"), 200, 200, 20, 50, [])
while True:
window.fill((0, 0, 0))
blit_bg()
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseDown = True
starting_point = pygame.mouse.get_pos()
if mouseDown and event.type == pygame.MOUSEMOTION:
ending_point = pygame.mouse.get_pos()
swipe(starting_point, ending_point)
fruit.checkIfCut(starting_point, ending_point)
if event.type == pygame.MOUSEBUTTONUP:
mouseDown = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_q]:
player.init()
menu()
fruit.spawn()
player.update()
pygame.display.update()
def menu():
font = pygame.font.SysFont("cooperblack", 90)
while True:
window.fill((0, 0, 0))
window.blit(menuBG, (0, 0))
window.blit(gameLogo, (120, 80))
playButton = pygame.draw.rect(window, "#ff5c70", (130, 335, 300, 120))
window.blit(font.render("PLAY", True, (0, 0, 0)), (150, 340))
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if playButton.collidepoint(event.pos):
main()
pygame.display.update()
if __name__ == "__main__":
menu()
我希望这个问题可以帮助其他正在为同样的问题而苦苦挣扎的人。
好吧,当您不知道如何做事时,您 google 就可以了。您可以检查您的线是否与构成矩形的任何线发生碰撞。 Here 是我获得代码的地方。
def lineLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
uAd = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
uBd = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
if uAd != 0 and uBd != 0:
uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / uAd
uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / uBd
return uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1
return False
def lineRectIntersecton(x1, y1, x2, y2, rx, ry, rw, rh):
return lineLineIntersection(x1,y1,x2,y2, rx,ry,rx, ry+rh) \
or lineLineIntersection(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh) \
or lineLineIntersection(x1,y1,x2,y2, rx,ry, rx+rw,ry) \
or lineLineIntersection(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh)
然后您可以将 checkIfCut
函数更改为:
def checkIfCut(self, starting_point, ending_point):
x1, y1 = starting_point
x2, y2 = ending_point
rw, rh = self.fruit.get_size()
return lineRectIntersecton(x1, y1, x2, y2, self.x, self.y, rw, rh)
我正在 python pygame 中重新创建 Fruit Ninja 游戏。到目前为止,我已经添加了切片功能,它是从鼠标第一次按下时的位置到鼠标按下并移动时的位置绘制的一条线。现在我正致力于检测水果和水果切片线之间的碰撞。我正在尝试为此使用 y=mx+b 方程。使用直线的起点和终点,我计算斜率 (m) 并使用斜率计算 b。最后,我检查线是否穿过水果的坐标。在尝试实施之后,我遇到了一个问题。尝试切片水果时,未检测到碰撞。我想我知道是什么导致了这个问题:水果的宽度和高度如何?毕竟,我只是在检查直线是否穿过水果的 x 和 y 位置,尽管水果不仅仅是一个点,而是一个充满点的图像。现在我的问题是:如何检测线是否穿过水果的 image 而不仅仅是它的位置?我不确定如何使用 y=mx+b 来做到这一点。 代码:
import pygame
import random
import os
pygame.init()
WINDOW_SIZE = (550, 550)
window = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Fruit Ninja")
score_font = pygame.font.SysFont("Arial", 30, "bold")
# List of fruit images
fruitsImages = [os.listdir("Fruits/" + path) for path in os.listdir("Fruits")]
# Images
heartImg = pygame.image.load("heart.png")
game_bg = pygame.image.load("BGs/woodenBG.png")
game_bg = pygame.transform.scale(game_bg, WINDOW_SIZE)
menuBG = pygame.image.load("BGs/menuBG.png")
menuBG = pygame.transform.scale(menuBG, WINDOW_SIZE)
gameLogo = pygame.image.load("fruitNinjaLogo.png")
gameLogo = pygame.transform.scale(gameLogo, (318, 197))
bombImg = pygame.image.load("bomb.png")
class Player:
def __init__(self):
self.score = 0
self.lives = 3
self.font = score_font
self.heart = heartImg
def update(self):
window.blit(self.font.render(f"SCORE:{self.score}", True, (255, 255, 255)), (10, 10))
x = 480
for i in range(self.lives):
window.blit(self.heart, (x, 10))
x -= 60
def init(self):
self.score = 0
self.lives = 3
player = Player()
class Fruit:
def __init__(self, fruit, x, y, upSpeed, downSpeed, fruitsList:list):
self.fruit = fruit
self.x = x
self.y = y
self.upSpeed = upSpeed
self.downSpeed = downSpeed
self.fruitsList = fruitsList
def spawn(self):
window.blit(self.fruit, (self.x, self.y))
def throwUp(self):
self.y -= self.upSpeed
def drop(self):
if self.y <= WINDOW_SIZE[1]:
self.y += self.downSpeed
else:
self.fruitsList.remove(self)
def checkIfCut(self, starting_point, ending_point):
try:
m = (starting_point[1] - ending_point[1]) / (starting_point[0] - ending_point[0])
if starting_point[0] < 0:
b = starting_point[1] + starting_point[0] * m
else:
b = starting_point[1] - starting_point[0] * m
if self.y == m * self.x + b:
print("Fruit Sliced")
else:
print("Fruit Not Sliced")
except ZeroDivisionError:
pass
def swipe(starting_point, ending_point):
pygame.draw.line(window, (255, 255, 255), starting_point, ending_point, 5)
def blit_bg():
window.blit(game_bg, (0, 0))
def main():
clock = pygame.time.Clock()
mouseDown = False
fruit = Fruit(pygame.image.load("Fruits/RedApple/redApple.png"), 200, 200, 20, 50, [])
while True:
window.fill((0, 0, 0))
blit_bg()
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseDown = True
starting_point = pygame.mouse.get_pos()
if mouseDown and event.type == pygame.MOUSEMOTION:
ending_point = pygame.mouse.get_pos()
swipe(starting_point, ending_point)
fruit.checkIfCut(starting_point, ending_point)
if event.type == pygame.MOUSEBUTTONUP:
mouseDown = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_q]:
player.init()
menu()
fruit.spawn()
player.update()
pygame.display.update()
def menu():
font = pygame.font.SysFont("cooperblack", 90)
while True:
window.fill((0, 0, 0))
window.blit(menuBG, (0, 0))
window.blit(gameLogo, (120, 80))
playButton = pygame.draw.rect(window, "#ff5c70", (130, 335, 300, 120))
window.blit(font.render("PLAY", True, (0, 0, 0)), (150, 340))
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if playButton.collidepoint(event.pos):
main()
pygame.display.update()
if __name__ == "__main__":
menu()
我希望我说的很清楚。如果有什么不清楚的地方,请告诉我。
在@user16038533的帮助下,问题解决了!他还链接了 this 页面,这有助于我理解他的解决方案。 代码:
import pygame
import random
import os
pygame.init()
WINDOW_SIZE = (550, 550)
window = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Fruit Ninja")
score_font = pygame.font.SysFont("Arial", 30, "bold")
# List of fruit images
fruitsImages = [os.listdir("Fruits/" + path) for path in os.listdir("Fruits")]
# Images
heartImg = pygame.image.load("heart.png")
game_bg = pygame.image.load("BGs/woodenBG.png")
game_bg = pygame.transform.scale(game_bg, WINDOW_SIZE)
menuBG = pygame.image.load("BGs/menuBG.png")
menuBG = pygame.transform.scale(menuBG, WINDOW_SIZE)
gameLogo = pygame.image.load("fruitNinjaLogo.png")
gameLogo = pygame.transform.scale(gameLogo, (318, 197))
bombImg = pygame.image.load("bomb.png")
class Player:
def __init__(self):
self.score = 0
self.lives = 3
self.font = score_font
self.heart = heartImg
def update(self):
window.blit(self.font.render(f"SCORE:{self.score}", True, (255, 255, 255)), (10, 10))
x = 480
for i in range(self.lives):
window.blit(self.heart, (x, 10))
x -= 60
def init(self):
self.score = 0
self.lives = 3
player = Player()
class Fruit:
def __init__(self, fruit, x, y, upSpeed, downSpeed, fruitsList:list):
self.fruit = fruit
self.x = x
self.y = y
self.upSpeed = upSpeed
self.downSpeed = downSpeed
self.fruitsList = fruitsList
self.width, self.height = fruit.get_size()
def spawn(self):
window.blit(self.fruit, (self.x, self.y))
def throwUp(self):
self.y -= self.upSpeed
def drop(self):
if self.y <= WINDOW_SIZE[1]:
self.y += self.downSpeed
else:
self.fruitsList.remove(self)
def checkIfCut(self, starting_point, ending_point):
x1, y1 = starting_point
x2, y2 = ending_point
if self.lineRectIntersecton(x1, y1, x2, y2):
print("Fruit Sliced")
else:
print("Fruit Not Sliced")
def lineRectIntersecton(self, startingX, startingY, endingX, endingY):
left = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x, self.y, self.x,
self.y + self.height)
right = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x + self.width,
self.y, self.x + self.width, self.y + self.height)
top = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x, self.y,
self.x + self.width, self.y)
bottom = self.lineLineIntersection(startingX, startingY, endingX, endingY, self.x,
self.y + self.height, self.x + self.width, self.y + self.height)
return left or right or top or bottom
@staticmethod
def lineLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
uAd = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
uBd = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
if uAd != 0 and uBd != 0:
uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / uAd
uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / uBd
return 0 <= uA <= 1 and 0 <= uB <= 1
return False
def swipe(starting_point, ending_point):
pygame.draw.line(window, (255, 255, 255), starting_point, ending_point, 5)
def blit_bg():
window.blit(game_bg, (0, 0))
def main():
clock = pygame.time.Clock()
mouseDown = False
fruit = Fruit(pygame.image.load("Fruits/RedApple/redApple.png"), 200, 200, 20, 50, [])
while True:
window.fill((0, 0, 0))
blit_bg()
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseDown = True
starting_point = pygame.mouse.get_pos()
if mouseDown and event.type == pygame.MOUSEMOTION:
ending_point = pygame.mouse.get_pos()
swipe(starting_point, ending_point)
fruit.checkIfCut(starting_point, ending_point)
if event.type == pygame.MOUSEBUTTONUP:
mouseDown = False
if event.type == pygame.KEYDOWN:
keys = pygame.key.get_pressed()
if keys[pygame.K_q]:
player.init()
menu()
fruit.spawn()
player.update()
pygame.display.update()
def menu():
font = pygame.font.SysFont("cooperblack", 90)
while True:
window.fill((0, 0, 0))
window.blit(menuBG, (0, 0))
window.blit(gameLogo, (120, 80))
playButton = pygame.draw.rect(window, "#ff5c70", (130, 335, 300, 120))
window.blit(font.render("PLAY", True, (0, 0, 0)), (150, 340))
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if playButton.collidepoint(event.pos):
main()
pygame.display.update()
if __name__ == "__main__":
menu()
我希望这个问题可以帮助其他正在为同样的问题而苦苦挣扎的人。
好吧,当您不知道如何做事时,您 google 就可以了。您可以检查您的线是否与构成矩形的任何线发生碰撞。 Here 是我获得代码的地方。
def lineLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
uAd = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
uBd = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
if uAd != 0 and uBd != 0:
uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / uAd
uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / uBd
return uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1
return False
def lineRectIntersecton(x1, y1, x2, y2, rx, ry, rw, rh):
return lineLineIntersection(x1,y1,x2,y2, rx,ry,rx, ry+rh) \
or lineLineIntersection(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh) \
or lineLineIntersection(x1,y1,x2,y2, rx,ry, rx+rw,ry) \
or lineLineIntersection(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh)
然后您可以将 checkIfCut
函数更改为:
def checkIfCut(self, starting_point, ending_point):
x1, y1 = starting_point
x2, y2 = ending_point
rw, rh = self.fruit.get_size()
return lineRectIntersecton(x1, y1, x2, y2, self.x, self.y, rw, rh)