PyGame 碰撞系统仅每隔一段时间工作一次
PyGame collision system working only every other time
当我注意到一个奇怪的错误时,我正试图为我的简单游戏制作一个有效的碰撞系统。有一个随机位置的矩形,以恒定的速度向上移动。然后还有一个位置随机的球,但它可以用箭头移动。当球击中矩形时,它应该与矩形一起上升,就好像矩形已经“接住球”一样,而当球离开矩形时,它应该会再次下降。
这是我的代码:
import time
import random
import pygame
from pygame.locals import *
pygame.init()
# Window size.
screen_width = 800
screen_height = 400
# Rectangle size and position.
position_x = random.randint(0, screen_width-150)
position_y = random.randint(0, screen_height-50)
r_width = random.randint(150, (screen_width - position_x))
r_height = 10
rect_gap = 50
velocity_r = 1
# Ball properties.
radius = 10
xcoor = random.randint((radius + 2), screen_width-r_width)
ycoor = 20
gravity = 1
velocity_b = 1
# colors
white = (255, 255, 255,)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
light_blue = (78, 231, 245)
dark_green = (37, 125, 0)
# Window.
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Keep Falling")
screen.fill(light_blue)
# Class for generating the Ball.
class Ball:
def __init__(self, screen, color, xcoor, ycoor, radius, ):
self.screen = screen
self.color = color
self.xcoor = xcoor
self.ycoor = ycoor
self.radius = radius
def draw(self, color, xcoor, ycoor, radius):
pygame.draw.circle(screen, color, (xcoor, ycoor), radius)
# Class for generating the
class Rectangle:
def __init__(self, screen, color, position_x, position_y, r_width, r_height):
self.screen = screen
self.color = color
self.position_x = position_x
self.position_y = position_y
self.r_width = r_width
self.r_height = r_height
def draw(self, color, position_x, position_y, r_width, r_height):
pygame.draw.rect(screen, dark_green, (position_x,
position_y, r_width, r_height))
# game loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(light_blue)
time.sleep(0.005)
# Upward movement of the rectangle.
position_y -= velocity_r
# Teleportation of the triangle to the bottom.
if position_y < 0 - r_height:
position_y = screen_height+radius
key = pygame.key.get_pressed()
# Ball controls and collisions with the rectangle from the sides.
if key[pygame.K_LEFT]:
xcoor -= velocity_b
if xcoor < 0:
xcoor = screen_width
if ycoor > position_y and ycoor < (position_y + r_height) and xcoor == (position_x + r_width + radius):
xcoor += velocity_b
if key[pygame.K_RIGHT]:
xcoor += velocity_b
if xcoor > screen_width:
xcoor = 0
if ycoor > position_y and ycoor < (position_y + r_height) and xcoor == (position_x - radius):
xcoor -= velocity_b
# Teleporation of the ball to the top.
if ycoor > screen_height:
ycoor = 0
# Collision system.
if ycoor == (position_y+r_height+radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
ycoor += velocity_b
if ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if ycoor == (position_y - radius) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
# Falling of the ball.
ycoor += gravity
# Ball and rectangle display.
Rectangle.draw(screen, dark_green, position_x, position_y, r_width, r_height)
Ball.draw(screen, white, xcoor, ycoor, radius)
pygame.display.update()
pygame.quit()
我的碰撞系统是基于球和矩形的坐标,如下所示:
# Collision system.
if ycoor == (position_y+r_height+radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
ycoor += velocity_b
if ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if ycoor == (position_y - radius) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
由于未知原因,在这种情况下,球只会每隔一次被“接住”,否则它会落入三角形。
我想出了一个同样基于坐标的替代方案
while ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
使用此方法也会导致错误
- 球在第一次与三角形碰撞时掉落,从第二个矩形开始,碰撞正常。
- 当球离开矩形后,它不会再次开始下落。在循环后添加
gravity = 1
会导致球根本不与矩形碰撞。
是否存在错误或我的代码在逻辑上有缺陷,我应该重做整个碰撞检测系统?
感谢您的任何建议。
问题是,矩形每帧移动一次,而球每帧移动一次。结果,球的底部并不总是准确地击中矩形的顶部。有时条件ycoor == (position_y - radius)
不满足。
您必须评估球的底部是否在矩形顶部的“范围内”:
if ycoor == (position_y - radius) and ...
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and ...
例如:
while run:
# [...]
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
无论如何,我建议使用 pygame.Rect
object and either .collidepoint()
or colliderect()
来查找矩形和对象之间的碰撞。
rect1 = pygame.Rect(x1, y1, w1, h1)
rect2 = pygame.Rect(x2, y2, w2, h2)
if rect1.colliderect(rect2):
# [...]
例如:
run = True
while run:
# [...]
# Teleporation of the ball to the top.
if ycoor > screen_height:
ycoor = 0
rect_rect = pygame.Rect(position_x, position_y, r_width, r_height)
ball_rect = pygame.Rect(xcoor-radius, ycoor-radius, radius*2, radius*2)
if rect_rect.colliderect(ball_rect):
ycoor = position_y - radius
else:
# Falling of the ball.
ycoor += gravity
# Ball and rectangle display.
# [...]
当我注意到一个奇怪的错误时,我正试图为我的简单游戏制作一个有效的碰撞系统。有一个随机位置的矩形,以恒定的速度向上移动。然后还有一个位置随机的球,但它可以用箭头移动。当球击中矩形时,它应该与矩形一起上升,就好像矩形已经“接住球”一样,而当球离开矩形时,它应该会再次下降。
这是我的代码:
import time
import random
import pygame
from pygame.locals import *
pygame.init()
# Window size.
screen_width = 800
screen_height = 400
# Rectangle size and position.
position_x = random.randint(0, screen_width-150)
position_y = random.randint(0, screen_height-50)
r_width = random.randint(150, (screen_width - position_x))
r_height = 10
rect_gap = 50
velocity_r = 1
# Ball properties.
radius = 10
xcoor = random.randint((radius + 2), screen_width-r_width)
ycoor = 20
gravity = 1
velocity_b = 1
# colors
white = (255, 255, 255,)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
light_blue = (78, 231, 245)
dark_green = (37, 125, 0)
# Window.
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Keep Falling")
screen.fill(light_blue)
# Class for generating the Ball.
class Ball:
def __init__(self, screen, color, xcoor, ycoor, radius, ):
self.screen = screen
self.color = color
self.xcoor = xcoor
self.ycoor = ycoor
self.radius = radius
def draw(self, color, xcoor, ycoor, radius):
pygame.draw.circle(screen, color, (xcoor, ycoor), radius)
# Class for generating the
class Rectangle:
def __init__(self, screen, color, position_x, position_y, r_width, r_height):
self.screen = screen
self.color = color
self.position_x = position_x
self.position_y = position_y
self.r_width = r_width
self.r_height = r_height
def draw(self, color, position_x, position_y, r_width, r_height):
pygame.draw.rect(screen, dark_green, (position_x,
position_y, r_width, r_height))
# game loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(light_blue)
time.sleep(0.005)
# Upward movement of the rectangle.
position_y -= velocity_r
# Teleportation of the triangle to the bottom.
if position_y < 0 - r_height:
position_y = screen_height+radius
key = pygame.key.get_pressed()
# Ball controls and collisions with the rectangle from the sides.
if key[pygame.K_LEFT]:
xcoor -= velocity_b
if xcoor < 0:
xcoor = screen_width
if ycoor > position_y and ycoor < (position_y + r_height) and xcoor == (position_x + r_width + radius):
xcoor += velocity_b
if key[pygame.K_RIGHT]:
xcoor += velocity_b
if xcoor > screen_width:
xcoor = 0
if ycoor > position_y and ycoor < (position_y + r_height) and xcoor == (position_x - radius):
xcoor -= velocity_b
# Teleporation of the ball to the top.
if ycoor > screen_height:
ycoor = 0
# Collision system.
if ycoor == (position_y+r_height+radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
ycoor += velocity_b
if ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if ycoor == (position_y - radius) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
# Falling of the ball.
ycoor += gravity
# Ball and rectangle display.
Rectangle.draw(screen, dark_green, position_x, position_y, r_width, r_height)
Ball.draw(screen, white, xcoor, ycoor, radius)
pygame.display.update()
pygame.quit()
我的碰撞系统是基于球和矩形的坐标,如下所示:
# Collision system.
if ycoor == (position_y+r_height+radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
ycoor += velocity_b
if ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if ycoor == (position_y - radius) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
由于未知原因,在这种情况下,球只会每隔一次被“接住”,否则它会落入三角形。
我想出了一个同样基于坐标的替代方案
while ycoor == (position_y - radius) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
使用此方法也会导致错误
- 球在第一次与三角形碰撞时掉落,从第二个矩形开始,碰撞正常。
- 当球离开矩形后,它不会再次开始下落。在循环后添加
gravity = 1
会导致球根本不与矩形碰撞。
是否存在错误或我的代码在逻辑上有缺陷,我应该重做整个碰撞检测系统?
感谢您的任何建议。
问题是,矩形每帧移动一次,而球每帧移动一次。结果,球的底部并不总是准确地击中矩形的顶部。有时条件ycoor == (position_y - radius)
不满足。
您必须评估球的底部是否在矩形顶部的“范围内”:
if ycoor == (position_y - radius) and ...
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and ...
例如:
while run:
# [...]
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and xcoor >= position_x and xcoor <= (position_x + r_width):
gravity = 0
ycoor -= velocity_r
if ycoor < 0:
ycoor = screen_height
if (position_y - radius - 1) <= ycoor <= (position_y - radius + 1) and xcoor <= position_x or xcoor >= (position_x + r_width):
gravity = 1
无论如何,我建议使用 pygame.Rect
object and either .collidepoint()
or colliderect()
来查找矩形和对象之间的碰撞。
rect1 = pygame.Rect(x1, y1, w1, h1)
rect2 = pygame.Rect(x2, y2, w2, h2)
if rect1.colliderect(rect2):
# [...]
例如:
run = True
while run:
# [...]
# Teleporation of the ball to the top.
if ycoor > screen_height:
ycoor = 0
rect_rect = pygame.Rect(position_x, position_y, r_width, r_height)
ball_rect = pygame.Rect(xcoor-radius, ycoor-radius, radius*2, radius*2)
if rect_rect.colliderect(ball_rect):
ycoor = position_y - radius
else:
# Falling of the ball.
ycoor += gravity
# Ball and rectangle display.
# [...]