add coins and flip hero

This commit is contained in:
Dmitry Belyaev 2024-03-30 22:00:07 +03:00
parent 6efab241a5
commit 1ddc649165

View File

@ -11,8 +11,6 @@
Если скачать не получилось то будут нарисованы заглушки.
Зависимости: pygame pillow
TODO: отбработка выхода из лабиринта
"""
import os
@ -21,7 +19,7 @@ import tempfile
import urllib.request
from abc import ABC, abstractmethod
from contextlib import contextmanager
from random import choice, randrange
from random import choice, randrange, sample
from typing import NamedTuple, Optional
import PIL
@ -285,6 +283,9 @@ class Hero(GameObject):
self.rect = self.surface.get_rect()
self.rect.topleft = coords
self.active = True
self.looking_right = False
# картинка изначально влево, а надо бы начинать со взгляда вправо
self.flip()
def draw(self):
self.parent.surface.blit(self.surface, self.rect)
@ -320,36 +321,57 @@ class Hero(GameObject):
has_collision = self._check_collision(coords)
super().set_coords(coords)
def flip(self):
self.looking_right = not self.looking_right
self._surface = pygame.transform.flip(self.surface, True, False)
def move(self, direction: Coords, step: int = 1):
if direction.x != 0:
going_right = direction.x > 0
if self.looking_right != going_right:
self.flip()
self.coords += direction * step
self.scene.coins.collect(self.rect)
def move_left(self, step: int = 1):
self.move(Coords(-1, 0), step)
def move_right(self, step: int = 1):
self.move(Coords(1, 0), step)
def move_up(self, step: int = 1):
self.move(Coords(0, -1), step)
def move_down(self, step: int = 1):
self.move(Coords(0, 1), step)
def handle_event(self, event: pygame.event.Event):
if not self.active:
return
up, down = Coords(0, -1), Coords(0, 1)
left, right = Coords(-1, 0), Coords(1, 0)
da, db = 30, 5
wide, short = 30, 5
if event.type == pygame.KEYDOWN:
match event.key:
case pygame.K_UP:
self.coords += up * da
self.move_up(wide)
case pygame.K_DOWN:
self.coords += down * da
self.move_down(wide)
case pygame.K_LEFT:
self.coords += left * da
self.move_left(wide)
case pygame.K_RIGHT:
self.coords += right * da
self.move_right(wide)
case pygame.K_w:
self.coords += up * db
self.move_up(short)
case pygame.K_s:
self.coords += down * db
self.move_down(short)
case pygame.K_a:
self.coords += left * db
self.move_left(short)
case pygame.K_d:
self.coords += right * db
self.move_right(short)
class WallBlock(GameObject):
"""объект елемента стены"""
"""объект элемента стены"""
def __init__(
self,
@ -395,6 +417,102 @@ class Walls(GameObject):
return True
return False
class Coin(GameObject):
"""объект монетки"""
def __init__(
self,
coords: Coords,
parent: GameObject,
assets: dict | None = None,
):
super().__init__(coords, parent, assets)
self._surface = pygame.image.load(self.assets["coin.png"])
self.rect = self.surface.get_rect()
self.rect.topleft = coords
# уменьшаем размер монетки
new_rect = self.rect.scale_by(0.7, 0.7)
self._surface = pygame.transform.scale(self._surface, new_rect.size)
self.rect = new_rect
def draw(self):
self.parent.surface.blit(self.surface, self.rect)
class Coins(GameObject):
"""объект коллекции монеток"""
def __init__(
self,
parent: GameObject,
maze: list[list[int]],
box_sz: Coords,
count: int,
assets: dict | None = None,
):
super().__init__(Coords.zero(), parent, assets)
self.box_sz = box_sz
self._capacity = count
free_points = []
excluded = Coords(0, 1), get_maze_sz(maze) - Coords(1, 2)
for i, row in enumerate(maze):
for j, item in enumerate(row):
p = Coords(j, i)
if item < 1 and p not in excluded:
free_points.append(p)
continue
coin_points = sample(free_points, min(count, len(free_points)))
self.coins = [
Coin(point.transform(box_sz), self, self.assets)
for point in coin_points
]
self.collected_coins = []
# Надпись, если все монетки собраны
font = pygame.font.SysFont("Arial", 30)
text = "Все монетки собраны!"
self.done_txt = font.render(text, 1, "#050366e3")
self.done_txt_rect = self.done_txt.get_rect()
self.done_txt_rect.topleft = Coords(10, 10)
@property
def capacity(self):
return self._capacity
@property
def coins_left(self):
return len(self.coins)
@property
def coins_collected(self):
return self.capacity - self.coins_left
@property
def all_collected(self):
return self.coins_left == 0
def draw(self):
for coin in self.collected_coins:
coin.draw()
for coin in self.coins:
coin.draw()
if self.all_collected:
self.parent.surface.blit(self.done_txt, self.done_txt_rect)
def add_to_collected(self, coin: Coin):
last_pos = Coords(10, 10)
if self.collected_coins:
last_pos = Coords(*self.collected_coins[-1].rect.topright)
last_pos -= Coords(coin.rect.width // 2, 0)
coin.coords = last_pos
self.collected_coins.append(coin)
def collect(self, rect: pygame.Rect):
mined = [*filter(lambda coin: coin.rect.colliderect(rect), self.coins)]
for coin in mined:
self.coins.remove(coin)
self.add_to_collected(coin)
class EndLevel(GameObject):
def __init__(self, scene: GameObject):
@ -417,10 +535,19 @@ class EndLevel(GameObject):
self.hint_rect.center = self.parent.rect.center
self.hint_rect = self.hint_rect.move(Coords(0, 300))
# Надпись для хорошего финала
text = "Все монетки собраны!"
self.goodtxt = font_hint.render(text, 1, "#96081ba4")
self.goodtxt_rect = self.goodtxt.get_rect()
self.goodtxt_rect.center = self.parent.rect.center
self.goodtxt_rect = self.goodtxt_rect.move(Coords(0, -100))
def draw(self):
if not self.active:
return
self.parent.surface.blit(self.image, self.rect)
if self.scene.coins.all_collected:
self.parent.surface.blit(self.image, self.rect)
self.parent.surface.blit(self.goodtxt, self.goodtxt_rect)
self.parent.surface.blit(self.surface, self.rect)
self.parent.surface.blit(self.hint, self.hint_rect)
@ -439,7 +566,7 @@ class Scene(GameObject):
# кнопки для выхода из игры
exit_keys = (pygame.K_ESCAPE, pygame.K_q)
def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords):
def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords, coins_count: int):
super().__init__(Coords.zero(), None, assets)
self.maze = maze_gen(*maze_sz)
maze_sz = get_maze_sz(self.maze)
@ -447,6 +574,7 @@ class Scene(GameObject):
box_sz = screen_sz // get_maze_sz(self.maze)
self.box_sz = box_sz
resize_img(self.assets, "brick.png", box_sz)
resize_img(self.assets, "coin.png", box_sz)
hero_sz = Coords(*map(int, box_sz * 0.8))
resize_img(self.assets, "ghost.png", hero_sz)
@ -461,12 +589,15 @@ class Scene(GameObject):
self.done = False
self.level_completed = False
self.maze = maze_gen(6, 6)
self.walls = Walls(self, self.maze, box_sz)
self.coins = Coins(self, self.maze, box_sz, coins_count)
self.end = EndLevel(self)
self.end.active = False
self.want_new_level = False
self.exit_rect = self.get_exit_rect()
# для тестирования экрана конца уровня
# #для тестирования экрана конца уровня
# self.hero.coords = Coords(*self.exit_rect.topleft) + Coords(
# -self.box_sz.x // 2, 5
# )
@ -495,9 +626,9 @@ class Scene(GameObject):
if self.level_completed:
self.end.draw()
else:
# pygame.draw.rect(self._surface, pygame.Color("#42c53d25"), self.get_exit_rect())
self.hero.draw()
self.walls.draw()
self.coins.draw()
def handle_event(self, event: pygame.event.Event):
if self.done:
@ -525,11 +656,12 @@ def game(assets):
pygame.init()
screen_sz = Coords(1000, 1000)
maze_sz = Coords(6, 6)
coins_count = 10
pygame.display.set_caption("Движение рисунка на Pygame")
want_new_level = True
while want_new_level:
scene = Scene(assets, screen_sz, maze_sz)
scene = Scene(assets, screen_sz, maze_sz, coins_count)
scene.event_loop()
want_new_level = scene.want_new_level
@ -542,6 +674,7 @@ def main():
"ghost.png",
"brick.png",
"win.png",
"coin.png",
]
with get_assets(assets) as assets:
game(assets)