我正在制作一个 Covid 模拟器,需要我的球随机移动。但是,我希望它们保持沿着它们选择的第一个随机方向移动,并且只有在撞到另一个单元或撞到墙壁时才会改变方向。
我当前的代码如下:
import random import pygame # --- constants --- (UPPER_CASE_NAMES) GREEN1 = (0, 255, 0) # Healthy cells RED = (255, 0, 0) # Infected cells GREEN2 = (0, 100, 0) # Healthy cells not susecptible BLACK = (0, 0, 0) # Dead cells WHITE = (255, 255, 255) BACKGROUND_COLOR = (225, 198, 153) SCREEN_SIZE = (800, 800) # --- classes --- (CamelCaseNames) # class keeep only one cell so it should has name `Cell` instead of `Cells` class Cell(pygame.sprite.Sprite): def __init__(self, color, speed, width, height): super().__init__() self.color = color self.speed = speed self.image = pygame.Surface([width, height]) self.image.fill(WHITE) self.image.set_colorkey(WHITE) self.radius = width // 2 # 25 center = [width // 2, height // 2] pygame.draw.circle(self.image, self.color, center, self.radius, width=0) self.rect = self.image.get_rect() self.rect.x = random.randint(0, 400) self.rect.y = random.randint(50, 700) self.pos = pygame.math.Vector2(self.rect.center) self.dir = pygame.math.Vector2(1, 0).rotate(random.randrange(360)) def update(self): self.pos += self.dir * self.speed if self.pos.x - self.radius < 0 or self.pos.x + self.radius > SCREEN_SIZE[0]: self.dir.x *= -1 if self.pos.y - self.radius < 0 or self.pos.y + self.radius > SCREEN_SIZE[1]: self.dir.y *= -1 for other_cell in all_cells: if all_cells != self: distance_vec = self.pos - other_cell.pos if 0 < distance_vec.length_squared() < (self.radius * 2) ** 2: self.dir.reflect_ip(distance_vec) other_cell.dir.reflect_ip(distance_vec) self.rect.centerx = round(self.pos.x) self.rect.centery = round(self.pos.y) # --- functions --- (lower_case_names) # empty # --- main --- (lower_case_names) pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE) pygame.display.set_caption("Covid-19 Simualtion") speed = [0.5, -0.5] # - objects - all_cells = pygame.sprite.Group() # PEP8: lower_case_name for _ in range(5): cell = Cell(GREEN1, 5, 10, 10) # PEP8: lower_case_name all_cells.add(cell) # - loop - clock = pygame.time.Clock() end = False while not end: # - events - for event in pygame.event.get(): if event.type == pygame.QUIT: end = True # - upadates (without draws) - all_cells.update() # - draws (without updates) - screen.fill(BACKGROUND_COLOR) pygame.draw.rect(screen, BLACK, (0, 50, 400, 700), 3) all_cells.draw(screen) pygame.display.flip() clock.tick(30) # to use less CPU # - end pygame.quit() # some system may need it to close window
我想知道是否有办法做到这一点,非常感谢任何帮助。提前致谢。我尝试将下面的第一个答案复制到我的 pycharm 中,但没有成功,所以现在我把我的完整代码放在上面,而不是只放一个部分,以备不时之需。抱歉之前没有放上所有内容
我建议使用pygame.math.Vector2。将属性pos和dir(位置和方向)添加到类中Cell。使用属性的中心设置位置rect并生成具有随机方向的向量:
pygame.math.Vector2
pos
dir
Cell
rect
class Cell(pygame.sprite.Sprite): def __init__(self, color, speed, width, height): # [...] self.pos = pygame.math.Vector2(self.rect.center) self.dir = pygame.math.Vector2(1, 0).rotate(random.randrange(360))
使用方法 改变位置update。将方向向量dir与的乘积添加speed到位置 ( )。通过对位置向量pos进行四舍五入 ( ) 来更新矩形的位置:round
update
speed
round
class Cell(pygame.sprite.Sprite): # [...] def update(self): self.pos += self.dir * self.speed self.rect.centerx = round(self.pos.x) self.rect.centery = round(self.pos.y)
并将建议应用到您的代码中:
class Cell(pygame.sprite.Sprite): # [...] def update(self): self.pos += self.dir * self.speed if self.pos.x - self.radius < 0: self.pos.x = self.radius self.dir.x = abs(self.dir.x) elif self.pos.x + self.radius > 400: self.pos.x = 400 - self.radius self.dir.x = -abs(self.dir.x) if self.pos.y - self.radius < 50: self.pos.y = 50 + self.radius self.dir.y = abs(self.dir.y) elif self.pos.y + self.radius > 700: self.pos.y = 700 - self.radius self.dir.y = -abs(self.dir.y) self.rect.centerx = round(self.pos.x) self.rect.centery = round(self.pos.y)
测试单元格是否发生碰撞,并在检测到碰撞时反映方向向量:
class Cell(pygame.sprite.Sprite): # [...] def update(self): self.pos += self.dir * self.speed # [...] for other_cell in all_cells: if all_cells != self: distance_vec = self.pos - other_cell.pos if 0 < distance_vec.length_squared() < (self.radius*2) ** 2: self.dir.reflect_ip(distance_vec) other_cell.dir.reflect_ip(distance_vec) self.rect.centerx = round(self.pos.x) self.rect.centery = round(self.pos.y)
另请参阅碰撞与相交 - 圆与圆
完整示例:
import random import pygame class Particle(pygame.sprite.Sprite): def __init__(self, hue, pos, radius, dir, vel): super().__init__() self.pos = pygame.math.Vector2(pos) self.dir = pygame.math.Vector2(dir) self.vel = vel self.radius = radius self.rect = pygame.Rect(round(self.pos.x - radius), round(self.pos.y - radius), radius*2, radius*2) self.image = pygame.Surface((radius*2, radius*2)) self.changeColor(hue) def changeColor(self, hue): self.hue = hue color = pygame.Color(0) color.hsla = (self.hue, 100, 50, 100) self.image.set_colorkey((0, 0, 0)) self.image.fill(0) pygame.draw.circle(self.image, color, (self.radius, self.radius), self.radius) def move(self): self.pos += self.dir * self.vel def update(self, border_rect): if self.pos.x - self.radius < border_rect.left: self.pos.x = border_rect.left + self.radius self.dir.x = abs(self.dir.x) elif self.pos.x + self.radius > border_rect.right: self.pos.x = border_rect.right - self.radius self.dir.x = -abs(self.dir.x) if self.pos.y - self.radius < border_rect.top: self.pos.y = border_rect.top + self.radius self.dir.y = abs(self.dir.y) elif self.pos.y + self.radius > border_rect.bottom: self.pos.y = border_rect.bottom - self.radius self.dir.y = -abs(self.dir.y) self.rect = self.image.get_rect(center = (round(self.pos.x), round(self.pos.y))) pygame.init() window = pygame.display.set_mode((400, 400)) clock = pygame.time.Clock() rect_area = window.get_rect().inflate(-40, -40) all_particles = pygame.sprite.Group() radius, velocity = 5, 1 pos_rect = rect_area.inflate(-radius * 2, -radius * 2) run = True while run: clock.tick(40) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False if len(all_particles.sprites()) < 100: hue = random.randrange(360) x = random.randrange(pos_rect.left, pos_rect.right) y = random.randrange(pos_rect.top, pos_rect.bottom) dir = pygame.math.Vector2(1, 0).rotate(random.randrange(360)) particle = Particle(hue, (x, y), radius, dir, velocity) if not pygame.sprite.spritecollide(particle, all_particles, False, collided = pygame.sprite.collide_circle): all_particles.add(particle) for particle in all_particles: particle.move() particle_list = all_particles.sprites() for i, particle_1 in enumerate(particle_list): for particle_2 in particle_list[i:]: distance_vec = particle_1.pos - particle_2.pos if 0 < distance_vec.length_squared() < (particle_1.radius + particle_2.radius) ** 2: particle_1.dir.reflect_ip(distance_vec) particle_2.dir.reflect_ip(distance_vec) if abs(particle_1.hue - particle_2.hue) <= 180: hue = (particle_1.hue + particle_2.hue) // 2 else: hue = (particle_1.hue + particle_2.hue + 360) // 2 % 360 particle_1.changeColor(hue) particle_2.changeColor(hue) break all_particles.update(rect_area) window.fill(0) pygame.draw.rect(window, (255, 0, 0), rect_area, 3) all_particles.draw(window) pygame.display.flip() pygame.quit() exit()