Untitled
unknown
plain_text
2 years ago
9.6 kB
6
Indexable
import pygame import random from typing import Dict, List, Tuple #úkoly #1. postava se může vydat za hranice mapy, napravte to #2. protivník se může narodit velice blízko či přímo na hráčovi, napravte to #3. postava se pohybuje jen při stisku klávesy a ne při podržení, napravte to #4. předělejtu hru tak, aby postavy byly kruhy místo čtverců #5. pokud větší objekt koliduje s menším, tak větší absorbuje menší a povyroste o jeho obsah #6. dejte hráčovi tři životy, představované barvami v pořadí zelená, oranžová, červená #7. dejte možnost hru opakovat pokud hráč prohraje #8. přidejte možnost hru pozastavit (pause game) #9. přidejte možnost ovládat hru myší #10. předělejte hru do OOP, aktuálně není s rozšířeními příliš udržitelná #colors COLORS = { "white": (255, 255, 255), "red": (255, 0, 0), "green": (0, 255, 0), "blue": (0, 0, 255), "black": (0, 0, 0), "orange": (255,165,0) } #game visualization WINDOW_WIDTH = 500 WINDOW_HEIGHT = 500 WINDOW_COLOR = COLORS["white"] SCORE_COORDINATES = (50, 50) SCORE_FONT_SIZE = 50 SCORE_COLOR = COLORS["black"] TITLE = "Square? Beware!" FPS = 30 #character settings PLAYER_COLOR = COLORS["blue"] PLAYER_CHARACTER_WIDTH = 10 PLAYER_CHARACTER_HEIGHT = 10 #enemy settings ENEMY_COLOR = COLORS["black"] ENEMY_WIDTH = 5 ENEMY_HEIGHT = 5 ENEMY_DELTA_X = 5 ENEMY_DELTA_Y = 5 SPAWN_EVERY_MILISECONDS = 5000 SPAWN_ENEMY = pygame.USEREVENT ENEMY_MOVE_EVERY = 500 ENEMY_MOVE = pygame.USEREVENT #player control DELTA_X = 5 DELTA_Y = 5 KEY_MAPPING = { pygame.K_UP: "move_up", pygame.K_DOWN: "move_down", pygame.K_LEFT: "move_left", pygame.K_RIGHT: "move_right", pygame.K_q: "quit_game", pygame.K_p: "pause_game" } def print_score(canvas: pygame.Surface, collided_with: int, main_character: Dict[str, int], score_font_renderer: pygame.font.Font) -> None: """Recalcutes and prints actual score (number of collided object with) of player. Args: canvas (pygame.Surface): pygame surface where score will be rendered on. collided_with (int): index of object that player collided with. Negative if no collision detected. main_character (Dict[str, int]): player state for score update. score_font_renderer(pygame.font.Font): font renderer for player score. """ main_character["score"] += 1 if collided_with >= 0 else 0 text = f"Score: {main_character['score']} object eaten." message = score_font_renderer.render(text, True, SCORE_COLOR) canvas.blit(source=message, dest=SCORE_COORDINATES) def remove_enemy(enemies: List[Dict[str, int]], collided_with: int) -> None: """Removes enemy that player collided with. Args: enemies (List[Dict[str, int]]): list od enemies with their state collided_with (int): index of enemy that player collided with. If -1, then no collision have been detected. """ if collided_with != -1: enemies.pop(collided_with) def collision_detection(main_character: Dict[str, int], enemies: List[Dict[str, int]]) -> int: """Detects collision of player character with enemy. Args: main_character (Dict[str, int]): player character state enemies (List[Dict[str, int]]): list of enemies and their states Returns: int: index of firt enemy that player collided with. """ character_collider = pygame.Rect(main_character["x"], main_character["y"], PLAYER_CHARACTER_WIDTH, PLAYER_CHARACTER_HEIGHT) enemy_colliders = [pygame.Rect(enemy["x"], enemy["y"], ENEMY_WIDTH, ENEMY_HEIGHT) for enemy in enemies] return character_collider.collidelist(enemy_colliders) def enemy_movement(enemies: List[Dict[str, int]]) -> None: """Moves enemies in random direction. Args: enemies (List[Dict[str, int]]): list of enemies with their states. """ for enemy in enemies: enemy["x"] += random.uniform(-ENEMY_DELTA_X, -ENEMY_DELTA_X) enemy["y"] += random.uniform(-ENEMY_DELTA_Y, ENEMY_DELTA_Y) def generate_enemy(enemies: List[Dict[str, int]]) -> None: """Generates new enemy to list of enemies on random position in game canvas. Args: enemies (List[Dict[str, int]]): List of enemy states that new enemy with its state will be added. """ random_position = [random.uniform(0, WINDOW_WIDTH), random.uniform(0, WINDOW_HEIGHT)] enemies.append(dict(zip(["x", "y"], random_position))) def control_character(player_action: str, main_character: Dict[str, int], game_state: Dict[str, bool]) -> None: """Updates game state or player state based upon keyboard input. Args: player_action (str): keymapped action that player inputted main_character (Dict[str, int]): state of player character game_state (Dict[str, bool]): state of game """ if player_action == "move_up": main_character["y"] -= DELTA_Y elif player_action == "move_down": main_character["y"] += DELTA_Y elif player_action == "move_left": main_character["x"] -= DELTA_X elif player_action == "move_right": main_character["x"] += DELTA_X elif player_action == "pause_game": game_state["pause"] = not game_state["pause"] elif player_action == "quit_game": game_state["gameover"] = True def get_game_event() -> Tuple[str, str]: """Gets pygame events that happened in actual frame. Returns: Tuple[str, str]: event type and concrete event action that happened. """ for event in pygame.event.get(): if event.type == pygame.QUIT: return ("state_change", "quit_game") elif event.type == pygame.KEYDOWN: return ("user_action", KEY_MAPPING.get(event.key)) elif event.type == SPAWN_ENEMY: return ("enemy_event", "spawn_enemy") elif event.type == ENEMY_MOVE: return ("enemy_event", "move_enemy") return ("no_event", None) def render_game(game_canvas: pygame.Surface, main_character: Dict[str, int], enemies: List[Dict[str, int]]) -> None: """Renders player character and enemies on pygame Surface object. Args: game_canvas (pygame.Surface): surface that objects will be rendered on. main_character (Dict[str, int]): player character state to be rendered. enemies (List[Dict[str, int]]): list of enemies with their states to be rendered. """ game_canvas.fill(WINDOW_COLOR) pygame.draw.rect( surface=game_canvas, color=PLAYER_COLOR, rect=[main_character["x"], main_character["y"], PLAYER_CHARACTER_WIDTH, PLAYER_CHARACTER_HEIGHT] ) for enemy in enemies: pygame.draw.rect( surface=game_canvas, color=ENEMY_COLOR, rect=[enemy["x"], enemy["y"], ENEMY_WIDTH, ENEMY_HEIGHT] ) def gameloop() -> None: """Game loop that repeats until quit event. """ #init game state game_size = {"width": WINDOW_WIDTH, "height": WINDOW_HEIGHT} game_canvas = pygame.display.set_mode(size=(WINDOW_WIDTH, WINDOW_HEIGHT)) score_font_renderer = pygame.font.SysFont(None, SCORE_FONT_SIZE) #List of available fonts: pygame.font.get_fonts() main_character = {"x": game_size["width"]//2, "y": game_size["height"]//2, "score": 0} enemies = [] game_clock = pygame.time.Clock() game_state = {"gameover": False, "pause": False} #start gameloop while not game_state["gameover"]: #1. render state of game render_game(game_canvas, main_character, enemies) #2. get events that happened event_type, event_action = get_game_event() #3. update game state based upon event if event_type == "user_action": control_character(event_action, main_character, game_state) elif event_type == "enemy_event" and event_action == "spawn_enemy": generate_enemy(enemies) elif event_type == "enemy_event" and event_action == "move_enemy": enemy_movement(enemies) elif event_type == "state_change" and event_action == "quit_game": game_state["gameover"] = True #4. calculate collisions collided_with = collision_detection(main_character, enemies) #5. update enemies states based on collisions remove_enemy(enemies, collided_with) #6. update player state based on collisions print_score(game_canvas, collided_with, main_character, score_font_renderer) #wait for demanded FPS and render new frame game_clock.tick(FPS) pygame.display.update() #FPS calculation explanation # frame = one iteration of gameloop (concretly pygame.display update) # T = time that one iteration (frame) took # 1/T = number of frames per second (frequency of pygame.display update) # FPS = how many frames per second we actually want # tick(FPS) = force iteration to sleep to reach demanded FPS def main() -> None: """Program entry point. """ #game initialization with window settings pygame.init() pygame.display.set_caption(TITLE) #events registration pygame.time.set_timer(ENEMY_MOVE, ENEMY_MOVE_EVERY) pygame.time.set_timer(SPAWN_ENEMY, SPAWN_EVERY_MILISECONDS) #start game loop gameloop() #quit game when gameloop ends via return statement pygame.quit() quit() if __name__ == "__main__": main()
Editor is loading...
Leave a Comment