From 5bdcccfb5f7e6d82e013edc48a627d87417993cb Mon Sep 17 00:00:00 2001 From: LZH Date: Sun, 23 Feb 2025 11:13:57 +0800 Subject: [PATCH] commit all modifications for the first time --- code/ch03/ch03_02.py | 1 + code/ch03/ch03_03.py | 1 + code/ch03/ch03_04.py | 6 +-- code/ch03/ch03_05.py | 4 +- code/ch03/ch03_06.py | 2 + code/ch04/snake.py | 42 +++++++++++---------- code/ch05/brick.py | 38 +++++++++---------- code/ch05/brick_better.py | 2 +- code/ch05/pong.py | 10 ++--- code/ch06/flappy_bird.py | 79 +++++++++++++++++++++------------------ 10 files changed, 100 insertions(+), 85 deletions(-) diff --git a/code/ch03/ch03_02.py b/code/ch03/ch03_02.py index 7f35caa..0d0e6cf 100644 --- a/code/ch03/ch03_02.py +++ b/code/ch03/ch03_02.py @@ -18,6 +18,7 @@ #Circle(surface, color, center, radius, thickness...0 for fill) pygame.draw.circle(display_surface, BLUE, (WINDOW_WIDTH//2, WINDOW_HEIGHT//2), 150) +pygame.draw.circle(display_surface,WHITE,(WINDOW_WIDTH//2, WINDOW_HEIGHT//2),50) #Rectangle(surface, color, (top-left x, top-left y, width, height)) pygame.draw.rect(display_surface, BLUE, (0, 0, 100, 100)) diff --git a/code/ch03/ch03_03.py b/code/ch03/ch03_03.py index 60fb1bb..1ea2e97 100644 --- a/code/ch03/ch03_03.py +++ b/code/ch03/ch03_03.py @@ -26,6 +26,7 @@ running = False if event.type == pygame.KEYDOWN: + print(event.type) if event.key == pygame.K_b: circle_color = BLUE elif event.key == pygame.K_g: diff --git a/code/ch03/ch03_04.py b/code/ch03/ch03_04.py index 55ae59d..e45d4bf 100644 --- a/code/ch03/ch03_04.py +++ b/code/ch03/ch03_04.py @@ -14,7 +14,7 @@ GRAY = (127, 127, 127) #Give a background color to the display -display_surface.fill(WHITE) +display_surface.fill(GRAY) #Create images...returns a Surface object with the image drawon on it. #We can then get the rect of the surface and use the rect to position the image. @@ -25,7 +25,7 @@ font = pygame.font.Font('WenQuan.ttf', 32) text = font.render("飞龙在天", True, GRAY, WHITE) text_rect = text.get_rect() -text_rect.center = (WINDOW_WIDTH//2, text_rect.height//2) +text_rect.center = (WINDOW_WIDTH//2, text_rect.height//2+1) #The main game loop running = True @@ -36,7 +36,7 @@ #Blit (copy) a surface object at the given coordinates to our display display_surface.blit(dragon_image, dragon_rect) - pygame.draw.rect(display_surface, GRAY,dragon_rect, 4) + pygame.draw.rect(display_surface, WHITE,dragon_rect, 4) display_surface.blit(text, text_rect) #Update the display diff --git a/code/ch03/ch03_05.py b/code/ch03/ch03_05.py index 6211887..0c8cc3c 100644 --- a/code/ch03/ch03_05.py +++ b/code/ch03/ch03_05.py @@ -1,4 +1,6 @@ import pygame +from pygame import K_ESCAPE + pygame.init() WINDOW_WIDTH = 600 @@ -28,7 +30,7 @@ running = True while running: for event in pygame.event.get(): - if event.type == pygame.QUIT: + if event.type == pygame.QUIT or event.type == pygame.KEYUP: running = False dragon_rect.x += speed[0] diff --git a/code/ch03/ch03_06.py b/code/ch03/ch03_06.py index 351dd26..046c2f6 100644 --- a/code/ch03/ch03_06.py +++ b/code/ch03/ch03_06.py @@ -41,6 +41,8 @@ # 键盘连续移动,常用于按住操作,例如方向移动 keys = pygame.key.get_pressed() + print(keys) + print(pygame.K_a) if keys[pygame.K_a]: dragon_rect.x -= speed[0] if keys[pygame.K_d]: diff --git a/code/ch04/snake.py b/code/ch04/snake.py index 36eb19f..672f20c 100644 --- a/code/ch04/snake.py +++ b/code/ch04/snake.py @@ -3,9 +3,9 @@ from collections import namedtuple from pygame.locals import K_RIGHT,K_LEFT,K_UP,K_DOWN,QUIT -Position = namedtuple('Point', 'x, y') +Position = namedtuple('Point', 'x, y') #定义带名字的元组,方便用.x和.y来使用坐标值 -class Direction: +class Direction: #不需要实例,直接使用类属性 right = 0 left = 1 up = 2 @@ -14,11 +14,11 @@ class Direction: class Snake: def __init__(self,block_size): - self.blocks=[] - self.blocks.append(Position(20,15)) - self.blocks.append(Position(19,15)) + self.blocks=[] #用列表储存蛇头蛇身坐标 + self.blocks.append(Position(20,15)) #放蛇头 + self.blocks.append(Position(19,15)) #放一个蛇身 self.block_size = block_size - self.current_direction = Direction.right + self.current_direction = Direction.right #记录当前移动方向 self.image = pygame.image.load('snake.png') def move(self): @@ -31,8 +31,9 @@ def move(self): else: movesize = (0, 1) head = self.blocks[0] - new_head = Position(head.x + movesize[0], head.y + movesize[1]) - self.blocks.insert(0,new_head) + new_head = Position(head.x + movesize[0], head.y + movesize[1]) #当前蛇头坐标朝移动方向增加1,获得新的坐标 + self.blocks.insert(0,new_head) #在移动方向增加一个新蛇头,相当于移动一格 + def handle_input(self): keys = pygame.key.get_pressed() @@ -49,13 +50,13 @@ def handle_input(self): def draw(self,surface,frame): for index, block in enumerate(self.blocks): positon = (block.x * self.block_size, - block.y * self.block_size) + block.y * self.block_size) #计算绘图的位置坐标,格子数×格子边长的像素数 if index == 0: src = (((self.current_direction * 2) + frame) * self.block_size, - 0, self.block_size, self.block_size) + 0, self.block_size, self.block_size) #通过current_direction来选择对应方向的蛇头小图片,frame用来切换蛇头张嘴和闭嘴的小图片 else: src = (8 * self.block_size, 0, self.block_size, self.block_size) - surface.blit(self.image, positon, src) + surface.blit(self.image, positon, src) #第三个参数src用来切割源图片的切割坐标(左上角横坐标,左上角纵坐标,长尺寸,宽尺寸) class Berry: @@ -76,13 +77,14 @@ class Wall: def __init__(self,block_size): self.block_size = block_size - self.map = self.load_map('map.txt') + self.map = self.load_map('map.txt') #map.txt用数字来描述地图,1代表放置墙,其他数字代表不放 self.image = pygame.image.load('wall.png') def load_map(self,fileName): with open(fileName,'r') as map_file: content = map_file.readlines() - content = [list(line.strip()) for line in content] + content = [list(line.strip()) for line in content] #嵌套列表[[行1],[行2],...,[行n]] + # print(content) return content def draw(self,surface): @@ -100,8 +102,8 @@ def __init__(self,Width=640, Height=480): pygame.init() self.block_size = 16 self.Win_width , self.Win_height = (Width, Height) - self.Space_width = self.Win_width//self.block_size-2 - self.Space_height = self.Win_height//self.block_size-2 + self.Space_width = self.Win_width//self.block_size-2 #格子的宽度数 + self.Space_height = self.Win_height//self.block_size-2 #格子的高度数 self.surface = pygame.display.set_mode((self.Win_width, self.Win_height)) self.score = 0 self.frame = 0 @@ -114,7 +116,7 @@ def __init__(self,Width=640, Height=480): self.wall = Wall(self.block_size) self.position_berry() - def position_berry(self): + def position_berry(self): #在格子里随机放置果子 bx = random.randint(1, self.Space_width) by = random.randint(1, self.Space_height) self.berry.position = Position(bx, by) @@ -125,11 +127,11 @@ def position_berry(self): def berry_collision(self): head = self.snake.blocks[0] if (head.x == self.berry.position.x and - head.y == self.berry.position.y): + head.y == self.berry.position.y): #碰到果子,重新随机放置一个果子,分数加1 self.position_berry() self.score += 1 else: - self.snake.blocks.pop() + self.snake.blocks.pop() #没碰到果子,蛇尾减1个 def head_hit_body(self): head = self.snake.blocks[0] @@ -166,8 +168,8 @@ def play(self): if event.type == QUIT: self.running = False - self.frame = (self.frame + 1) % 2 - self.snake.handle_input() + self.frame = (self.frame + 1) % 2 #通过0、1变化来控制蛇头张嘴闭嘴动画 + self.snake.handle_input() #蛇头方向 self.berry_collision() if self.head_hit_wall() or self.head_hit_body(): print('Final Score', self.score) diff --git a/code/ch05/brick.py b/code/ch05/brick.py index 4fcb720..b7393a1 100644 --- a/code/ch05/brick.py +++ b/code/ch05/brick.py @@ -14,7 +14,7 @@ def draw(self, surface): def update(self,win_width): mousex, _ = pygame.mouse.get_pos() - if (mousex > win_width - self.rect.width): + if (mousex > win_width - self.rect.width): #横坐标为左上角,如果左上角到右边框距离小于球拍的宽度就需要把横坐标设置为到右边框一个拍长距离的位置 mousex = win_width - self.rect.width self.rect.topleft = (mousex, self.mousey) @@ -28,13 +28,13 @@ def __init__(self,win_width): self.reset(win_width) def reset(self,win_width,startY=220,speed=5, degree=45): - self.served = False + self.served = False #表示球是否移动,False为静止 self.positionX = random.randint(0,win_width) self.positionY = startY self.rect.topleft = (self.positionX, self.positionY) self.speed = speed - self.speedX = self.speed * sin(radians(degree)) - self.speedY = self.speed * cos(radians(degree)) + self.speedX = self.speed * sin(radians(degree)) #横轴移动分量 + self.speedY = self.speed * cos(radians(degree)) #纵轴移动分量 def draw(self, surface): surface.blit(self.image, self.rect) @@ -63,7 +63,7 @@ class Bricks: def __init__(self,row=5, col=12): self.image = pygame.image.load('brick.png') self.rect = self.image.get_rect() - self.contains = [] + self.contains = [] #用列表存储每块砖的坐标和尺寸 for y in range(row): brickY = (y * 24) + 100 for x in range(col): @@ -95,29 +95,29 @@ def __init__(self, Width=800,Height=600): pygame.display.set_caption('Bricks') - def bat_collision(self): + def bat_collision(self): #球和球板的碰撞检测 if self.ball.rect.colliderect(self.bat.rect): - self.ball.rect.bottom = self.bat.mousey + self.ball.rect.bottom = self.bat.mousey #把球置于球板上方,避免球板侧面与球碰撞情况时异常 self.ball.speed += 0.1 - diff_x = self.ball.rect.centerx - self.bat.rect.centerx - diff_ratio = min(0.95,abs(diff_x)/(0.5*self.bat.rect.width)) - theta = asin(diff_ratio) + diff_x = self.ball.rect.centerx - self.bat.rect.centerx #球中心到球板中心的横向距离 + diff_ratio = min(0.95,abs(diff_x)/(0.5*self.bat.rect.width)) #计算比例,为避免侧面碰撞时出现大于1的情况,最多取到0.95 + theta = asin(diff_ratio) #反正弦函数获取角度值,diff_ratio越小则角度值越小 self.ball.speedX = self.ball.speed * sin(theta) self.ball.speedY = self.ball.speed * cos(theta) - self.ball.speedY *= -1 - if (diff_x<0 and self.ball.speedX>0) or (diff_x>0 and self.ball.speedX<0): + self.ball.speedY *= -1 #球转向上飞 + if (diff_x<0 and self.ball.speedX>0) or (diff_x>0 and self.ball.speedX<0): #控制球反弹方向,碰到球板左侧往左反弹,右侧往右反弹 self.ball.speedX *= -1 - def bricks_collision(self): - brickHitIndex = self.ball.rect.collidelist(self.bricks.contains) + def bricks_collision(self): #球和砖块的碰撞检测 + brickHitIndex = self.ball.rect.collidelist(self.bricks.contains) #collidelist函数用来检测一个rect对象和一组rect对象之间的碰撞,没有碰撞返回-1,发生碰撞返回列表索引 if brickHitIndex >= 0: brick = self.bricks.contains[brickHitIndex] if (self.ball.rect.centerx > brick.right or self.ball.rect.centerx < brick.left): - self.ball.speedX *= -1 + self.ball.speedX *= -1 #左右侧碰撞则球横坐标反转 else: - self.ball.speedY *= -1 - del (self.bricks.contains[brickHitIndex]) + self.ball.speedY *= -1 #上下侧碰撞则球横坐标反转 + del (self.bricks.contains[brickHitIndex]) #从contains里删除碰撞的砖块 self.score +=1 if len(self.bricks.contains)==0: self.running = False @@ -127,13 +127,13 @@ def check_failed(self): self.ball.reset(self.Win_width) self.score = 0 - def draw_data(self): + def draw_data(self): #绘制分数 score_text = "得分:{score}".format(score=self.score) score_img = self.font.render(score_text, 1, Game.WHITE) score_rect = score_img.get_rect(centerx=self.Win_width//2, top=5) self.surface.blit(score_img, score_rect) - def draw(self): + def draw(self): #绘制所有元素 self.surface.fill(Game.BLACK) self.draw_data() self.bricks.draw(self.surface) diff --git a/code/ch05/brick_better.py b/code/ch05/brick_better.py index 75b8282..97ef1cd 100644 --- a/code/ch05/brick_better.py +++ b/code/ch05/brick_better.py @@ -10,7 +10,7 @@ class Resources: def __init__(self): - self.explosion = [] + self.explosion = [] #列表储存爆炸连续变化的图片 for i in range(1,5): self.explosion.append(pygame.image.load(f"item-feedback/item-feedback-{i}.png")) diff --git a/code/ch05/pong.py b/code/ch05/pong.py index 0acbafa..c306a45 100644 --- a/code/ch05/pong.py +++ b/code/ch05/pong.py @@ -89,7 +89,7 @@ def draw(self): def paddle_collision(self,paddle): if (self.ball.rect.centery >= paddle.rect.top and - self.ball.rect.centery <= paddle.rect.bottom): + self.ball.rect.centery <= paddle.rect.bottom): #控制球心坐标在球板顶和低之间,避免顶部和底部的侧面与球相碰的情况 if self.ball.rect.colliderect(paddle.rect): self.ball.x_vel *= -1 @@ -111,14 +111,14 @@ def handle_collision(self): self.left_score += 1 self.ball.reset() - if self.ball.x_vel < 0: + if self.ball.x_vel < 0: #根据横轴球移动增加值的正负来选择使用哪侧的球板进行碰撞检测 self.paddle_collision(self.left_paddle) else: self.paddle_collision(self.right_paddle) def handle_paddle_movement(self): - keys = pygame.key.get_pressed() + keys = pygame.key.get_pressed() #按w和s键会出现没有响应,这是因为处于中文输入模式,需要先切换为英文输入 if (keys[pygame.K_w] and self.left_paddle.rect.top - self.left_paddle.VEL >= 0): self.left_paddle.move(up=True) @@ -132,7 +132,7 @@ def handle_paddle_movement(self): self.right_paddle.rect.bottom + self.right_paddle.VEL <= self.Win_height): self.right_paddle.move(up=False) - def game_is_win(self): + def game_is_win(self): #有一侧得分大于等于win_score则该侧赢得游戏 won = False if self.left_score >= self.win_score: won = True @@ -145,7 +145,7 @@ def game_is_win(self): self.surface.blit(text, (self.Win_width//2 - text.get_width() // 2, self.Win_height//2 - text.get_height()//2)) pygame.display.update() - pygame.time.delay(5000) + pygame.time.delay(5000) #窗口冻结5s self.ball.reset() self.left_paddle.reset() self.right_paddle.reset() diff --git a/code/ch06/flappy_bird.py b/code/ch06/flappy_bird.py index 89e41cc..3d046f5 100644 --- a/code/ch06/flappy_bird.py +++ b/code/ch06/flappy_bird.py @@ -5,15 +5,22 @@ class Bird(pygame.sprite.Sprite): def __init__(self, x, y): - super().__init__() - self.images = [] - self.index = 0 - self.counter = 0 + super().__init__() #运行父类初始化 + self.images = [] #由3张图片组成小鸟飞行动画 + self.index = 0 #用来选取存在images里的不同图片 + self.counter = 0 #用于帧数累加计算,用来控制小鸟图片变化的时间,使翅膀扇动频率变慢 self.vel = 0 self.cap = 10 self.flying = False self.failed = False - self.clicked = False + ''' + flying failed + 0 0 游戏开始,小鸟处于准备飞行状态 + 1 0 游戏进行,小鸟处于飞行状态 + 1 1 碰撞导致游戏失败,小鸟处于掉落状态 + 0 1 游戏失败停止,小鸟落到地面 + ''' + self.clicked = False #用来判断是否被单击过,避免按住不放 for num in range (1, 4): img = pygame.image.load(f"resources/bird{num}.png") self.images.append(img) @@ -23,21 +30,21 @@ def __init__(self, x, y): self.wing = pygame.mixer.Sound('resources/wing.wav') def handle_input(self): - if pygame.mouse.get_pressed()[0] == 1 and not self.clicked : + if pygame.mouse.get_pressed()[0] == 1 and not self.clicked : #鼠标左键被单击,并且之前没被单击过 self.clicked = True - self.vel = -1 * self.cap + self.vel = -1 * self.cap #速度变量设为-10,使小鸟向上移动 self.wing.play() - if pygame.mouse.get_pressed()[0] == 0: + if pygame.mouse.get_pressed()[0] == 0: #鼠标左键被松开 self.clicked = False - def animation(self): + def animation(self): #实现小鸟扇动翅膀和上升时仰头、下降时低头的动画 flap_cooldown = 5 self.counter += 1 - if self.counter > flap_cooldown: + if self.counter > flap_cooldown: #每5帧更换一次图片 self.counter = 0 self.index = (self.index + 1) % 3 self.image = self.images[self.index] - self.image = pygame.transform.rotate(self.images[self.index], self.vel * -2) + self.image = pygame.transform.rotate(self.images[self.index], self.vel * -2) #第二个参数为旋转角度,利用速度变量来算出角度 def touch_ground(self): return self.rect.bottom >= Game.ground_y @@ -45,11 +52,11 @@ def touch_ground(self): def update(self): if self.flying : self.handle_input() - self.vel += 0.5 + self.vel += 0.5 #增加向下的速度变量模拟重力加速度 if self.vel > 8: self.vel = 8 if not self.touch_ground(): - self.rect.y += int(self.vel) + self.rect.y += int(self.vel) #没有碰到地面就更新小鸟的纵坐标 if not self.failed: self.animation() @@ -60,18 +67,18 @@ def update(self): class Pipe(pygame.sprite.Sprite): - scroll_speed = 4 - pipe_gap = 180 + scroll_speed = 4 #钢管移动速度 + pipe_gap = 180 #上下钢管间的空隙 def __init__(self, x, y, is_top): super().__init__() - self.passed = False - self.is_top = is_top + self.passed = False #钢管是否被小鸟穿越 + self.is_top = is_top #判断是否为上方钢管 self.image = pygame.image.load("resources/pipe.png") self.rect = self.image.get_rect() if is_top : - self.image = pygame.transform.flip(self.image, False, True) + self.image = pygame.transform.flip(self.image, False, True) #沿y轴反转钢管图片 self.rect.bottomleft = [x, y - Pipe.pipe_gap // 2] else: self.rect.topleft = [x, y + Pipe.pipe_gap // 2] @@ -79,7 +86,7 @@ def __init__(self, x, y, is_top): def update(self): self.rect.x -= Pipe.scroll_speed if self.rect.right < 0: - self.kill() + self.kill() #超出左边界删除这个钢管对象 class Button: @@ -91,7 +98,7 @@ def pressed(self, event): pressed = False if event.type == MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() - if self.rect.collidepoint(pos): + if self.rect.collidepoint(pos): #判断坐标值是否在边框内部 pressed = True return pressed @@ -107,18 +114,18 @@ def __init__(self,Width=600,Height=800): self.surface = pygame.display.set_mode((self.Win_width, self.Win_height)) self.ground_x = 0 self.score = 0 - self.pipe_counter = 0 - self.observed = dict() + self.pipe_counter = 0 #生成钢管时间间隔计数 + self.observed = dict() #创建字典,用来保存没有被小鸟穿越且最靠近小鸟的右侧钢管的间隙坐标 self.Clock = pygame.time.Clock() self.fps = 60 self.font = pygame.font.Font('resources/LuckiestGuy-Regular.ttf', 28) - self.images = self.loadImages() - self.sounds = self.loadSounds() - self.pipe_group = pygame.sprite.Group() - self.bird_group = pygame.sprite.Group() + self.images = self.loadImages() #字典,保持背景图片、大地图片 + self.sounds = self.loadSounds() #字典,保存碰撞音效、得分音效 + self.pipe_group = pygame.sprite.Group() #保存一组管道角色 + self.bird_group = pygame.sprite.Group() #保存一组小鸟角色(实际只存了一个) self.flappy = Bird(100, self.ground_y // 2) self.bird_group.add(self.flappy) - self.new_pipes(time=0) + self.new_pipes(time=0) #此时设置time为0是为了立刻生成一组钢管 self.button = Button(self.Win_width//2 , self.Win_height//2 ) pygame.display.set_caption('Flappy Bird') pygame.mixer.music.load('resources/BGMUSIC.mp3') @@ -147,16 +154,16 @@ def reset_game(self): def start_flying(self,event): if (event.type == pygame.MOUSEBUTTONDOWN and not self.flappy.flying - and not self.flappy.failed): + and not self.flappy.failed): #判断处于等待游戏开始状态再设置小鸟处于飞的状态 self.flappy.flying = True def game_restart(self,event): if (self.flappy.failed - and self.button.pressed(event)): + and self.button.pressed(event)): #处于游戏失败,且点击了restart按键,重置游戏 self.reset_game() def handle_collision(self): - if (pygame.sprite.groupcollide(self.bird_group, self.pipe_group, False, False) + if (pygame.sprite.groupcollide(self.bird_group, self.pipe_group, False, False) #组碰撞,两个角色组里任何角色碰撞返回真 or self.flappy.rect.top < 0 or self.flappy.rect.bottom >= Game.ground_y): self.flappy.failed = True @@ -170,7 +177,7 @@ def ground_update(self): def new_pipes(self, time = 90): self.pipe_counter += 1 - if self.pipe_counter >= time: + if self.pipe_counter >= time: #计数超过90,生成一组钢管 pipe_height = random.randint(-150, 150) top_pipe = Pipe(self.Win_width, self.ground_y // 2 + pipe_height, True) btm_pipe = Pipe(self.Win_width, self.ground_y // 2 + pipe_height, False) @@ -178,9 +185,9 @@ def new_pipes(self, time = 90): self.pipe_group.add(btm_pipe) self.pipe_counter = 0 - def get_pipe_dist(self): - pipe_2 = [pipe for pipe in self.pipe_group.sprites() if pipe.passed==False][:2] - for pipe in pipe_2: + def get_pipe_dist(self): #用于计算、保持passed的观测值,passed用来记录当前钢管是否被小鸟穿越,False为没有穿越 + pipe_2 = [pipe for pipe in self.pipe_group.sprites() if pipe.passed==False][:2] #取当前处于小鸟右侧钢管最靠近小鸟的前两根 + for pipe in pipe_2: #获取改组钢管的中间间隙的右侧上下坐标 if pipe.is_top: self.observed['pipe_dist_right'] = pipe.rect.right self.observed['pipe_dist_top'] = pipe.rect.bottom @@ -207,8 +214,8 @@ def draw_text(self,text,color,x,y): def draw(self): self.surface.blit(self.images['bg'],(0,0)) - self.pipe_group.draw(self.surface) - self.bird_group.draw(self.surface) + self.pipe_group.draw(self.surface) #绘制钢管组角色 + self.bird_group.draw(self.surface) #绘制小鸟组角色 self.surface.blit(self.images['ground'],(self.ground_x,self.ground_y)) self.draw_text(f'score: {self.score}', (255, 255, 255), 20, 20)