Untitled
unknown
plain_text
2 years ago
9.6 kB
9
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