最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python基于Pygame開發(fā)的經(jīng)典打磚塊游戲

 更新時間:2026年06月17日 09:06:40   作者:Wang ruoxi  
Pygame 是 Python 中一個功能強(qiáng)大的 2D 游戲開發(fā)庫,它提供了處理圖形、聲音、輸入和事件循環(huán)的完整工具集,本文通過 Pygame 實現(xiàn)一個經(jīng)典打磚塊游戲(Breakout),需要的朋友可以參考下

項目概述

Pygame 是 Python 中一個功能強(qiáng)大的 2D 游戲開發(fā)庫,它提供了處理圖形、聲音、輸入和事件循環(huán)的完整工具集。本文通過 Pygame 實現(xiàn)一個經(jīng)典打磚塊游戲(Breakout)。

在這個游戲中,玩家控制屏幕底部的擋板,彈射小球擊碎上方所有磚塊。每塊磚擁有獨(dú)立的生命值,顏色隨血量變化而加深,擊中即扣血,歸零后磚塊消失并觸發(fā)粒子爆炸效果,目標(biāo)是在不失去所有球命的情況下清空全部磚塊。其中:

  • 移動擋板:左右方向鍵 / A、D 鍵水平移動擋板;擋板擊球時,根據(jù)球撞擊擋板的偏移位置改變出球角度,越靠近兩端反彈角度越大。
  • 發(fā)球:按空格鍵發(fā)球,發(fā)球方向在豎直向上的基礎(chǔ)上加入隨機(jī)偏角(±0.5 弧度),每局可多次發(fā)球(失球不扣關(guān)卡)。
  • 磚塊血量:不同行的磚塊初始血量不同(最高行血量最高),每次被球擊中扣 1 點血;血量 > 0 時磚塊上顯示剩余血量數(shù)字,血量歸零后磚塊消失。
  • 關(guān)卡遞進(jìn):清空全部磚塊后自動進(jìn)入下一關(guān),球速隨關(guān)卡提升(上限 9.0);當(dāng)前關(guān)卡、得分和最高分實時顯示在 HUD 欄。
  • 鍵盤操作:SPACE:發(fā)球;R:重新開始;P:暫停/繼續(xù)。
  • 勝利/失敗條件:球掉落到屏幕底部扣一條命;命數(shù)歸零 = 游戲失敗;清空所有磚塊 = 過關(guān)(自動進(jìn)入下一關(guān),無上限)。

游戲?qū)崿F(xiàn)

初始化與基礎(chǔ)設(shè)置

游戲啟動時初始化 Pygame 并定義屏幕尺寸、HUD 高度、顏色常量和磚塊布局參數(shù)。

pygame.init()

WIDTH, HEIGHT = 480, 580
HUD_H = 56

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Breakout: Brick Blaster")
clock = pygame.time.Clock()

顏色定義

DARK_BG = (3,   8,  16)   # 全局深色背景
BLUE1   = (56, 138, 221)   # 擋板主色 / 球外圈
BLUE2   = (133,183, 235)   # 擋板高光 / 球內(nèi)圈
WHITE   = (220, 225, 235)  # 文字 / 球高光
GRAY    = (100, 115, 130)  # 次要文字
RED     = (226,  75,  74)  # 生命指示圓 / 游戲結(jié)束文字
GOLD    = (250, 199, 117)  # 暫停文字
GREEN   = (93,  202, 165)  # 關(guān)卡文字

磚塊顏色與布局

BRICK_COLORS = [
    (83,  74, 183),   # 紫色  第 0 行(最高血量)
    (24,  95, 165),   # 藍(lán)色  第 1 行
    (15, 110,  86),   # 青色  第 2 行
    (59, 109,  17),   # 綠色  第 3 行
    (186,117,  23),   # 琥珀  第 4 行
    (152,  60,  29),  # 珊瑚  第 5 行(最低血量)
]

PAD_W, PAD_H = 80, 12     # 擋板寬高
BALL_R = 8                 # 小球半徑
COLS, ROWS_BRICKS = 8, 6   # 磚塊列數(shù)、行數(shù)
B_GAP  = 6                 # 磚塊間距
BW = (WIDTH - 40 - (COLS - 1) * B_GAP) // COLS  # 單塊寬度
BH = 18                    # 單塊高度
``

### 字體加載

```python
CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"

font_sm  = pygame.font.Font(CHINESE_FONT_PATH, 20)
font_md  = pygame.font.Font(CHINESE_FONT_PATH, 28)
font_big = pygame.font.Font(CHINESE_FONT_PATH, 52)

直接加載系統(tǒng)中文字體文件,保證中文 HUD 文字正常顯示??缙脚_時可替換為 pygame.font.SysFont(None, size)

核心類設(shè)計

1. 擋板類(Paddle)

Paddle 類封裝擋板的位置、移動邏輯和繪制方法。

構(gòu)造函數(shù) __init__

def __init__(self):
    self.x = WIDTH // 2   # 水平中心位置(以擋板中心為基準(zhǔn))
    self.y = HEIGHT - 36  # 固定在屏幕底部附近
    self.speed = 7        # 每幀移動像素數(shù)

移動方法 move

def move(self, dx):
    self.x = max(PAD_W // 2, min(WIDTH - PAD_W // 2, self.x + dx))

通過 max/min 將擋板限制在屏幕邊界內(nèi),防止超出屏幕。

矩形區(qū)域 rect

def rect(self):
    return pygame.Rect(self.x - PAD_W // 2, self.y, PAD_W, PAD_H)

self.x 為中心計算左邊界,便于后續(xù)碰撞檢測復(fù)用。

繪制方法 draw

def draw(self, surf):
    r = self.rect()
    pygame.draw.rect(surf, BLUE1, r, border_radius=6)               # 底層主色
    pygame.draw.rect(surf, BLUE2, r.inflate(-20, -4), border_radius=4)  # 高光條

用兩層矩形疊加實現(xiàn)簡潔的高光立體感:外層為深藍(lán)主色,內(nèi)層收縮后繪制淺藍(lán)高光條。

2. 小球類(Ball)

Ball 類管理小球的物理運(yùn)動、邊界反彈和出屏檢測。

構(gòu)造函數(shù) __init__

def __init__(self, px, py):
    self.x = float(px)
    self.y = float(py - BALL_R - 2)   # 初始貼在擋板上方
    self.vx = 0.0
    self.vy = 0.0
    self.launched = False              # 是否已發(fā)球
    self.radius = BALL_R

未發(fā)球時小球跟隨擋板水平移動,按空格后才獲得速度。

發(fā)球方法 launch

def launch(self, speed):
    ang = -math.pi / 2 + random.uniform(-0.5, 0.5)   # 在豎直向上基礎(chǔ)上隨機(jī)偏角
    self.vx = speed * math.cos(ang)
    self.vy = speed * math.sin(ang)
    self.launched = True

使用極坐標(biāo)分解速度,random.uniform(-0.5, 0.5) 約 ±28.6°的隨機(jī)偏角讓每局發(fā)球方向各不相同,增加趣味性。

更新方法 update

def update(self, pad_x):
    if not self.launched:
        self.x = float(pad_x)   # 未發(fā)球時跟隨擋板
        return
    self.x += self.vx
    self.y += self.vy
    # 左右邊界反彈
    if self.x - self.radius < 0:
        self.x = float(self.radius); self.vx = abs(self.vx)
    if self.x + self.radius > WIDTH:
        self.x = float(WIDTH - self.radius); self.vx = -abs(self.vx)
    # 頂部邊界反彈(HUD 下沿)
    if self.y - self.radius < HUD_H:
        self.y = float(HUD_H + self.radius); self.vy = abs(self.vy)

三面墻反彈均使用 abs() 強(qiáng)制方向,避免因浮點誤差導(dǎo)致球"穿墻"后速度方向錯誤的情況。

出屏檢測 off_screen

def off_screen(self):
    return self.y - self.radius > HEIGHT

繪制方法 draw

def draw(self, surf):
    ix, iy = int(self.x), int(self.y)
    pygame.draw.circle(surf, BLUE1, (ix, iy), self.radius + 2)  # 外發(fā)光圈
    pygame.draw.circle(surf, BLUE2, (ix, iy), self.radius)       # 主球體
    pygame.draw.circle(surf, WHITE, (ix - 2, iy - 2), 3)         # 高光點

三層圓形疊繪:外層比主球半徑大 2px 的深藍(lán)光暈,中層淺藍(lán)主體,右上角白色高光點,使球體具有立體感。

3. 磚塊類(Brick)

Brick 類封裝單塊磚的狀態(tài)、血量和帶漸變的繪制邏輯。

構(gòu)造函數(shù) __init__

def __init__(self, col, row, hp):
    self.col    = col
    self.row    = row
    self.hp     = hp        # 當(dāng)前血量
    self.max_hp = hp        # 初始最大血量(用于計算顏色漸變比例)
    self.alive  = True
    self.x = 20 + col * (BW + B_GAP)          # 像素坐標(biāo)(左上角)
    self.y = HUD_H + 20 + row * (BH + B_GAP)

繪制方法 draw

def draw(self, surf):
    if not self.alive:
        return
    frac  = self.hp / self.max_hp                       # 血量比例 0.0~1.0
    base  = BRICK_COLORS[self.row % len(BRICK_COLORS)]
    color = tuple(int(c * (0.4 + 0.6 * frac)) for c in base)  # 血量越少顏色越暗
    r = self.rect()
    pygame.draw.rect(surf, color, r, border_radius=4)
    border_a = int(80 * frac)                           # 邊框透明度隨血量變化
    pygame.draw.rect(surf, (*WHITE[:3], border_a), r, width=1, border_radius=4)
    if self.max_hp > 1:
        label = font_sm.render(str(self.hp), True,
                               tuple(min(255, c + 120) for c in color))
        surf.blit(label, (r.centerx - label.get_width()  // 2,
                          r.centery - label.get_height() // 2))

該方法實現(xiàn)了三層視覺反饋:

  1. 顏色變暗0.4 + 0.6 * frac 將血量映射到亮度,滿血時最亮,瀕死時保留 40% 亮度(不完全變黑)。
  2. 邊框漸隱:白色邊框透明度隨血量同步減弱,強(qiáng)化"受損"的視覺感。
  3. 血量數(shù)字:血量 > 1 的磚塊在中心繪制剩余血量,文字色在磚塊底色基礎(chǔ)上提亮 120,保證可讀性。

4. 粒子類(Particle)

Particle 類實現(xiàn)擊磚時的粒子爆炸效果,模擬帶重力的碎片運(yùn)動。

構(gòu)造函數(shù) __init__

def __init__(self, x, y, color):
    self.x    = float(x)
    self.y    = float(y)
    self.vx   = random.uniform(-3, 3)    # 水平隨機(jī)速度
    self.vy   = random.uniform(-4, 0.5)  # 初始向上(含少量向下)
    self.life = 28                        # 生命幀數(shù)
    self.color = color

更新方法 update

def update(self):
    self.x  += self.vx
    self.y  += self.vy
    self.vy += 0.2     # 模擬重力加速度
    self.life -= 1

每幀對 vy 施加 0.2 的向下加速度,模擬重力拋物線軌跡。

繪制方法 draw

def draw(self, surf):
    if self.life <= 0:
        return
    a = min(255, self.life * 9)          # 透明度隨生命衰減
    s = pygame.Surface((5, 5), pygame.SRCALPHA)
    s.fill((*self.color[:3], a))
    surf.blit(s, (int(self.x), int(self.y)))

使用 SRCALPHA 透明 Surface 繪制帶 Alpha 通道的小方塊,life * 9 使粒子在約 28 幀內(nèi)從不透明線性淡出至消失。

5. 游戲主類(Game)

Game 類統(tǒng)籌所有子系統(tǒng),負(fù)責(zé)關(guān)卡管理、碰撞檢測、狀態(tài)更新和畫面渲染。

構(gòu)造函數(shù) __init__

def __init__(self):
    self.best = 0
    self.hp_values = [13, 11, 7, 5, 3, 2]   # 各行磚塊初始血量(由上至下遞減)
    self.reset()

hp_values 列表決定每行磚塊的初始生命值,第 0 行(最上方)最厚實,第 5 行(最下方)最薄,鼓勵玩家優(yōu)先擊打下方磚塊。

關(guān)卡初始化 _init_level

def _init_level(self):
    self.paddle = Paddle()
    self.ball   = Ball(self.paddle.x, self.paddle.y)
    self.bricks = [
        Brick(c, r, self.hp_values[r])
        for r in range(ROWS_BRICKS)
        for c in range(COLS)
    ]

使用列表推導(dǎo)式快速生成 6×8 共 48 塊磚,每行血量由 hp_values[r] 統(tǒng)一控制。

事件處理 handle_events

def handle_events(self):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                self.reset()
            if event.key == pygame.K_p:
                self.paused = not self.paused
            if event.key == pygame.K_SPACE and not self.ball.launched:
                self.ball.launch(self.ball_speed)

核心更新 update

update 方法集中處理三段碰撞檢測邏輯:

def update(self):
    if self.paused or self.game_over or self.win:
        return

    # ① 擋板移動
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]  or keys[pygame.K_a]: self.paddle.move(-self.paddle.speed)
    if keys[pygame.K_RIGHT] or keys[pygame.K_d]: self.paddle.move(self.paddle.speed)

    self.ball.update(self.paddle.x)

    # ② 球出屏 → 扣命
    if self.ball.off_screen():
        self.lives -= 1
        if self.lives <= 0:
            self.game_over = True
            if self.score > self.best: self.best = self.score
        else:
            self.ball = Ball(self.paddle.x, self.paddle.y)
        return

    # ③ 球 vs 擋板
    pr = self.paddle.rect()
    b  = self.ball
    if (b.vy > 0 and
            pr.left - b.radius <= b.x <= pr.right + b.radius and
            pr.top  - b.radius <= b.y <= pr.bottom):
        b.vy = -abs(b.vy)
        offset = (b.x - self.paddle.x) / (PAD_W / 2)   # -1.0 ~ +1.0
        b.vx   = offset * abs(b.vy) * 1.4               # 偏移越大,水平速度越強(qiáng)
        spd    = math.hypot(b.vx, b.vy)
        cap    = self.ball_speed * 1.5
        if spd > cap:
            b.vx *= cap / spd; b.vy *= cap / spd        # 限速,防止球速失控
        b.y = float(pr.top - b.radius - 1)              # 防止球嵌入擋板

    # ④ 球 vs 磚塊
    for brick in self.bricks:
        if not brick.alive: continue
        br = brick.rect()
        if not (b.x + b.radius > br.left  and b.x - b.radius < br.right and
                b.y + b.radius > br.top   and b.y - b.radius < br.bottom):
            continue
        # 判斷從哪個方向擊中(水平穿透量 vs 垂直穿透量)
        ol   = abs(b.x + b.radius - br.left)
        or_  = abs(br.right  - (b.x - b.radius))
        ot   = abs(b.y + b.radius - br.top)
        ob   = abs(br.bottom - (b.y - b.radius))
        minH = min(ol, or_)
        minV = min(ot, ob)
        if minH < minV: b.vx = -b.vx    # 從側(cè)面擊中,反轉(zhuǎn)水平速度
        else:           b.vy = -b.vy    # 從上/下?lián)糁?,反轉(zhuǎn)垂直速度
        brick.hp -= 1
        self.score += brick.max_hp * 10 * self.level
        if self.score > self.best: self.best = self.score
        for _ in range(12):              # 生成粒子
            self.particles.append(Particle(br.centerx, br.centery,
                                           BRICK_COLORS[brick.row % len(BRICK_COLORS)]))
        if brick.hp <= 0: brick.alive = False
        break                            # 每幀只處理一塊磚,防止多重碰撞

    # ⑤ 粒子更新
    for p in self.particles[:]:
        p.update()
        if p.life <= 0: self.particles.remove(p)

    # ⑥ 過關(guān)檢測
    if all(not br.alive for br in self.bricks):
        self.level     += 1
        self.ball_speed = min(4.5 + self.level * 0.4, 9.0)
        self._init_level()

碰撞檢測的核心思路:通過比較球在四個方向上的穿透深度(overlap),取最小值判斷實際接觸面:水平穿透淺 → 從側(cè)面撞;垂直穿透淺 → 從上/下撞。這種方法比復(fù)雜的幾何求交更簡潔,適合 AABB 碰撞場景。

繪制方法 draw

def draw(self):
    screen.fill(DARK_BG)

    # 星空背景(固定點陣,無需每幀隨機(jī))
    for i in range(80):
        sx = (i * 137) % WIDTH
        sy = (i * 251) % HEIGHT
        b  = 30 + (i % 4) * 15
        pygame.draw.circle(screen, (b, b, b + 20), (sx, sy), 1)

    # HUD 信息欄
    pygame.draw.rect(screen, (8, 14, 26), (0, 0, WIDTH, HUD_H))
    pygame.draw.line(screen, BLUE1, (0, HUD_H), (WIDTH, HUD_H), 1)
    screen.blit(font_sm.render(f"分?jǐn)?shù) {self.score}", True, WHITE), (12, 10))
    screen.blit(font_sm.render(f"最高 {self.best}",  True, GRAY),  (12, 32))
    screen.blit(font_sm.render(f"LV {self.level}",  True, GREEN), (WIDTH - 100, 10))
    for li in range(self.lives):                           # 生命圓點
        pygame.draw.circle(screen, RED, (WIDTH - 22 - li * 20, 34), 6)

    # 游戲?qū)ο?
    for br in self.bricks:   br.draw(screen)
    for p  in self.particles: p.draw(screen)
    self.paddle.draw(screen)
    self.ball.draw(screen)

    # 提示 / 暫停 / 游戲結(jié)束遮罩
    if not self.ball.launched and not self.game_over and not self.win:
        hint = font_sm.render("按 SPACE 發(fā)球", True, GRAY)
        screen.blit(hint, (WIDTH // 2 - hint.get_width() // 2, HEIGHT - 70))

    if self.paused:
        overlay = pygame.Surface((WIDTH, HEIGHT - HUD_H), pygame.SRCALPHA)
        overlay.fill((3, 8, 16, 160))
        screen.blit(overlay, (0, HUD_H))
        t = font_big.render("PAUSED", True, GOLD)
        screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 30))

    if self.game_over:
        overlay = pygame.Surface((WIDTH, HEIGHT - HUD_H), pygame.SRCALPHA)
        overlay.fill((3, 8, 16, 190))
        screen.blit(overlay, (0, HUD_H))
        t1 = font_big.render("GAME OVER", True, RED)
        t2 = font_sm.render(f"得分 {self.score}  最高 {self.best}", True, WHITE)
        t3 = font_sm.render("按 R 重新開始", True, GRAY)
        screen.blit(t1, (WIDTH // 2 - t1.get_width() // 2, HEIGHT // 2 - 60))
        screen.blit(t2, (WIDTH // 2 - t2.get_width() // 2, HEIGHT // 2 + 10))
        screen.blit(t3, (WIDTH // 2 - t3.get_width() // 2, HEIGHT // 2 + 44))

    pygame.display.flip()

繪制順序為:背景 → 星空 → HUD → 磚塊 → 粒子 → 擋板 → 球 → 疊加遮罩,嚴(yán)格保證層次正確。

主循環(huán) run

def run(self):
    while True:
        self.handle_events()
        self.update()
        self.draw()
        clock.tick(60)

固定 60 FPS,保證物理運(yùn)動和動畫在不同性能機(jī)器上表現(xiàn)一致。

全部代碼

import pygame
import sys
import math
import random

pygame.init()

WIDTH, HEIGHT = 480, 580
HUD_H = 56

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Breakout: Brick Blaster")
clock = pygame.time.Clock()

DARK_BG  = (3, 8, 16)
BLUE1    = (56, 138, 221)
BLUE2    = (133, 183, 235)
WHITE    = (220, 225, 235)
GRAY     = (100, 115, 130)
RED      = (226, 75, 74)
GOLD     = (250, 199, 117)
GREEN    = (93, 202, 165)

BRICK_COLORS = [
    (83,  74, 183),   # purple   row 0
    (24,  95, 165),   # blue     row 1
    (15, 110,  86),   # teal     row 2
    (59, 109,  17),   # green    row 3
    (186,117,  23),   # amber    row 4
    (152,  60,  29),  # coral    row 5
]

CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"

font_sm  = pygame.font.Font(CHINESE_FONT_PATH, 20)
font_md  = pygame.font.Font(CHINESE_FONT_PATH, 28)
font_big = pygame.font.Font(CHINESE_FONT_PATH, 52)

PAD_W, PAD_H = 80, 12
BALL_R = 8
COLS, ROWS_BRICKS = 8, 6
B_GAP  = 6
BW = (WIDTH - 40 - (COLS - 1) * B_GAP) // COLS
BH = 18


class Paddle:
    def __init__(self):
        self.x = WIDTH // 2
        self.y = HEIGHT - 36
        self.speed = 7

    def move(self, dx):
        self.x = max(PAD_W // 2, min(WIDTH - PAD_W // 2, self.x + dx))

    def rect(self):
        return pygame.Rect(self.x - PAD_W // 2, self.y, PAD_W, PAD_H)

    def draw(self, surf):
        r = self.rect()
        pygame.draw.rect(surf, BLUE1, r, border_radius=6)
        pygame.draw.rect(surf, BLUE2, r.inflate(-20, -4).move(0, 0), border_radius=4)


class Ball:
    def __init__(self, px, py):
        self.x = float(px)
        self.y = float(py - BALL_R - 2)
        self.vx = 0.0
        self.vy = 0.0
        self.launched = False
        self.radius = BALL_R

    def launch(self, speed):
        ang = -math.pi / 2 + random.uniform(-0.5, 0.5)
        self.vx = speed * math.cos(ang)
        self.vy = speed * math.sin(ang)
        self.launched = True

    def update(self, pad_x):
        if not self.launched:
            self.x = float(pad_x)
            return
        self.x += self.vx
        self.y += self.vy
        if self.x - self.radius < 0:
            self.x = float(self.radius); self.vx = abs(self.vx)
        if self.x + self.radius > WIDTH:
            self.x = float(WIDTH - self.radius); self.vx = -abs(self.vx)
        if self.y - self.radius < HUD_H:
            self.y = float(HUD_H + self.radius); self.vy = abs(self.vy)

    def off_screen(self):
        return self.y - self.radius > HEIGHT

    def draw(self, surf):
        ix, iy = int(self.x), int(self.y)
        pygame.draw.circle(surf, BLUE1, (ix, iy), self.radius + 2)
        pygame.draw.circle(surf, BLUE2, (ix, iy), self.radius)
        pygame.draw.circle(surf, WHITE, (ix - 2, iy - 2), 3)


class Brick:
    def __init__(self, col, row, hp):
        self.col = col
        self.row = row
        self.hp  = hp
        self.max_hp = hp
        self.alive = True
        self.x = 20 + col * (BW + B_GAP)
        self.y = HUD_H + 20 + row * (BH + B_GAP)

    def rect(self):
        return pygame.Rect(self.x, self.y, BW, BH)

    def draw(self, surf):
        if not self.alive:
            return
        frac  = self.hp / self.max_hp
        base  = BRICK_COLORS[self.row % len(BRICK_COLORS)]
        color = tuple(int(c * (0.4 + 0.6 * frac)) for c in base)
        r = self.rect()
        pygame.draw.rect(surf, color, r, border_radius=4)
        border_a = int(80 * frac)
        pygame.draw.rect(surf, (*WHITE[:3], border_a), r, width=1, border_radius=4)
        if self.max_hp > 1:
            label = font_sm.render(str(self.hp), True,
                                   tuple(min(255, c + 120) for c in color))
            surf.blit(label, (r.centerx - label.get_width() // 2,
                              r.centery - label.get_height() // 2))


class Particle:
    def __init__(self, x, y, color):
        self.x  = float(x)
        self.y  = float(y)
        self.vx = random.uniform(-3, 3)
        self.vy = random.uniform(-4, 0.5)
        self.life = 28
        self.color = color

    def update(self):
        self.x  += self.vx
        self.y  += self.vy
        self.vy += 0.2
        self.life -= 1

    def draw(self, surf):
        if self.life <= 0:
            return
        a = min(255, self.life * 9)
        s = pygame.Surface((5, 5), pygame.SRCALPHA)
        s.fill((*self.color[:3], a))
        surf.blit(s, (int(self.x), int(self.y)))


class Game:
    def __init__(self):
        self.best = 0
        self.hp_values = [13, 11, 7, 5, 3, 2]
        self.reset()


    def reset(self):
        self.score      = 0
        self.lives      = 3
        self.level      = 1
        self.ball_speed = 4.5
        self.paused     = False
        self.game_over  = False
        self.win        = False
        self.particles  = []
        self._init_level()

    def _init_level(self):
        self.paddle = Paddle()
        self.ball   = Ball(self.paddle.x, self.paddle.y)
        self.bricks = [
            Brick(c, r, self.hp_values[r])
            # Brick(c, r, random.randint(1, 10))
            for r in range(ROWS_BRICKS)
            for c in range(COLS)
        ]

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit(); sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    self.reset()
                if event.key == pygame.K_p:
                    self.paused = not self.paused
                if event.key == pygame.K_SPACE and not self.ball.launched:
                    self.ball.launch(self.ball_speed)

    def update(self):
        if self.paused or self.game_over or self.win:
            return
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]  or keys[pygame.K_a]:
            self.paddle.move(-self.paddle.speed)
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.paddle.move(self.paddle.speed)

        self.ball.update(self.paddle.x)

        # Ball lost
        if self.ball.off_screen():
            self.lives -= 1
            if self.lives <= 0:
                self.game_over = True
                if self.score > self.best:
                    self.best = self.score
            else:
                self.ball = Ball(self.paddle.x, self.paddle.y)
            return

        # Ball vs paddle
        pr = self.paddle.rect()
        b  = self.ball
        if (b.vy > 0 and
                pr.left - b.radius <= b.x <= pr.right + b.radius and
                pr.top  - b.radius <= b.y <= pr.bottom):
            b.vy = -abs(b.vy)
            offset = (b.x - self.paddle.x) / (PAD_W / 2)
            b.vx   = offset * abs(b.vy) * 1.4
            spd    = math.hypot(b.vx, b.vy)
            cap    = self.ball_speed * 1.5
            if spd > cap:
                b.vx *= cap / spd; b.vy *= cap / spd
            b.y = float(pr.top - b.radius - 1)

        # Ball vs bricks
        for brick in self.bricks:
            if not brick.alive:
                continue
            br = brick.rect()
            if not (b.x + b.radius > br.left  and b.x - b.radius < br.right and
                    b.y + b.radius > br.top    and b.y - b.radius < br.bottom):
                continue
            ol = abs(b.x + b.radius - br.left)
            or_ = abs(br.right  - (b.x - b.radius))
            ot = abs(b.y + b.radius - br.top)
            ob = abs(br.bottom - (b.y - b.radius))
            minH = min(ol, or_)
            minV = min(ot, ob)
            if minH < minV:
                b.vx = -b.vx
            else:
                b.vy = -b.vy
            brick.hp -= 1
            pts = brick.max_hp * 10 * self.level
            self.score += pts
            if self.score > self.best:
                self.best = self.score
            color = BRICK_COLORS[brick.row % len(BRICK_COLORS)]
            for _ in range(12):
                self.particles.append(Particle(br.centerx, br.centery, color))
            if brick.hp <= 0:
                brick.alive = False
            break

        # Update particles
        for p in self.particles[:]:
            p.update()
            if p.life <= 0:
                self.particles.remove(p)

        # Level clear?
        if all(not br.alive for br in self.bricks):
            self.level      += 1
            self.ball_speed  = min(4.5 + self.level * 0.4, 9.0)
            self._init_level()

    def draw(self):
        screen.fill(DARK_BG)

        # subtle star dots
        for i in range(80):
            sx = (i * 137) % WIDTH
            sy = (i * 251) % HEIGHT
            b  = 30 + (i % 4) * 15
            pygame.draw.circle(screen, (b, b, b + 20), (sx, sy), 1)

        # HUD
        pygame.draw.rect(screen, (8, 14, 26), (0, 0, WIDTH, HUD_H))
        pygame.draw.line(screen, BLUE1, (0, HUD_H), (WIDTH, HUD_H), 1)
        screen.blit(font_sm.render(f"分?jǐn)?shù) {self.score}", True, WHITE),  (12, 10))
        screen.blit(font_sm.render(f"最高 {self.best}",  True, GRAY),   (12, 32))
        screen.blit(font_sm.render(f"LV {self.level}",  True, GREEN),  (WIDTH - 100, 10))
        # lives
        for li in range(self.lives):
            pygame.draw.circle(screen, RED, (WIDTH - 22 - li * 20, 34), 6)

        for br in self.bricks:
            br.draw(screen)
        for p in self.particles:
            p.draw(screen)
        self.paddle.draw(screen)
        self.ball.draw(screen)

        if not self.ball.launched and not self.game_over and not self.win:
            hint = font_sm.render("按 SPACE 發(fā)球", True, GRAY)
            screen.blit(hint, (WIDTH // 2 - hint.get_width() // 2, HEIGHT - 70))

        if self.paused:
            overlay = pygame.Surface((WIDTH, HEIGHT - HUD_H), pygame.SRCALPHA)
            overlay.fill((3, 8, 16, 160))
            screen.blit(overlay, (0, HUD_H))
            t = font_big.render("PAUSED", True, GOLD)
            screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 30))

        if self.game_over:
            overlay = pygame.Surface((WIDTH, HEIGHT - HUD_H), pygame.SRCALPHA)
            overlay.fill((3, 8, 16, 190))
            screen.blit(overlay, (0, HUD_H))
            t1 = font_big.render("GAME OVER", True, RED)
            t2 = font_sm.render(f"得分 {self.score}  最高 {self.best}", True, WHITE)
            t3 = font_sm.render("按 R 重新開始", True, GRAY)
            screen.blit(t1, (WIDTH // 2 - t1.get_width() // 2, HEIGHT // 2 - 60))
            screen.blit(t2, (WIDTH // 2 - t2.get_width() // 2, HEIGHT // 2 + 10))
            screen.blit(t3, (WIDTH // 2 - t3.get_width() // 2, HEIGHT // 2 + 44))

        pygame.display.flip()

    def run(self):
        while True:
            self.handle_events()
            self.update()
            self.draw()
            clock.tick(60)


if __name__ == "__main__":
    Game().run()

以上就是Python基于Pygame開發(fā)的經(jīng)典打磚塊游戲的詳細(xì)內(nèi)容,更多關(guān)于Python Pygame打磚塊游戲的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用python批量修改XML文件中圖像的depth值

    使用python批量修改XML文件中圖像的depth值

    這篇文章主要介紹了使用python批量修改XML文件中圖像的depth值,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • python調(diào)試模塊ipdb詳解

    python調(diào)試模塊ipdb詳解

    ipdb是用來python中用以交互式debug的模塊,可以直接利用pip安裝,這篇文章主要介紹了python調(diào)試模塊ipdb詳解,需要的朋友可以參考下
    2023-03-03
  • Python?OpenCV形態(tài)學(xué)運(yùn)算示例詳解

    Python?OpenCV形態(tài)學(xué)運(yùn)算示例詳解

    這篇文章主要為大家介紹了OpenCV中的幾個形態(tài)學(xué)運(yùn)算,例如:腐蝕&膨脹、開&閉運(yùn)算、梯度運(yùn)算、頂帽運(yùn)算黑帽運(yùn)算,感興趣的可以了解一下
    2022-04-04
  • python使用Scrapy庫進(jìn)行數(shù)據(jù)提取和處理的方法詳解

    python使用Scrapy庫進(jìn)行數(shù)據(jù)提取和處理的方法詳解

    在我們的初級教程中,我們介紹了如何使用Scrapy創(chuàng)建和運(yùn)行一個簡單的爬蟲,在這篇文章中,我們將深入了解Scrapy的強(qiáng)大功能,學(xué)習(xí)如何使用Scrapy提取和處理數(shù)據(jù)
    2023-09-09
  • python中的代碼運(yùn)行時間獲取方式

    python中的代碼運(yùn)行時間獲取方式

    這篇文章主要介紹了python中的代碼運(yùn)行時間獲取方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • python實現(xiàn)畫桃心表白

    python實現(xiàn)畫桃心表白

    這篇文章主要介紹了python實現(xiàn)畫桃心表白的代碼,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Python中的一些陷阱與技巧小結(jié)

    Python中的一些陷阱與技巧小結(jié)

    這篇文章主要介紹了Python中的一些陷阱與技巧小結(jié),包括生成器等高級用法,和Python2.x與3版本換代帶來的兼容性問題等,需要的朋友可以參考下
    2015-07-07
  • 使用PyCharm在Github上保存代碼并在服務(wù)器上運(yùn)行方式

    使用PyCharm在Github上保存代碼并在服務(wù)器上運(yùn)行方式

    這篇文章主要介紹了使用PyCharm在Github上保存代碼并在服務(wù)器上運(yùn)行方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Python urllib2運(yùn)行過程原理解析

    Python urllib2運(yùn)行過程原理解析

    這篇文章主要介紹了Python urllib2運(yùn)行過程原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 基于Python實現(xiàn)自動化文檔整理工具

    基于Python實現(xiàn)自動化文檔整理工具

    一個人可能會在計算機(jī)上存儲大量的照片、視頻和文檔文件,這些文件可能散落在不同的文件夾中,難以管理和查找。所以本文就來用Python制作一個自動化文檔整理工具吧
    2023-04-04

最新評論

栾城县| 高陵县| 苗栗市| 抚远县| 礼泉县| 临西县| 阳曲县| 禄劝| 涞水县| 衡阳市| 孟村| 建宁县| 云龙县| 恩施市| 临桂县| 星子县| 盐边县| 南皮县| 定兴县| 北海市| 长顺县| 东莞市| 太仓市| 宁蒗| 金湖县| 达尔| 延安市| 永寿县| 永寿县| 赤水市| 永德县| 石泉县| 双流县| 青铜峡市| 通渭县| 镇平县| 琼海市| 高安市| 扶绥县| 惠州市| 英超|