diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..359bb530 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/FlapPyBird.iml b/.idea/FlapPyBird.iml new file mode 100644 index 00000000..7a6134d1 --- /dev/null +++ b/.idea/FlapPyBird.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..7f3497b4 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..300b0a47 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/main.py b/main.py index afbf6ae8..c10b5860 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,13 @@ -import asyncio +# import asyncio +# +# from src_old.flappy import Flappy +# +# if __name__ == "__main__": +# asyncio.run(Flappy().start()) -from src.flappy import Flappy + +import src.game if __name__ == "__main__": - asyncio.run(Flappy().start()) + game = src.game.Game() + game.main_loop() diff --git a/src/entities/background.py b/src/entities/background.py index 6e1226ed..32ae7717 100644 --- a/src/entities/background.py +++ b/src/entities/background.py @@ -1,14 +1,26 @@ -from ..utils import GameConfig -from .entity import Entity +from src.utils.manager import Manager +from src.utils.stats import Stats +import pygame +import random +class Background: + def __init__(self, display_surface): + self.display_surface = display_surface + self.stat = Stats().stat + self.base_image = Manager.get_instance().base + self.image = Manager.get_instance().background[random.randint(0, 1)] + self.rect = pygame.Rect(0, 400, 336, 112) -class Background(Entity): - def __init__(self, config: GameConfig) -> None: - super().__init__( - config, - config.images.background, - 0, - 0, - config.window.width, - config.window.height, - ) + def draw(self): + self.stat = Stats().stat + #画背景 + self.display_surface.blit(self.image, self.image.get_rect()) + #画地面 + if self.stat in ["welcome", "gameover"]: + self.display_surface.blit(self.base_image, self.rect) + elif self.stat == "run": + self.rect.move_ip(-5, 0) + self.display_surface.blit(self.base_image, self.rect) + self.display_surface.blit(self.base_image, self.rect.move(336, 0)) + if self.rect.x <= -336: + self.rect.x = 0 \ No newline at end of file diff --git a/src/entities/bird.py b/src/entities/bird.py new file mode 100644 index 00000000..d1baaaaf --- /dev/null +++ b/src/entities/bird.py @@ -0,0 +1,60 @@ +import pygame +import random +from src.utils.manager import Manager +from src.utils.stats import Stats + +class Bird(object): + def __init__(self, display_surface, rect): + # 定义一个小鸟类 + self.rect_list = rect + self.display_surface = display_surface + self.birdRect = pygame.Rect(65, 50, 32, 24) + self.birdStatus = Manager.get_instance().birds + self.stat = Stats() + self.birdx = 5 + self.birdy = 350 + self.jump = False + self.jumpSpeed = 0 + self.gravity = 0.05 + self.dead = False + self.colorBird=random.randint(0,2) + self.v = 0 + + def birdUpdate(self): + # 定义移动方法 + self.v += self.gravity + self.birdy += self.v + self.birdy += self.jumpSpeed + if self.jump: + # 小鸟跳跃状态 + self.jumpSpeed += 0.15 + self.v = -2.5 + self.v += self.jumpSpeed + + if self.jumpSpeed >= 0: + self.jumpSpeed = 0 + self.jump = False + + def createMap(self): + self.birdRect = pygame.Rect(self.birdx, self.birdy, 32, 24) + self.display_surface.blit(self.birdStatus[self.colorBird][0], self.birdRect) + self.display_surface.blit(self.birdStatus[self.colorBird][1], self.birdRect) + self.display_surface.blit(self.birdStatus[self.colorBird][2], self.birdRect) + if not self.dead: + self.birdUpdate() + + + def checkDead(self, rect): + #检测小鸟是否死亡 + self.rect_list = rect + + if 0 <= self.birdy <= 512: + self.dead = False + for x in self.rect_list: + if x.colliderect(self.birdRect): + self.dead = True + print('1') + + else: + self.dead = True + diff --git a/src/entities/pipe.py b/src/entities/pipe.py index f8b1851f..74b58766 100644 --- a/src/entities/pipe.py +++ b/src/entities/pipe.py @@ -1,104 +1,48 @@ +import pygame import random -from typing import List +from src.utils.stats import Stats +from src.utils.manager import Manager + +#pipe_length = 320 +class Pipe: + def __init__(self, display_surface): + self.display_surface = display_surface + self.images_list = Manager.get_instance().pipes + self.image_up = self.images_list[random.randint(0, 1)] + self.image_down = pygame.transform.flip(self.image_up, False, True) + + self.x_0 = 200 + self.x_1 = self.x_0 + 170 + self.y_0 = random.randint(200, 380) + self.y_1 = random.randint(200, 380) + + self.rect_0 = pygame.Rect(self.x_0, self.y_0, 52, 320) + self.rect_1 = pygame.Rect(self.x_1, self.y_1, 52, 320) + self.rect_2 = pygame.Rect(self.x_0, (self.y_0 - 500), 52, 320) + self.rect_3 = pygame.Rect(self.x_1, (self.y_1 - 500), 52, 320) + self.rect_list = [self.rect_0, self.rect_1] + self.rect_list_all = [self.rect_0, self.rect_1, self.rect_2, self.rect_3] + def update(self): + self.x_0 -= 2 + self.x_1 -= 2 + self.rect_0 = pygame.Rect(self.x_0, self.y_0, 52, 320) + self.rect_1 = pygame.Rect(self.x_1, self.y_1, 52, 320) + self.rect_2 = pygame.Rect(self.x_0, (self.y_0 - 500), 52, 320) + self.rect_3 = pygame.Rect(self.x_1, (self.y_1 - 500), 52, 320) + self.rect_list_all = [self.rect_0, self.rect_1, self.rect_2, self.rect_3] + self.rect_list = [self.rect_0, self.rect_1] + if self.x_0 <= -52: + self.x_0 = 288 + self.y_0 = random.randint(200, 380) + if self.x_1 <= -52: + self.x_1 = 288 + self.y_1 = random.randint(200, 380) + + + def draw(self): + for x in self.rect_list_all: + if x in self.rect_list: + self.display_surface.blit(self.image_up, x) + else: + self.display_surface.blit(self.image_down, x) -from ..utils import GameConfig -from .entity import Entity - - -class Pipe(Entity): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.vel_x = -5 - - def draw(self) -> None: - self.x += self.vel_x - super().draw() - - -class Pipes(Entity): - upper: List[Pipe] - lower: List[Pipe] - - def __init__(self, config: GameConfig) -> None: - super().__init__(config) - self.pipe_gap = 120 - self.top = 0 - self.bottom = self.config.window.viewport_height - self.upper = [] - self.lower = [] - self.spawn_initial_pipes() - - def tick(self) -> None: - if self.can_spawn_pipes(): - self.spawn_new_pipes() - self.remove_old_pipes() - - for up_pipe, low_pipe in zip(self.upper, self.lower): - up_pipe.tick() - low_pipe.tick() - - def stop(self) -> None: - for pipe in self.upper + self.lower: - pipe.vel_x = 0 - - def can_spawn_pipes(self) -> bool: - last = self.upper[-1] - if not last: - return True - - return self.config.window.width - (last.x + last.w) > last.w * 2.5 - - def spawn_new_pipes(self): - # add new pipe when first pipe is about to touch left of screen - upper, lower = self.make_random_pipes() - self.upper.append(upper) - self.lower.append(lower) - - def remove_old_pipes(self): - # remove first pipe if its out of the screen - for pipe in self.upper: - if pipe.x < -pipe.w: - self.upper.remove(pipe) - - for pipe in self.lower: - if pipe.x < -pipe.w: - self.lower.remove(pipe) - - def spawn_initial_pipes(self): - upper_1, lower_1 = self.make_random_pipes() - upper_1.x = self.config.window.width + upper_1.w * 3 - lower_1.x = self.config.window.width + upper_1.w * 3 - self.upper.append(upper_1) - self.lower.append(lower_1) - - upper_2, lower_2 = self.make_random_pipes() - upper_2.x = upper_1.x + upper_1.w * 3.5 - lower_2.x = upper_1.x + upper_1.w * 3.5 - self.upper.append(upper_2) - self.lower.append(lower_2) - - def make_random_pipes(self): - """returns a randomly generated pipe""" - # y of gap between upper and lower pipe - base_y = self.config.window.viewport_height - - gap_y = random.randrange(0, int(base_y * 0.6 - self.pipe_gap)) - gap_y += int(base_y * 0.2) - pipe_height = self.config.images.pipe[0].get_height() - pipe_x = self.config.window.width + 10 - - upper_pipe = Pipe( - self.config, - self.config.images.pipe[0], - pipe_x, - gap_y - pipe_height, - ) - - lower_pipe = Pipe( - self.config, - self.config.images.pipe[1], - pipe_x, - gap_y + self.pipe_gap, - ) - - return upper_pipe, lower_pipe diff --git a/src/entities/words.py b/src/entities/words.py new file mode 100644 index 00000000..e3bcd431 --- /dev/null +++ b/src/entities/words.py @@ -0,0 +1,21 @@ +import pygame +from src.utils.manager import Manager +import random + +class Words: + def __init__(self, display_surface): + self.display_surface = display_surface + self.image_GM = Manager.get_instance().gameover + self.rect_GM = pygame.Rect(48, 235, 192, 42) + + self.image_M = Manager.get_instance().message + self.rect_M = pygame.Rect(52, 122.5, 184, 267) + def draw_GM(self): + self.display_surface.blit(self.image_GM, self.rect_GM) + + + def draw_M(self): + self.display_surface.blit(self.image_M, self.rect_M) + + + diff --git a/src/game.py b/src/game.py new file mode 100644 index 00000000..5772f99d --- /dev/null +++ b/src/game.py @@ -0,0 +1,88 @@ +import sys +import pygame + +#from src.utils.screen import Screen +from src.utils.settings import Settings +from src.utils.manager import Manager +from src.entities.background import Background +from src.entities.words import Words +from src.entities.pipe import Pipe +from src.entities.bird import Bird +from src.utils.stats import Stats + + +class Game: + def __init__(self): + pygame.init() + pygame.display.set_caption("Flappy Bird") + self.settings = Settings() + self.screen = pygame.display.set_mode(self.settings.window_size)#改成Screen() + self.clock = pygame.time.Clock() + self.manager = Manager() + self.stats = Stats() + + self.stat = self.stats.stat + + self.background = Background(self.screen) + self.words = Words(self.screen) + self.pipe = Pipe(self.screen) + self.bird = Bird(self.screen, self.pipe.rect_list_all) + #self.stats = "gameover" #0=welcome,1=run,2=gameover + + def handle_events(self): + for event in pygame.event.get(): + + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_q: + pygame.quit() + sys.exit() + elif event.type == pygame.MOUSEBUTTONDOWN: + #响应鼠标点击 + if not self.bird.dead: + self.stat = "run" + self.bird.jump = True + self.bird.jumpSpeed = -1 + print('mouse test') + + def update_screen(self): + self.background.draw() + + if self.stat == "welcome": + self.words.draw_M() + + #画开始界面 + elif self.stat == "run": + self.pipe.draw() + self.pipe.update() + self.bird.birdUpdate() + self.bird.createMap() + self.bird.checkDead(self.pipe.rect_list_all) + # 画游戏界面的物品 + elif self.stat == "gameover": + self.pipe.draw() + self.bird.createMap() + self.words.draw_GM() + + #画gameover + pygame.display.flip() + + + def main_loop(self): + while True: + + self.handle_events() + self.update_screen() + if self.bird.dead: + self.stat = "gameover" + + + self.clock.tick(self.settings.fps) + + + + + def initialize(self): + pass diff --git a/src/utils/manager.py b/src/utils/manager.py new file mode 100644 index 00000000..fb8c2956 --- /dev/null +++ b/src/utils/manager.py @@ -0,0 +1,40 @@ +import pygame.image +import os + +FILE_ROOT = f"{os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))}/assets/sprites/" + +class Manager: + def __init__(self): + pygame.init() + pygame.display.set_mode((288, 512)) + + + red_bird_name = ["redbird-downflap", "redbird-midflap", "redbird-upflap"] + yellow_bird_name = ["yellowbird-downflap", "yellowbird-midflap", "yellowbird-upflap"] + blue_bird_name = ["bluebird-downflap", "bluebird-midflap", "bluebird-upflap"] + background_name = ["background-day", "background-night"] + pipe_name = ["pipe-green", "pipe-red"] + + blue_bird = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in blue_bird_name] + red_bird = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in red_bird_name] + yellow_bird = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in yellow_bird_name] + + self.birds = [blue_bird, red_bird, yellow_bird] + self.numbers = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in [str(x) for x in range(10)]] + self.background = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in background_name] + self.pipes = [pygame.image.load(image.join([FILE_ROOT, ".png"])).convert_alpha() + for image in pipe_name] + self.base = pygame.image.load(os.path.join(FILE_ROOT, "base.png")).convert_alpha() + self.gameover = pygame.image.load(os.path.join(FILE_ROOT, "gameover.png")).convert_alpha() + self.message = pygame.image.load(os.path.join(FILE_ROOT, "message.png")).convert_alpha() + + @staticmethod + def get_instance(): + return instance + +instance = Manager() diff --git a/src/utils/screen.py b/src/utils/screen.py new file mode 100644 index 00000000..05bcb294 --- /dev/null +++ b/src/utils/screen.py @@ -0,0 +1,7 @@ +class Screen: + def __init__(self, screen): + self.screen = screen + + def flush(self, sprites: list): + for s in sprites: + s.blit(self.screen) diff --git a/src/utils/settings.py b/src/utils/settings.py new file mode 100644 index 00000000..94c1d2dc --- /dev/null +++ b/src/utils/settings.py @@ -0,0 +1,5 @@ +class Settings: + def __init__(self, window_size=(288, 512), fps=30): + self.window_size = window_size + self.fps = fps + diff --git a/src/utils/stats.py b/src/utils/stats.py new file mode 100644 index 00000000..9ab8d476 --- /dev/null +++ b/src/utils/stats.py @@ -0,0 +1,8 @@ +class Stats: # FIXME: singleton mode implementation + stat_list = ["welcome", "run", "gameover"] + #WELCOME, RUN, GAMEOVER = range(3) + def __init__(self): + self.stat = self.stat_list[0] + #self.stat = Stats.WELCOME + def reset(self): + self.stat = self.stat_list[0] diff --git a/src/__init__.py b/src_old/__init__.py similarity index 100% rename from src/__init__.py rename to src_old/__init__.py diff --git a/src/entities/__init__.py b/src_old/entities/__init__.py similarity index 100% rename from src/entities/__init__.py rename to src_old/entities/__init__.py diff --git a/src_old/entities/background.py b/src_old/entities/background.py new file mode 100644 index 00000000..6e1226ed --- /dev/null +++ b/src_old/entities/background.py @@ -0,0 +1,14 @@ +from ..utils import GameConfig +from .entity import Entity + + +class Background(Entity): + def __init__(self, config: GameConfig) -> None: + super().__init__( + config, + config.images.background, + 0, + 0, + config.window.width, + config.window.height, + ) diff --git a/src/entities/entity.py b/src_old/entities/entity.py similarity index 98% rename from src/entities/entity.py rename to src_old/entities/entity.py index 6ec067ce..673d4962 100644 --- a/src/entities/entity.py +++ b/src_old/entities/entity.py @@ -63,7 +63,7 @@ def tick(self) -> None: rect = self.rect if self.config.debug: pygame.draw.rect(self.config.screen, (255, 0, 0), rect, 1) - # write x and y at top of rect + # write x and y at top of rect_GM font = pygame.font.SysFont("Arial", 13, True) text = font.render( f"{self.x:.1f}, {self.y:.1f}, {self.w:.1f}, {self.h:.1f}", diff --git a/src/entities/floor.py b/src_old/entities/floor.py similarity index 100% rename from src/entities/floor.py rename to src_old/entities/floor.py diff --git a/src/entities/game_over.py b/src_old/entities/game_over.py similarity index 100% rename from src/entities/game_over.py rename to src_old/entities/game_over.py diff --git a/src_old/entities/pipe.py b/src_old/entities/pipe.py new file mode 100644 index 00000000..f8b1851f --- /dev/null +++ b/src_old/entities/pipe.py @@ -0,0 +1,104 @@ +import random +from typing import List + +from ..utils import GameConfig +from .entity import Entity + + +class Pipe(Entity): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.vel_x = -5 + + def draw(self) -> None: + self.x += self.vel_x + super().draw() + + +class Pipes(Entity): + upper: List[Pipe] + lower: List[Pipe] + + def __init__(self, config: GameConfig) -> None: + super().__init__(config) + self.pipe_gap = 120 + self.top = 0 + self.bottom = self.config.window.viewport_height + self.upper = [] + self.lower = [] + self.spawn_initial_pipes() + + def tick(self) -> None: + if self.can_spawn_pipes(): + self.spawn_new_pipes() + self.remove_old_pipes() + + for up_pipe, low_pipe in zip(self.upper, self.lower): + up_pipe.tick() + low_pipe.tick() + + def stop(self) -> None: + for pipe in self.upper + self.lower: + pipe.vel_x = 0 + + def can_spawn_pipes(self) -> bool: + last = self.upper[-1] + if not last: + return True + + return self.config.window.width - (last.x + last.w) > last.w * 2.5 + + def spawn_new_pipes(self): + # add new pipe when first pipe is about to touch left of screen + upper, lower = self.make_random_pipes() + self.upper.append(upper) + self.lower.append(lower) + + def remove_old_pipes(self): + # remove first pipe if its out of the screen + for pipe in self.upper: + if pipe.x < -pipe.w: + self.upper.remove(pipe) + + for pipe in self.lower: + if pipe.x < -pipe.w: + self.lower.remove(pipe) + + def spawn_initial_pipes(self): + upper_1, lower_1 = self.make_random_pipes() + upper_1.x = self.config.window.width + upper_1.w * 3 + lower_1.x = self.config.window.width + upper_1.w * 3 + self.upper.append(upper_1) + self.lower.append(lower_1) + + upper_2, lower_2 = self.make_random_pipes() + upper_2.x = upper_1.x + upper_1.w * 3.5 + lower_2.x = upper_1.x + upper_1.w * 3.5 + self.upper.append(upper_2) + self.lower.append(lower_2) + + def make_random_pipes(self): + """returns a randomly generated pipe""" + # y of gap between upper and lower pipe + base_y = self.config.window.viewport_height + + gap_y = random.randrange(0, int(base_y * 0.6 - self.pipe_gap)) + gap_y += int(base_y * 0.2) + pipe_height = self.config.images.pipe[0].get_height() + pipe_x = self.config.window.width + 10 + + upper_pipe = Pipe( + self.config, + self.config.images.pipe[0], + pipe_x, + gap_y - pipe_height, + ) + + lower_pipe = Pipe( + self.config, + self.config.images.pipe[1], + pipe_x, + gap_y + self.pipe_gap, + ) + + return upper_pipe, lower_pipe diff --git a/src/entities/player.py b/src_old/entities/player.py similarity index 100% rename from src/entities/player.py rename to src_old/entities/player.py diff --git a/src/entities/score.py b/src_old/entities/score.py similarity index 100% rename from src/entities/score.py rename to src_old/entities/score.py diff --git a/src/entities/welcome_message.py b/src_old/entities/welcome_message.py similarity index 100% rename from src/entities/welcome_message.py rename to src_old/entities/welcome_message.py diff --git a/src/flappy.py b/src_old/flappy.py similarity index 100% rename from src/flappy.py rename to src_old/flappy.py diff --git a/src/utils/__init__.py b/src_old/utils/__init__.py similarity index 100% rename from src/utils/__init__.py rename to src_old/utils/__init__.py diff --git a/src/utils/constants.py b/src_old/utils/constants.py similarity index 100% rename from src/utils/constants.py rename to src_old/utils/constants.py diff --git a/src/utils/game_config.py b/src_old/utils/game_config.py similarity index 100% rename from src/utils/game_config.py rename to src_old/utils/game_config.py diff --git a/src/utils/images.py b/src_old/utils/images.py similarity index 100% rename from src/utils/images.py rename to src_old/utils/images.py diff --git a/src/utils/sounds.py b/src_old/utils/sounds.py similarity index 100% rename from src/utils/sounds.py rename to src_old/utils/sounds.py diff --git a/src/utils/utils.py b/src_old/utils/utils.py similarity index 100% rename from src/utils/utils.py rename to src_old/utils/utils.py diff --git a/src/utils/window.py b/src_old/utils/window.py similarity index 100% rename from src/utils/window.py rename to src_old/utils/window.py