我目前正在尝试在发生碰撞时在屏幕上打印,但不知道如何仅在1个班级上进行打印。我知道如何使2个对象从不同的类中发生碰撞,但是我不知道如何对具有1000个不同对象的一个类进行处理
我尝试在游戏pygame中使用一些功能,例如pygame.rect.contain,但我不知道从那里去哪里。 感谢您对我的帮助。
下面列出了代码:
import pygame
import random
import sys
import time
Height = 800
Width = 800
Steps = 0
running = True
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
pygame.init()
display = pygame.display.set_mode((Height, Width))
clock = pygame.time.Clock()
pygame.display.set_caption("Game")
class Ball(object):
def __init__(self, x, y, delta_x, delta_y):
self.x = x
self.y = y
self.delta_x = delta_x
self.delta_y = delta_y
def draw(self):
pygame.draw.rect(display, WHITE, (self.x, self.y, 1, 1))
def update(self):
self.x += self.delta_x
self.y += self.delta_y
if self.x < 0:
self.delta_x = self.delta_x * -1
if self.x > Width - 5:
self.delta_x = self.delta_x * -1
if self.y < 0:
self.delta_y = self.delta_y * -1
if self.y > Height - 5:
self.delta_y = self.delta_y * -1
list = []
for i in range(1000):
ball = Ball(random.randrange(0, Width - 5), random.randrange(0, Height - 5),
random.randint(-10, 10), random.randint(-10, 10))
list.append(ball)
while running:
display.fill(BLACK)
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
# Update
# Draw
for ball in list:
ball.draw()
ball.update()
pygame.display.update()
pygame.display.update()
print(clock.tick(60))
答案 0 :(得分:1)
如果要找到相等的点,则必须比较这些点的x和y坐标。
例如
for i in range(len(list)):
eq_list = [j for j in range(len(list)) if i != j and list[i].x == list[j].x and list[i].y == list[j].y]
例如,您可以使用这种方法绘制读取时的碰撞点:
class Ball(object):
# [...]
def draw(self, color):
pygame.draw.rect(display, color, (self.x, self.y, 1, 1))
for i in range(len(list)):
ball = list[i]
collide = any([j for j in range(len(list)) if i != j and list[i].x == list[j].x and list[i].y == list[j].y])
ball.draw(RED if collide else WHITE)
ball.update()
请注意,这种方法在1000点上比较慢,因此需要进行1000 * 1000碰撞测试。
更好的解决方案是使用1000x1000字段并在该字段中设置状态(如果有对象)。不管是否设置了字段中的状态,这都会将碰撞测试简化为某种测试。
创建一个具有布尔状态的字段:
obj_grid = [[False for i in range(Height)] for j in range(Width)]
获取对象位置的列表,并在字段中设置相应的状态。将字段传递给Ball.update
方法。更新和绘制后,由于位置已更改,必须清除字段。为此,请使用职位列表,因为这比“清除”整个字段要快得多。我添加了Som边界检查,因为您的某些对象似乎超出范围(可能也已修复了该错误)。
# collect the postions of the items
poslist = [(ball.x, ball.y) for ball in list]
# set the positions in the field
for pos in poslist:
if pos[0] < Width and pos[1] < Height:
obj_grid[pos[0]][pos[1]] = True
# update and draw
for ball in list:
ball.update(obj_grid)
ball.draw()
# set the positions in the field
for pos in poslist:
if pos[0] < Width and pos[1] < Height:
obj_grid[pos[0]][pos[1]] = False
在Ball.update
方法中,必须测试路径挂起方式上的位置。如果声明了任何字段,则对象发生碰撞。我将状态存储在属性self.collide
中。如果设置了状态,则将对象放大并涂成红色,以使碰撞可视化。当然,您也可以执行其他操作,例如更改方向:
class Ball(object):
def __init__(self, x, y, delta_x, delta_y):
self.x = x
self.y = y
self.delta_x = delta_x
self.delta_y = delta_y
self.collide = False
def draw(self):
color, size = (RED, 5) if self.collide else (WHITE, 1)
pygame.draw.rect(display, color, (self.x, self.y, size, size))
def update(self, obj_grid):
# check if the object is colliding on his way
pos = [self.x, self.y]
new_pos = [self.x + self.delta_x, self.y + self.delta_y]
self.collide = False
while not self.collide and pos != new_pos:
if abs(pos[0]-new_pos[0]) > abs(pos[1]-new_pos[1]):
pos[0] += 1 if self.delta_x > 0 else -1
else:
pos[1] += 1 if self.delta_y > 0 else -1
self.collide = pos[0] < Width and pos[1] < Height and obj_grid[pos[0]][pos[1]]
self.x += self.delta_x
self.y += self.delta_y
if self.x < 0:
self.delta_x = self.delta_x * -1
if self.x > Width - 5:
self.delta_x = self.delta_x * -1
if self.y < 0:
self.delta_y = self.delta_y * -1
if self.y > Height - 5:
self.delta_y = self.delta_y * -1
答案 1 :(得分:1)
已更新:在修复了较早版本中的一个会大大降低其速度的错误之后,我走得更远,并实施了许多其他改进/优化,因为这似乎非常可行技术。
在这里,一种方法的可运行实现似乎很有效。为了避免在每次迭代中比较每对球的位置(对1,000个球进行近1,000,000次比较),它采用了将屏幕细分为M x N个分区或“箱”并将每个球“分类”到其中的策略根据其在屏幕上的当前位置。由于只涉及一些相对简单的计算,因此可以以非常廉价的方式对它们进行有效排序。
一旦完成,仅需要将每个仓中的成对球相互比较,这会更快,因为每个仓仅容纳所有这些球的一个子集。这样做需要权衡取舍,因为,尽管您创建了更多的料箱-因此每个料箱中的球数量较少-它也增加了需要处理的料箱数。
碰撞球的颜色首先更改为指定的颜色,以在后续步骤中将其标记为删除-该颜色实际上并没有用于绘制任何内容-仅是一种简单的标记方法。我这样做是为了避免在检测到碰撞时将打印速度减慢太多的打印。
在下面的示例代码中,有8 x 8 = 64个bin,假设随机分布,每个容器最初平均仅包含1000/64(15.625)个球。
结果似乎运行很快。
from itertools import combinations
from copy import deepcopy
from math import sqrt
import pygame
from random import randint
BLACK = 0, 0, 0
WHITE = 255, 255, 255
RED = 255, 0, 0
GREEN = 0, 255, 0
BLUE = 0, 0, 255
NUM_BALLS = 1000
MARKED = RED # Color used to indicte ball collided.
WIDTH, HEIGHT = 800, 800
M, N = 8, 8 # Number of screen sub-divisions in each dimension.
MARGIN = 5 # Size of space around edges.
MAX_SPEED = 10
MAX_DELTA = round(sqrt(2 * MAX_SPEED**2))
MAX_DELTAX_X, MAX_DELTAX_Y = MAX_DELTA, MAX_DELTA
MAX_X, MAX_Y = WIDTH-MARGIN, HEIGHT-MARGIN
EMPTY_BINS = [[[] for i in range(M)] for j in range(N)]
WM, WN = WIDTH // M, HEIGHT // N # Dimensions of each sub-division.
class Ball(object):
def __init__(self, x, y, delta_x, delta_y, color=WHITE):
self.x, self.y = x, y
self.delta_x, self.delta_y = delta_x, delta_y
self.color = color
def draw(self, display):
# Using Surface.fill() can be faster than pygame.draw.rect().
display.fill(self.color, (self.x, self.y, 1, 1))
def update(self):
self.x += self.delta_x
self.y += self.delta_y
if self.x < 0:
self.x = 0
self.delta_x = -self.delta_x
elif self.x > MAX_X:
self.x = MAX_X
self.delta_x = -self.delta_x
if self.y < 0:
self.y = 0
self.delta_y = -self.delta_y
elif self.y > MAX_Y:
self.y = MAX_Y
self.delta_y = -self.delta_y
def classify(balls):
""" Sort balls in bins. """
bins = deepcopy(EMPTY_BINS)
for ball in balls:
m, n = ball.x // WM, ball.y // WN
try:
bins[m][n].append(ball)
except IndexError:
raise IndexError(f'bins[{m}][{n}] -> {ball.x}, {ball.y}')
return bins
def detect_collisions(balls):
""" Find all colliding balls and return whether any were found.
"""
bins = classify(balls) # Separate balls into bins.
collisions = False
for m in range(M):
for n in range(N):
if bins[m][n]: # Non-empty?
for a, b in (pair for pair in combinations(bins[m][n], 2)):
if(a.x == b.x and a.y == b.y and (a.color != MARKED or
b.color != MARKED)):
a.color = b.color = MARKED
collisions = True
return collisions
def main():
pygame.init()
display = pygame.display.set_mode((HEIGHT, WIDTH))
clock = pygame.time.Clock()
pygame.display.set_caption("Game")
balls = [
Ball(randint(MARGIN, MAX_X), randint(MARGIN, MAX_Y),
randint(-MAX_DELTAX_X, MAX_DELTAX_X), randint(-MAX_DELTAX_Y, MAX_DELTAX_Y))
for _ in range(NUM_BALLS)
]
# Main loop.
remove_collisions = False # No collisions first iteration.
while len(balls):
display.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
# Remove any collisions found.
if remove_collisions:
balls[:] = [ball for ball in balls if ball.color != MARKED]
# Update display.
for ball in balls:
ball.draw(display)
ball.update()
# Check after ball updates.
remove_collisions = detect_collisions(balls)
pygame.display.flip()
clock.tick(60)
main()