

Python,速成心法
敲代码,查资料,问度娘
练习,探索,总结,优化

★★★★★博文创作不易,使用代码的过程中,如有疑问的地方,欢迎大家指正留言交流。喜欢的老铁可以多多点赞+收藏分享+置顶,小红牛在此表示感谢。★★★★★
---------★Pygame教程★---------
Python经典游戏:太空飞机大战Space Plane Battle
Pygame经典游戏:坦克大战TankWar+五子棋人机对弈+俄罗斯方块(安排!!)
Pygame经典游戏:微信飞机大战Wechatflying(初级版)
Python经典游戏:走迷宫,好烦呀,我到现在还没有走出去!!
Pygame教程03:文本显示+字体加载+transform方法
Pygame教程04:draw方法绘制矩形、多边形、圆、椭圆、弧线、直线和线条等
Pygame教程05:帧动画原理+边界值检测,让小球来回上下运动
Pygame教程06:Event事件的类型+处理方法+监听鼠标事件
Pygame教程08:使用键盘方向键,控制小球,上下左右移动。
Pygame教程09:font.render文本内容,如何自动换行显示
实现经典打砖块(Arkanoid)的基本功能: 挡板移动,球反弹,砖块碰撞消除,游戏结束和胜利判定,分数显示。可以直接运行,不需要素材资源。

↓ 源码如下 ↓
import pygameimport sysimport math# 初始化Pygamepygame.init()# ---------- 常量定义 ----------SCREEN_WIDTH = 800SCREEN_HEIGHT = 600FPS = 60# 颜色 (RGB)BLACK = (0, 0, 0)WHITE = (255, 255, 255)RED = (255, 0, 0)GREEN = (0, 255, 0)BLUE = (0, 0, 255)YELLOW = (255, 255, 0)ORANGE = (255, 165, 0)PURPLE = (128, 0, 128)# 游戏对象尺寸PADDLE_WIDTH = 100PADDLE_HEIGHT = 15BALL_RADIUS = 8BRICK_WIDTH = 72 # 800 / 10 ≈ 80,留间隙,所以72BRICK_HEIGHT = 20BRICK_GAP = 2 # 砖块间隙# 砖块布局 (5行,10列)BRICK_ROWS = 5BRICK_COLS = 10# 球物理参数BALL_SPEED_X = 3BALL_SPEED_Y = -4MAX_HORIZONTAL_SPEED = 5 # 挡板反弹时的最大水平速度# 挡板移动速度PADDLE_SPEED = 8# 游戏状态STATE_READY = 0 # 等待发射,球跟随挡板STATE_PLAYING = 1 # 游戏进行中STATE_GAMEOVER = 2 # 游戏结束(球掉落)STATE_WIN = 3 # 胜利(所有砖块消除)# ---------- 支持中文的文字绘制函数 ----------# 提前创建字体对象以提高性能(在全局初始化时创建)def get_chinese_font(size):"""获取支持中文的字体对象,根据操作系统自动选择"""# 按优先级尝试常见中文字体名称font_names = ['simhei', # Windows 黑体'microsoftyahei', # Windows 微软雅黑'pingfang sc', # macOS 苹方'stheitisc', # macOS 华文黑体'wenquanyi zen hei',# Linux 文泉驿正黑'noto sans cjk sc' # Linux 思源黑体]for name in font_names:try:font = pygame.font.SysFont(name, size)# 简单测试能否渲染中文字符test_surf = font.render('中', True, WHITE)if test_surf.get_width() > 0:return fontexcept:continue# 如果所有系统字体都失败,尝试加载当前目录下的字体文件(如果有)try:font = pygame.font.Font('simhei.ttf', size)return fontexcept:pass# 最后的回退:使用默认字体(无法显示中文,但程序不会崩溃)return pygame.font.Font(None, size)# 为了提高效率,缓存不同尺寸的字体对象_font_cache = {}def draw_text(surface, text, size, x, y, color=WHITE):"""在屏幕上绘制文字(支持中文)"""key = sizeif key not in _font_cache:_font_cache[key] = get_chinese_font(size)font = _font_cache[key]text_surface = font.render(text, True, color)text_rect = text_surface.get_rect(center=(x, y))surface.blit(text_surface, text_rect)# ---------- 球碰撞与物理响应 ----------def collide_with_rect(ball_pos, ball_radius, rect, vx, vy):"""检测圆与矩形的碰撞,并修正位置和速度。返回: (new_vx, new_vy, collision_occurred)"""# 矩形中心rect_center = rect.center# 找到矩形上离圆心最近的点closest_x = max(rect.left, min(ball_pos.x, rect.right))closest_y = max(rect.top, min(ball_pos.y, rect.bottom))dx = ball_pos.x - closest_xdy = ball_pos.y - closest_ydistance = math.hypot(dx, dy)if distance < ball_radius:# 发生碰撞,修正位置if distance == 0:# 避免除以零,随便给一个方向overlap = ball_radiusdirection = pygame.math.Vector2(1, 0)else:overlap = ball_radius - distancedirection = pygame.math.Vector2(dx, dy).normalize()# 修正球的位置,使其刚好不再重叠ball_pos.x += direction.x * overlapball_pos.y += direction.y * overlap# 判断碰撞面:比较圆心与closest点的差值绝对值# 更精确的做法是比较重叠在x轴和y轴上的深度# 简单但有效的做法: 根据法线方向判断主要碰撞轴向if abs(dx) > abs(dy):vx = -vxelse:vy = -vyreturn vx, vy, Truereturn vx, vy, False# ---------- 主游戏类 ----------class Arkanoid:def __init__(self):self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))pygame.display.set_caption("打砖块 - Arkanoid")self.clock = pygame.time.Clock()# 游戏组件self.paddle = pygame.Rect(SCREEN_WIDTH//2 - PADDLE_WIDTH//2,SCREEN_HEIGHT - 40,PADDLE_WIDTH, PADDLE_HEIGHT)self.ball_pos = pygame.math.Vector2(SCREEN_WIDTH//2, SCREEN_HEIGHT - 60)self.ball_radius = BALL_RADIUSself.ball_speed = pygame.math.Vector2(BALL_SPEED_X, BALL_SPEED_Y)self.score = 0self.state = STATE_READY# 创建砖块self.bricks = []self.init_bricks()def init_bricks(self):"""初始化所有砖块"""self.bricks.clear()# 颜色渐变colors = [RED, ORANGE, YELLOW, GREEN, BLUE]for row in range(BRICK_ROWS):for col in range(BRICK_COLS):brick_x = col * (BRICK_WIDTH + BRICK_GAP) + BRICK_GAPbrick_y = row * (BRICK_HEIGHT + BRICK_GAP) + 60brick_rect = pygame.Rect(brick_x, brick_y, BRICK_WIDTH, BRICK_HEIGHT)# 为每一行分配不同颜色color = colors[row % len(colors)]self.bricks.append({'rect': brick_rect,'color': color})def reset_game(self):"""重置整个游戏"""self.paddle.x = SCREEN_WIDTH//2 - PADDLE_WIDTH//2self.ball_pos = pygame.math.Vector2(SCREEN_WIDTH//2, SCREEN_HEIGHT - 60)self.ball_speed = pygame.math.Vector2(BALL_SPEED_X, BALL_SPEED_Y)self.score = 0self.state = STATE_READYself.init_bricks()def handle_input(self):"""处理键盘输入"""keys = pygame.key.get_pressed()# 挡板移动(左右方向键)if keys[pygame.K_LEFT] and self.paddle.left > 0:self.paddle.x -= PADDLE_SPEEDif keys[pygame.K_RIGHT] and self.paddle.right < SCREEN_WIDTH:self.paddle.x += PADDLE_SPEED# 仅在READY状态时,空格发射if self.state == STATE_READY:if keys[pygame.K_SPACE]:self.state = STATE_PLAYING# 确保球有一个初始向上的速度if abs(self.ball_speed.y) < 2:self.ball_speed.y = -4self.ball_speed.x = BALL_SPEED_X# 在游戏结束或胜利时,按R键重新开始if self.state in (STATE_GAMEOVER, STATE_WIN):if keys[pygame.K_r]:self.reset_game()# 全局退出for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()def update_ready(self):"""处于待发射状态:球跟随挡板移动"""self.ball_pos.x = self.paddle.centerxself.ball_pos.y = self.paddle.top - self.ball_radiusdef update_playing(self):"""更新游戏逻辑(球移动,碰撞检测等)"""# 移动球self.ball_pos.x += self.ball_speed.xself.ball_pos.y += self.ball_speed.y# 1. 边界碰撞(左右上)if self.ball_pos.x - self.ball_radius <= 0:self.ball_pos.x = self.ball_radiusself.ball_speed.x = -self.ball_speed.xif self.ball_pos.x + self.ball_radius >= SCREEN_WIDTH:self.ball_pos.x = SCREEN_WIDTH - self.ball_radiusself.ball_speed.x = -self.ball_speed.xif self.ball_pos.y - self.ball_radius <= 0:self.ball_pos.y = self.ball_radiusself.ball_speed.y = -self.ball_speed.y# 2. 底部碰撞(游戏结束)if self.ball_pos.y + self.ball_radius >= SCREEN_HEIGHT:self.state = STATE_GAMEOVERreturn# 3. 挡板碰撞# 先检测球是否与挡板相交ball_rect = pygame.Rect(self.ball_pos.x - self.ball_radius,self.ball_pos.y - self.ball_radius,self.ball_radius*2, self.ball_radius*2)if ball_rect.colliderect(self.paddle):# 计算碰撞点相对于挡板中心的位置偏移(-1 到 1)offset = (self.ball_pos.x - self.paddle.centerx) / (self.paddle.width / 2)# 限制偏移范围offset = max(-1, min(1, offset))# 根据偏移改变水平速度,并设置垂直速度向上new_vx = offset * MAX_HORIZONTAL_SPEEDself.ball_speed.x = new_vx# 确保垂直速度向上且有一定的绝对值self.ball_speed.y = -abs(self.ball_speed.y)# 将球移到挡板之上避免卡住self.ball_pos.y = self.paddle.top - self.ball_radius# 4. 砖块碰撞(处理多次碰撞)# 使用循环,直到一次检测中没有碰撞发生,确保一帧内多次碰撞正确消除collision_occurred = Truemax_iter = 10 # 防止死循环iter_count = 0while collision_occurred and iter_count < max_iter:collision_occurred = Falsefor brick in self.bricks[:]: # 遍历副本以便删除rect = brick['rect']# 检测球和砖块碰撞vx, vy, hit = collide_with_rect(self.ball_pos, self.ball_radius,rect, self.ball_speed.x, self.ball_speed.y)if hit:self.ball_speed.x = vxself.ball_speed.y = vy# 删除砖块并加分self.bricks.remove(brick)self.score += 10collision_occurred = Truebreak # 一次只处理一个碰撞,避免一次跳跃多个响应异常iter_count += 1# 检查胜利条件if len(self.bricks) == 0:self.state = STATE_WINdef update(self):"""主更新函数,根据状态调用不同逻辑"""if self.state == STATE_READY:self.update_ready()elif self.state == STATE_PLAYING:self.update_playing()# GAMEOVER 和 WIN 状态下不需要更新游戏对象def draw(self):"""绘制所有元素"""self.screen.fill(BLACK)# 绘制砖块for brick in self.bricks:pygame.draw.rect(self.screen, brick['color'], brick['rect'])# 添加边框效果pygame.draw.rect(self.screen, WHITE, brick['rect'], 1)# 绘制挡板pygame.draw.rect(self.screen, WHITE, self.paddle)pygame.draw.rect(self.screen, BLUE, self.paddle, 3)# 绘制球pygame.draw.circle(self.screen, YELLOW,(int(self.ball_pos.x), int(self.ball_pos.y)),self.ball_radius)pygame.draw.circle(self.screen, WHITE,(int(self.ball_pos.x), int(self.ball_pos.y)),self.ball_radius, 2)# 绘制分数draw_text(self.screen, f"得分: {self.score}", 36, 70, 30, WHITE)# 根据状态显示提示文字(已改为中文)if self.state == STATE_READY:draw_text(self.screen, "按空格开始游戏", 40,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 100, YELLOW)draw_text(self.screen, "← → 移动挡板", 30,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 150, WHITE)elif self.state == STATE_GAMEOVER:draw_text(self.screen, "游戏结束", 60,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 - 50, RED)draw_text(self.screen, f"您的得分: {self.score}", 40,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 20, WHITE)draw_text(self.screen, "按 R 键重新开始", 30,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 100, YELLOW)elif self.state == STATE_WIN:draw_text(self.screen, "恭喜胜利!", 60,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 - 50, GREEN)draw_text(self.screen, f"最终得分: {self.score}", 40,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 20, WHITE)draw_text(self.screen, "按 R 键重新开始", 30,SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 100, YELLOW)# 额外显示砖块剩余数量if self.state == STATE_PLAYING:remaining = len(self.bricks)draw_text(self.screen, f"剩余砖块: {remaining}", 24,SCREEN_WIDTH - 80, 30, WHITE)pygame.display.flip()def run(self):"""主游戏循环"""while True:self.handle_input()self.update()self.draw()self.clock.tick(FPS)# ---------- 程序入口 ----------if __name__ == "__main__":game = Arkanoid()game.run()
完毕!!感谢您的收看
--------★★历史博文集合★★--------
