def draw_in_world(self, camera=None): """ Отрисовка объекта в мире. :param camera: Камера, относительно которой нужно отрисовывать объект :return: """ # Внимание! Меняя что-то здесь, не забывай поменять данную функцию в WorldObject! if self.visible: if self.image is not None: surface_to_draw = self.image.get_current() rect_to_draw = Rect.copy(self.object_rect) # TODO: подумать, можно ли избежать здесь ненужного копирования if self.image.get_size() != self.object_rect.size: # Если размер изображения не совпадает с размером объекта surface_to_draw = transform.scale(surface_to_draw, (self.object_rect.width, self.object_rect.height)) if self.current_angle != "UP": surface_to_draw = transform.rotate(surface_to_draw, ANGLE[self.current_angle]) if camera is not None: rect_to_draw.x += camera.get_coords()[0] rect_to_draw.y += camera.get_coords()[1] self.parent_world.parent_surface.blit(surface_to_draw, rect_to_draw) if self.need_to_animate: self.image.next() # Тестовое: if ID_DEBUG: self.test_text = self.test_font.render("id={}".format(self.world_id), 0, (255, 255, 255)) self.parent_world.parent_surface.blit(self.test_text, (self.object_rect.x + camera.get_coords()[0], self.object_rect.y + camera.get_coords()[1]))
def __init__(self, bounds: Rect, color: Optional[Color]): self.relative_bounds = bounds self.absolute_bounds = bounds.copy() self.color = color self.childs = [] self.enabled = True self.parent = None
class GameObject: def __init__(self, x, y, w, h, speed=[0, 0]): self.bounds = Rect(x, y, w, h) self.speed = speed.copy() self.old_pos = None self.old_speed = None @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface): pass def move(self, dx: object, dy: object) -> object: self.bounds = self.bounds.move(dx, dy) def update(self): """""" self.old_pos = self.bounds.copy() self.old_speed = self.speed.copy() if self.speed == [0, 0]: return self.move(*self.speed)
def __init__(self, bounds: Rect, color: Optional[Color], absolute_anchor: Anchor = Anchor.TOP_LEFT, relative_anchor: Anchor = Anchor.TOP_LEFT): """ :param bounds: Bounds of element relatively to parent element :param color: Main color of element :param absolute_anchor: Anchor of location at the point of the parent element :param relative_anchor:Anchor of point of (x, y) = (0, 0) on element """ self.relative_bounds = bounds self.absolute_anchor = absolute_anchor self.relative_anchor = relative_anchor self.absolute_bounds = bounds.copy() self.color = color self.childs = [] self.enabled = True self.parent = None
while running: clock.tick(FPS) for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == K_SPACE: paused = not paused if event.type == pygame.QUIT: running = False screen.fill(BLACK) transparent_surface.fill((0, 0, 0, 255)) for carPos in SIMULATOR.getCarPositions(): rect = CAR_RECT.copy() rect.center = carPos.x, carPos.y #pygame.draw.circle(transparent_surface, (143, 235, 52, 20), toPairI(carPos), 20) screen.blit(transparent_surface, (0, 0), special_flags=pygame.BLEND_RGBA_ADD) for street in SIMULATOR.map.streets: pygame.draw.line(screen, WHITE, toPairI(street.start), toPairI(street.end), 1) for street in SIMULATOR.map.streets: middlePoint = (street.start + street.end)/2 normal = scale(street.getDefaultVector().rotate(90), 4) middleVertex = scale(street.getDefaultVector(), 4) opposite = scale(street.getDefaultVector().rotate(-90), 4) pygame.draw.lines(screen, WHITE, False, [toPairI(middlePoint + normal), toPairI(middlePoint + middleVertex), toPairI(middlePoint + opposite)])
class UserInterface(Listener): def __init__(self, width: int, height: int): pygame.font.init() self.width = width self.height = height self.window = pygame.display.set_mode((self.width, self.height)) self._grid_rect = None self._grid_rows = None self._grid_cols = None self._tile_side = None self._tiles_cache = None self._grid_spacing = 15 self._padding = 40 # The main padded area inside the window self._container = Rect(self._padding, self._padding, self.width - self._padding * 2, self.height - self._padding * 2) self._title = "2048" self._score = "SCORE" self._best = "BEST" self._caption = "Join the numbers and get to the 2048 tile!" self._how_to = OrderedDict(move=['arrows', 'W, A, S, D'], restart=['r'], quit=['q', 'ESC']) self.window.fill(theme.BACKGROUND) self._header = self._setup_header() self._footer = self._setup_footer() self._drawable = True pygame.display.set_caption("2048 Game by Oleg Pavlovich") pygame.display.update() def _reset_background(self): self.window.fill(theme.BACKGROUND) self._setup_header() self._setup_footer() def _setup_grid(self, grid: Grid) -> Rect: assert self._header assert self._footer # Calculate grid size and draw it margin = 40 reserved = self._header.height + self._footer.height + 2 * margin min_side = min(self._container.width, self._container.height - reserved) scale_factor = min_side / max(grid.width, grid.height) grid_height = grid.height * scale_factor grid_width = grid.width * scale_factor grid_size = (grid_width, grid_height) # Save computed values and instances self._grid_rows = grid.height self._grid_cols = grid.width self._grid_rect = Rect((self._container.centerx - grid_width / 2, self._header.bottom + margin), grid_size) self._tile_side = (self._grid_rect.width - self._grid_spacing * (self._grid_cols + 1)) / self._grid_cols return self._grid_rect def _setup_header(self) -> Rect: # Draw title title = Rect((self._container.left, self._container.top), theme.H1_FONT.size(self._title)) utils.draw_text(self.window, self._title, theme.COLOR_DARK, title, theme.H1_FONT, True) # Draw caption margin = 15 caption = Rect((self._container.left, title.bottom + margin), theme.BASE_FONT.size(self._caption)) utils.draw_text(self.window, self._caption, theme.COLOR_DARK, caption, theme.BASE_FONT, True) self._header = Rect(self._container.topleft, (self._container.width, caption.height + title.height + margin)) pygame.display.update() return self._header def _setup_footer(self) -> Rect: # Draw tips from bottom to top margin_top = 5 margin_right = 6 tips = reversed(self._how_to.keys()) tip_h = theme.BOLD_FONT.size("Tg")[1] tip_x = self._container.left tip_y = self._container.bottom - tip_h for tip in tips: label = f"{tip.title()}" label_font = theme.BOLD_FONT label_size = label_font.size(label) label_rect = Rect((tip_x, tip_y), label_size) options = "on " + " or ".join(self._how_to[tip]) options_font = theme.BASE_FONT options_size = options_font.size(options) options_rect = Rect((label_rect.right + margin_right, label_rect.y), options_size) utils.draw_text(self.window, label, theme.COLOR_DARK, label_rect, label_font, True) utils.draw_text(self.window, options, theme.COLOR_DARK, options_rect, options_font, True) height = label_rect.height + margin_top tip_y = label_rect.top - height pygame.display.update() total_height = tip_h * len(self._how_to.keys()) self._footer = Rect(self._container.left, self._container.bottom - total_height, self._container.width, total_height) return self._footer def _draw_scores(self, score: int = 0, best: int = 0): score_margin = 10 score_side = (self._container.width // 2 - score_margin * 2) / 2 scale_factor = (self._header.height // 2) / score_side score_side *= scale_factor score_y = (self._header.centery - score_side / 2) - 15 # purely visual offset score_x = self._container.right - score_margin - 2 * score_side for label, value in zip((self._score, self._best), (score, best)): value = str(value) label_size = theme.LABEL_FONT.size(label) value_size = theme.VALUE_FONT.size(value) bg_rect = Rect(score_x, score_y, score_side, score_side) label_rect = Rect((bg_rect.centerx - label_size[0] / 2, bg_rect.top + 6), label_size) value_rect = Rect((bg_rect.centerx - value_size[0] / 2, label_rect.bottom), value_size) utils.draw_rounded_rect(self.window, theme.GRID_COLOR, bg_rect, border_radius=4) utils.draw_text(self.window, label, theme.COLOR_LIGHT, label_rect, theme.LABEL_FONT, True) utils.draw_text(self.window, value, theme.COLOR_WHITE, value_rect, theme.VALUE_FONT, True) # Set next block's x-coordinate score_x = bg_rect.right + score_margin pygame.display.update() def _draw_tiles(self, grid: Grid): if not self._grid_rect: self._setup_grid(grid) utils.draw_rounded_rect(self.window, theme.GRID_COLOR, self._grid_rect, border_radius=8) x, y = self._grid_rect.topleft for i in range(self._grid_rows): rect = None x = self._grid_rect.left for j in range(self._grid_cols): color = theme.CELL_COLOR tile = grid.get_cell(Position(j, i)) text = None rect = Rect(x + self._grid_spacing, y + self._grid_spacing, self._tile_side, self._tile_side) if tile: color = theme.TILE_COLOR_BASE text = theme.H2_FONT.render(str(tile.value), True, theme.COLOR_DARK) utils.draw_rounded_rect(self.window, color, rect, 4) if text: text_x = rect.left + (rect.width - text.get_width()) // 2 text_y = rect.top + (rect.height - text.get_height()) // 2 self.window.blit(text, (text_x, text_y)) x = rect.right y = rect.bottom pygame.display.update() def _draw_message(self, text: str, overlay: bool = True): message_rect = self._header.copy() message_rect.y = message_rect.height * 2 if overlay: alpha = 235 color = theme.BACKGROUND color = color.r, color.g, color.b, alpha surf = pygame.Surface((self.width, self.height), pygame.SRCALPHA) surf.fill(color) self.window.blit(surf, (0, 0)) else: self.window.fill(theme.BACKGROUND) utils.draw_text(self.window, text, theme.COLOR_DARK, message_rect, theme.H3_FONT, True) pygame.display.update() def notify(self, event): if isinstance(event, GameReadyEvent): # Game was restarted with this flag if not self._drawable: self._reset_background() self._drawable = True self._setup_grid(event.grid) self._draw_tiles(event.grid) self._draw_scores(score=event.score, best=event.best) # If the game has ended this flag is raised if not self._drawable: return if isinstance(event, GridUpdateEvent): self._draw_tiles(event.grid) if isinstance(event, ScoreUpdateEvent): self._draw_scores(score=event.score, best=event.best) if isinstance(event, GameOverEvent): self._draw_scores(score=event.score, best=event.best) self._draw_message(f"Game over! Score {event.score}.\nPress r to restart or q to quit :)") self._drawable = False