用 pygame 做一个完整的小飞机大战:方向键控制飞机、空格发射子弹、敌机不断下落、碰撞判定、三条命和计分。全部用矩形和圆形绘制,不依赖任何图片素材。

1. 玩家飞机与键盘控制

飞机就是一个矩形,用方向键移动并限制在窗口内。用 pygame.key.get_pressed() 实现按住连续移动:

import pygame

W, H = 480, 640
plane = pygame.Rect(W // 2 - 25, H - 80, 50, 40)

def move_plane(keys):
    if keys[pygame.K_LEFT] and plane.left > 0:
        plane.move_ip(-6, 0)
    if keys[pygame.K_RIGHT] and plane.right < W:
        plane.move_ip(6, 0)
    if keys[pygame.K_UP] and plane.top > 0:
        plane.move_ip(0, -6)
    if keys[pygame.K_DOWN] and plane.bottom < H:
        plane.move_ip(0, 6)

2. 子弹与射击

子弹列表存矩形,空格键生成新子弹;每帧所有子弹向上移动,超出屏幕就移除:

bullets = []

def shoot(keys, now):
    global next_shot
    if keys[pygame.K_SPACE] and now >= next_shot:
        bullets.append(pygame.Rect(plane.centerx - 2, plane.top, 4, 12))
        next_shot = now + 200        # 200ms 一发

def update_bullets():
    for b in bullets[:]:
        b.move_ip(0, -10)
        if b.bottom < 0:
            bullets.remove(b)

3. 敌机与碰撞

敌机定时从顶部生成,随机横向位置,向下移动。子弹与敌机矩形相交(colliderect)就双双消失;敌机撞到飞机或飞出底部就扣命:

enemies = []
lives = 3
score = 0

def spawn_enemy(now):
    if now >= next_spawn:
        enemies.append(pygame.Rect(random.randrange(W - 40), -40, 40, 30))
        next_spawn = now + 900

def update_enemies():
    global lives, score
    for e in enemies[:]:
        e.move_ip(0, 4)
        if e.top > H:
            enemies.remove(e); lives -= 1
            continue
        for b in bullets[:]:
            if e.colliderect(b):
                enemies.remove(e); bullets.remove(b)
                score += 10
                break
        if plane.colliderect(e):
            enemies.remove(e); lives -= 1

4. 完整代码

import random, pygame

W, H = 480, 640
pygame.init()
screen = pygame.display.set_mode((W, H))
clock = pygame.time.Clock()
font = pygame.font.SysFont('simhei', 26)

plane = pygame.Rect(W // 2 - 25, H - 80, 50, 40)
bullets = []
enemies = []
lives = 3
score = 0
next_shot = 0
next_spawn = 0
game_over = False

while not game_over:
    now = pygame.time.get_ticks()
    keys = pygame.key.get_pressed()
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            pygame.quit(); raise SystemExit
    move_plane(keys)
    shoot(keys, now)
    update_bullets()
    spawn_enemy(now)
    update_enemies()
    if lives <= 0:
        game_over = True
        break

    screen.fill((10, 10, 25))
    pygame.draw.rect(screen, (90, 200, 255), plane, border_radius=8)
    for b in bullets:
        pygame.draw.rect(screen, (255, 220, 80), b)
    for e in enemies:
        pygame.draw.rect(screen, (230, 80, 80), e, border_radius=6)
    screen.blit(font.render('得分 %d  生命 %d' % (score, lives), True, (255, 255, 255)), (10, 10))
    pygame.display.flip()
    clock.tick(60)
print('游戏结束,最终得分:', score)

5. 运行与常见问题

保存为 plane.py,python3 plane.py 运行。常见问题:①子弹穿透敌机——检测到碰撞后要立刻 remove 并 break,防止一个子弹打多架;②敌机同时消失——遍历列表时用副本 [:];③卡顿——clock.tick(60) 限制帧率。

💡 改进方向:①爆炸粒子效果;②敌机分类型(血量/速度不同);③道具(双发、护盾);④关卡与 BOSS;⑤音效与最高分存档。