Я хочу создать игру-змейку с pygame на python, которая подходит для нескольких игроков. вначале я создал классы для змейки, кнопки и змейки … Теперь я хочу создать графический интерфейс с pygame, который поможет пользователю определять свойства игры, например экран настроек для определения таких вещей, как скорость змей, и если змеи будут дисквалифицированы, когда они выходят из экрана, или что они будут выходить с другой стороны от него. Главный экран, содержащий приветственное сообщение и кнопки, которые определяют, сколько игроков будет в игре. Экран, определяющий свойства змей, такие как цвет и ключи. И, конечно же, кнопки на каждом экране, которые позволяют пользователю переключаться между экранами. Но я не уверен, где выполнять все эти экраны, в каком классе? Где это должно быть? Может, в новом классе? Может в одиночных функциях? Где лучше всего реализовать это, сохранив при этом принципы ООП, такие как единственная ответственность, разделение интерфейсов, поддержание высокой сплоченности и низкой связи …? И как я могу всегда знать, где это место? Как я могу хорошо разработать свой код? Любой ответ мне очень поможет!
Это мой код:
class Snake:
drafting_score_mess = "{name} score: {score}"
def __init__(self, name, color: Tuple[int, int, int], speed: float, start_point: Tuple[int, int],
block, keys, disqualification_violation_boundaries=True):
self.name = name
self._color = color
self._speed = 1 / speed
self._last_measure = time.time()
self.disqualification_violation_boundaries = disqualification_violation_boundaries
self.score = 0
self.disqualified = False
self._length = []
self._block = block
self.start_point = start_point
self._x = start_point[0]
self._y = start_point[1]
self._x_cha = 0
self._y_cha = 0
self._d = -1
keys = {k.lower(): v for k, v in keys.items()}
self._l_key = keys["left"]
self._r_key = keys["right"]
self._u_key = keys["up"]
self._d_key = keys["down"]
def update_pos(self, screen: pygame.Surface) -> None:
if not self.disqualified:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[self._l_key] and self._d != 0:
self._x_cha = -self._block
self._y_cha = 0
self._d = 0
elif pressed_keys[self._r_key] and self._d != 0:
self._x_cha = self._block
self._y_cha = 0
self._d = 0
elif pressed_keys[self._u_key] and self._d != 1:
self._y_cha = -self._block
self._x_cha = 0
self._d = 1
elif pressed_keys[self._d_key] and self._d != 1:
self._y_cha = self._block
self._x_cha = 0
self._d = 1
self._x = self._x // 10 * 10
self._y = self._y // 10 * 10
current_measure = time.time()
if current_measure - self._last_measure >= self._speed:
self._x += self._x_cha
self._y += self._y_cha
self._length.append((self._x, self._y))
self._length = self._length[len(self._length) - (self.score + 1):]
self._last_measure = current_measure
for snake_block_pos in self._length:
pygame.draw.rect(screen, self._color,
[snake_block_pos[0], snake_block_pos[1], self._block, self._block])
def dis_score(self, font: pygame.font.Font, pos: Tuple[int, int], screen: pygame.Surface) -> None:
score_mass = font.render(self.drafting_score_mess.format(name=self.name, score=self.score),
True, self._color)
screen.blit(score_mass, pos)
def update_status_if_encountered_himself(self):
if (self._x, self._y) in self._length[:-1]:
self.disqualified = True
def update_status_if_encountered_margins(self, scr_size: Tuple[int, int]):
x, y = scr_size
if self.disqualification_violation_boundaries:
if self._x >= x or self._x < 0 or self._y >= y or self._y < 0:
self.disqualified = True
else:
if self._x > x:
self._x = 0
elif self._x < 0:
self._x = x
elif self._y > y:
self._y = 0
elif self._y < 0:
self._y = y
def get_head_pos(self):
return tuple((self._x, self._y))
def reset(self, start_point: Tuple[int, int] = None):
if start_point is None:
start_point = self.start_point
keys = {"up": self._u_key, "down": self._d_key, "right": self._r_key, "left": self._l_key}
Snake.__init__(self, self.name, self._color, 1 / self._speed, start_point, self._block, keys,
self.disqualification_violation_boundaries)
def get_all_positions(self):
return self._length
@property
def speed(self):
return int(1 / self._speed)
@speed.setter
def speed(self, new_speed):
if not (10 < new_speed < 150):
raise ValueError(f"speed need to be between 10-100 got {new_speed}")
self._speed = 1 / new_speed
def __gt__(self, other) -> bool:
return self.score > other
def __ge__(self, other) -> bool:
return self.score >= other
def __lt__(self, other) -> bool:
return self.score < other
def __le__(self, other) -> bool:
return self.score <= other
def __ne__(self, other) -> bool:
return self.score != other
def __eq__(self, other) -> bool:
return self.score == other
class Button:
def __init__(self, screen, pos_and_size: Tuple[int, int, int, int] or List[int, int, int, int], func_in_pressed,
background: Union[str, bytes, PathLike] or Tuple[int, int, int],
text: AnyStr = None, text_font: pygame.font.Font = None, text_color: Tuple[int, int, int] = None,
text_align_center=False, brightness_if_mouse_on=50, *func_parameters):
"""
:param text: The text on the button
:param pos_and_size: The position and the size of the
button in format - (x, y, width, height). can be tuple or list.
:param func_in_pressed: func that calling when the the button pressed
:param background: Color or image path - tuple or list of three integers or string with path
"""
self.x = pos_and_size[0]
self.y = pos_and_size[1]
self.width = pos_and_size[2]
self.height = pos_and_size[3]
self._screen = screen
self.text = text
self._font = text_font
self.text_color = text_color
self.text_align_center = text_align_center
self.background = background
self._brightness = brightness_if_mouse_on
self._func = func_in_pressed
self._func_parameters = func_parameters
def check_button(self):
if_mouse_click = pygame.mouse.get_pressed(3)[0]
click_pos = pygame.mouse.get_pos()
if if_mouse_click and self.x <= click_pos[0] <= self.x + self.width and self.y <= click_pos[1] <= self.y +
self.height:
return self._func(*self._func_parameters)
def display(self):
mouse = pygame.mouse.get_pos()
if isinstance(self.background, list) or isinstance(self.background, tuple):
if self.x <= mouse[0] <= self.x + self.width and self.y <= mouse[1] <= self.y + self.height:
color_light = [255 if i + self._brightness >= 255 else i + self._brightness for i in self.background]
pygame.draw.rect(self._screen, color_light, [self.x, self.y, self.width, self.height])
else:
pygame.draw.rect(self._screen, self.background, [self.x, self.y, self.width, self.height])
elif isinstance(self.background, str):
image = pygame.image.load(self.background)
image = pygame.transform.scale(image, (self.width, self.height))
bright_image = image.copy()
bright_image.fill((self._brightness, self._brightness, self._brightness),
special_flags=pygame.BLEND_RGB_ADD)
#
if self.x <= mouse[0] <= self.x + self.width and self.y <= mouse[1] <= self.y + self.height:
self._screen.blit(bright_image, (self.x, self.y))
else:
self._screen.blit(image, (self.x, self.y))
else:
raise ValueError("background have to be image path or RBG color.")
if self.text is not None and self._font is not None and self.text_color is not None:
text = self._font.render(self.text, True, self.text_color)
if self.text_align_center:
text_x = self.x + self.width // 2 - self._font.size(self.text)[0] // 2
else:
text_x = self.x
self._screen.blit(text, (text_x, self.y))
class Display:
@staticmethod
def update_display():
pygame.event.pump()
pygame.display.update()
time.sleep(0.009)
class SnakesArrayManager:
def __init__(self, snakes_lst):
self._snakes = snakes_lst
def update_snakes_pos(self, screen):
for snake in self._snakes:
snake.update_pos(screen)
def update_snakes_status_if_encountered_margins(self, screen: pygame.Surface):
width, height = screen.get_width(), screen.get_height()
for snake in self._snakes:
snake.update_status_if_encountered_margins((width, height))
def update_snakes_status_if_encountered_by_themselves(self):
for snake in self._snakes:
snake.update_status_if_encountered_himself()
def update_snakes_status_if_encountered_others(self):
for ind, snake in enumerate(self._snakes):
for other in self._snakes[:ind] + self._snakes[ind + 1:]:
if snake.get_head_pos() in other.get_all_positions():
snake.disqualified = True
def display_snakes_score_in_rows(self, screen, font: pygame.font.Font, first_line_pos_start: Tuple[int, int],
line_height: int):
x_row = first_line_pos_start[0]
y_row = first_line_pos_start[1]
for snake in self._snakes:
snake.dis_score(font, (x_row, y_row), screen)
y_row += line_height + 5
def __str__(self):
return f"SnakesArrayManager({self._snakes})"
def __getitem__(self, item):
return self._snakes[item]
class SnakeGame(Display, SnakesArrayManager):
def __init__(self, screen: pygame.Surface, snakes: List[Snake]):
super().__init__(snakes)
self._screen = screen
self._foods_lst = []
def add_food(self, block: int):
x = random.randrange(0, self._screen.get_width(), block)
y = random.randrange(0, self._screen.get_height(), block)
self._foods_lst.append((x, y))
def display_foods(self, color: Tuple[int, int, int], block):
for x_food, y_food in self._foods_lst:
if x_food >= self._screen.get_width() or y_food >= self._screen.get_height():
self._foods_lst.remove((x_food, y_food, block))
pygame.draw.rect(self._screen, color, [x_food, y_food, block, block])
def update_snakes_score_by_food(self):
for snake in self._snakes:
snake_pos = snake.get_head_pos()
if snake_pos in self._foods_lst:
self._foods_lst.remove(snake_pos)
snake.score += 1
def game_over_display(self, game_over_mass_text: str, game_over_font: pygame.font.Font,
game_over_mass_color: Tuple[int, int, int],
score_table_font: pygame.font.Font, mass_align_center: bool = True):
width, height = self._screen.get_width(), self._screen.get_height()
game_over_mass_size = game_over_font.size(game_over_mass_text)
if mass_align_center:
game_over_mass_x = width // 2 - game_over_mass_size[0] // 2
else:
game_over_mass_x = 0
game_over_mass = game_over_font.render(game_over_mass_text, True, game_over_mass_color)
self._screen.blit(game_over_mass, (game_over_mass_x, height // 4))
score_row_size = score_table_font.size(Snake.drafting_score_mess.format(name=" ", score=" "))
if mass_align_center:
score_table_x = width // 2 - score_row_size[0] // 2
else:
score_table_x = 0
self.display_snakes_score_in_rows(self._screen, score_table_font,
(score_table_x, height // 4 + game_over_mass_size[1] + 10), score_row_size[1])
def restart(self):
for snake in self._snakes:
snake.reset()
self.__init__(self._screen, *self._snakes)
@property
def game_over(self):
snakes_lost = [snake.disqualified for snake in self._snakes]
if False not in snakes_lost:
return True
else:
return False
Если у вас есть другие комментарии к коду (я уверен, что они есть), напишите также. Действительно спасибо за помощь.