20254123 2025-2026-2 《Python程序设计》实验4报告

课程:《Python程序设计》
班级: 2541
姓名: 刘浩丞
学号:20254123
实验教师:王志强
实验日期:2026年6月8日
必修/选修: 专选课

一、实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。

要求:
(1)程序能运行,功能丰富(至少5个功能)。(需求提交源代码,并建议录制程序运行的视频)15分
(2)综合实践报告,要体现实验分析、设计、实现过程、结果等信息,格式规范,逻辑清晰,结构合理。20分。
(3)在实践报告中,需要对全课进行总结,并写课程感想体会、意见和建议等。10分

二、实验分析
(一)问题分析
本次实验要求能力较为综合,是对课堂所学的一次全方位总结与实践。为了给老师呈现出较好的效果(不被老师拷打),选择了编写小游戏类的选题,且更贴合Python图形化编程的学习内容,并且本人较为喜欢坦克大战的游戏,故设计之。
(二)选题分析
坦克大战游戏实验需要综合运用Pygame的图形绘制、碰撞检测与事件驱动机制,将通过面向对象设计将坦克、子弹、墙壁等实体封装为独立类,并且要设计支持单人、双人合作及对战三种模式。为保证流畅对战,还需要调试了移动冲突与碰撞逻辑。该实践可以加深对游戏循环、状态机及实时交互的理解,验证了Python在小型游戏开发中的可行性与高效性。

三、实验设计
(一)实验模块设计
| 模块名称 | 文件/类名 | 主要职责 |
| 初始化与常量模块 | 全局常量 | 定义屏幕尺寸、颜色、速度、冷却帧数等游戏参数 |
| 墙体生成模块 | generate_random_walls() | 根据关卡等级随机生成砖墙,确保安全区域无墙 |
| 爆炸特效模块 | Explosion | 管理爆炸动画的半径、透明度及生命周期 |
| 坦克基类 | Tank | 封装坦克的坐标、方向、移动、射击、绘制等通用行为 |
| 玩家坦克类 | PlayerTank | 继承自Tank,添加生命值、无敌状态、键盘控制、分数统计 |
| 敌方坦克类 | EnemyTank | 继承自Tank,实现随机移动、随机转向、自动射击的简单AI |
| 子弹类 | Bullet | 管理子弹位置、方向、存活状态及所有者(用于对战模式) |
| 墙体类 | Wall | 管理砖墙的存在状态及绘制 |
| 游戏主控类 | Game | 整合所有模块,管理状态机(菜单/游戏中)、事件循环、碰撞检测、胜负判定 |
| 菜单界面模块 | draw_menu(), handle_menu_events() | 绘制按钮和选项,处理鼠标选择关卡、敌人数、游戏模式 |
| 碰撞处理模块 | handle_collisions() | 检测子弹与墙壁、子弹与坦克的碰撞,触发墙破坏、坦克受伤/死亡、爆炸特效及加分 |
| 坦克分离算法 | separate_tanks() | 强制推开重叠的坦克,防止卡死 |
| 主循环模块 | run() | 驱动游戏主循环,调用更新与绘制函数,维持帧率 |
(二)核心功能

  1. 游戏模式
    单人模式:一名玩家(绿色坦克,方向键+空格)与AI敌方坦克对战。
    双人合作模式:两名玩家(绿:WASD+空格;青:方向键+右Shift)共同对抗AI。
    双人对战模式:两名玩家互相射击,无敌方坦克。
  2. 关卡难度系统
    等级1:敌方速度2,射击延迟60帧,随机砖墙约15块。
    等级2:敌方速度3,射击延迟45帧,随机砖墙约30块。
    等级3:敌方速度3,射击延迟30帧,随机砖墙约50块。
  3. 随机障碍生成
    每关重新生成砖墙位置,障碍数量随难度递增。
    避开玩家和敌方出生点及其周围3×3区域,防止出生即被卡。
  4. 坦克操作与射击
    移动:四方向独立控制,支持斜向移动(分轴处理)。
    射击:独立于移动按键(空格/右Shift),子弹有冷却时间(15帧)。
    无敌帧:玩家被击中后短暂无敌(90帧),期间闪烁显示。
  5. 碰撞与物理系统
    坦克与边界、墙体、其他坦克的碰撞检测(移动时实时检测)。
    子弹与墙体碰撞:墙体被摧毁(alive=False)。
    子弹与坦克碰撞:
    玩家子弹击中敌方 → 敌方消失,玩家加分(100分/个),播放爆炸特效。
    敌方子弹击中玩家 → 玩家生命减1,若生命归零则死亡;合作模式下任一玩家死亡不影响另一玩家。
    对战模式下,玩家子弹击中对方玩家 → 对方生命减1,生命归零则游戏结束并显示胜者。
  6. 生命与分数系统
    每个玩家初始3条命,界面实时显示生命值。
    合作/单人模式显示总分,双人对战不显示分数,只显示胜利方。
  7. 游戏流程控制
    菜单界面:鼠标选择关卡(1-3)、敌方数量(3/5/7)、模式(单人/合作/对战),点击START开始。
    游戏中:ESC键返回菜单,R键(游戏结束后)重新开始当前关卡的同一配置。
    胜利/失败判定:
    合作/单人:全灭敌方坦克 → 胜利;所有玩家生命归零 → 失败。
    对战:某一玩家生命归零 → 另一方胜利。
  8. 视觉与特效
    坦克绘制不同颜色及炮管方向指示。
    无敌闪烁效果。
    爆炸动画(橙色圆逐渐放大并淡出)。
    砖墙纹理(棕色填充+灰色边框)。

(三)实现过程
源代码已分拆至下面的实验过程中
1.搭建Pygame环境
调用 pygame.init() 激活所有Pygame模块;定义屏幕宽高(800×600)、网格大小(40×40)、帧率(60 FPS);预设颜色RGB值和游戏参数(移动速度、子弹速度、冷却帧数、无敌帧数等);计算地图网格数量(20列 × 15行)。

点击查看代码
import pygame
import sys
import random
import math

# ---------- 初始化 ----------
pygame.init()

# ---------- 常量 ----------
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 40
FPS = 60

# 颜色
COLOR_BLACK = (0, 0, 0)
COLOR_WHITE = (255, 255, 255)
COLOR_GREEN = (0, 255, 0)
COLOR_RED = (255, 0, 0)
COLOR_YELLOW = (255, 255, 0)
COLOR_GRAY = (128, 128, 128)
COLOR_BROWN = (160, 82, 45)
COLOR_ORANGE = (255, 165, 0)
COLOR_BLUE = (0, 0, 255)
COLOR_CYAN = (0, 255, 255)

# 游戏参数
BASE_ENEMY_SPEED = 2
BASE_ENEMY_SHOOT_DELAY = 60
PLAYER_SPEED = 4
BULLET_SPEED = 8
PLAYER_SHOOT_DELAY = 15
INVINCIBLE_DURATION = 90

MAP_WIDTH = SCREEN_WIDTH // GRID_SIZE   # 20
MAP_HEIGHT = SCREEN_HEIGHT // GRID_SIZE # 15
2.实现基础图形 Tank.draw() 用 pygame.draw.rect() 绘制坦克车身,再根据当前方向用 pygame.draw.line() 绘制炮管(黄色线段);Wall.draw() 绘制棕色填充矩形加灰色边框,表示砖墙;颜色使用预定义的RGB元组。
点击查看代码
# ---------- 坦克基类绘制 ----------
class Tank:
    def __init__(self, x, y, direction, color, speed, is_player=False):
        self.x = x
        self.y = y
        self.direction = direction
        self.color = color
        self.speed = speed
        self.is_player = is_player
        self.width = GRID_SIZE
        self.height = GRID_SIZE
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        self.shoot_cooldown = 0

    def draw(self, screen):
        pygame.draw.rect(screen, self.color, self.rect)
        # 绘制炮管
        if self.direction == 'up':
            start = (self.x + self.width//2, self.y)
            end = (self.x + self.width//2, self.y - 10)
        elif self.direction == 'down':
            start = (self.x + self.width//2, self.y + self.height)
            end = (self.x + self.width//2, self.y + self.height + 10)
        elif self.direction == 'left':
            start = (self.x, self.y + self.height//2)
            end = (self.x - 10, self.y + self.height//2)
        else:  # right
            start = (self.x + self.width, self.y + self.height//2)
            end = (self.x + self.width + 10, self.y + self.height//2)
        pygame.draw.line(screen, COLOR_YELLOW, start, end, 4)

# ---------- 墙体绘制 ----------
class Wall:
    def __init__(self, grid_x, grid_y):
        self.grid_x = grid_x
        self.grid_y = grid_y
        self.x = grid_x * GRID_SIZE
        self.y = grid_y * GRID_SIZE
        self.rect = pygame.Rect(self.x, self.y, GRID_SIZE, GRID_SIZE)
        self.alive = True

    def draw(self, screen):
        if self.alive:
            pygame.draw.rect(screen, COLOR_BROWN, self.rect)
            pygame.draw.rect(screen, COLOR_GRAY, self.rect, 2)
3.坦克移动与碰撞检测 分轴移动:move() 先尝试水平移动,再尝试垂直移动。每次移动前创建临时矩形进行碰撞预测;碰撞检测:检查新位置是否超出边界、是否与任何墙体矩形相交、是否与其他坦克矩形相交。任一条件触发则移动失败;坦克分离算法:separate_tanks() 在每帧移动后调用,检测所有存活坦克之间的重叠。若重叠,沿中心连线方向推开双方各3像素,并限制在边界内,迭代最多5次。
点击查看代码
class Tank:
    # ... 接上面的 Tank 类

    def update_rect(self):
        self.rect.topleft = (self.x, self.y)

    def move(self, dx, dy, walls, other_tanks):
        # 水平移动
        if dx != 0:
            new_x = self.x + dx
            new_rect = pygame.Rect(new_x, self.y, self.width, self.height)
            can_move = True
            # 边界
            if new_x < 0 or new_x + self.width > SCREEN_WIDTH:
                can_move = False
            # 墙体
            for wall in walls:
                if new_rect.colliderect(wall.rect):
                    can_move = False
                    break
            # 其他坦克
            for tank in other_tanks:
                if new_rect.colliderect(tank.rect):
                    can_move = False
                    break
            if can_move:
                self.x = new_x
        # 垂直移动
        if dy != 0:
            new_y = self.y + dy
            new_rect = pygame.Rect(self.x, new_y, self.width, self.height)
            can_move = True
            if new_y < 0 or new_y + self.height > SCREEN_HEIGHT:
                can_move = False
            for wall in walls:
                if new_rect.colliderect(wall.rect):
                    can_move = False
                    break
            for tank in other_tanks:
                if new_rect.colliderect(tank.rect):
                    can_move = False
                    break
            if can_move:
                self.y = new_y
        self.update_rect()
        return True

# ---------- 坦克分离算法(在Game类中) ----------
class Game:
    def separate_tanks(self, max_iter=5):
        all_tanks = [p for p in self.players if p.alive] + self.enemies
        for _ in range(max_iter):
            any_overlap = False
            for i in range(len(all_tanks)):
                for j in range(i+1, len(all_tanks)):
                    t1, t2 = all_tanks[i], all_tanks[j]
                    if t1.rect.colliderect(t2.rect):
                        any_overlap = True
                        dx = t1.rect.centerx - t2.rect.centerx
                        dy = t1.rect.centery - t2.rect.centery
                        if dx == 0 and dy == 0:
                            dx, dy = 1, 1
                        length = math.hypot(dx, dy)
                        dx /= length
                        dy /= length
                        push = 3
                        t1.x += dx * push
                        t1.y += dy * push
                        t2.x -= dx * push
                        t2.y -= dy * push
                        t1.x = max(0, min(SCREEN_WIDTH - t1.width, t1.x))
                        t1.y = max(0, min(SCREEN_HEIGHT - t1.height, t1.y))
                        t2.x = max(0, min(SCREEN_WIDTH - t2.width, t2.x))
                        t2.y = max(0, min(SCREEN_HEIGHT - t2.height, t2.y))
                        t1.update_rect()
                        t2.update_rect()
            if not any_overlap:
                break
4.射击机制 Bullet 类存储位置、方向、速度、存活状态和发射者(owner,用于对战模式避免击中自己);update() 每帧按方向增加坐标,超出屏幕边界则 alive = False;Tank.shoot() 计算炮口中心坐标,创建子弹加入列表,并设置冷却帧数(玩家15帧,敌方根据难度30~60帧);can_shoot() 检查 shoot_cooldown == 0 决定是否允许发射。
点击查看代码
class Bullet:
    def __init__(self, x, y, direction, is_player, owner=None):
        self.x = x - 3
        self.y = y - 3
        self.direction = direction
        self.is_player = is_player
        self.owner = owner
        self.width = 6
        self.height = 6
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        self.speed = BULLET_SPEED
        self.alive = True

    def update(self):
        if self.direction == 'up':
            self.y -= self.speed
        elif self.direction == 'down':
            self.y += self.speed
        elif self.direction == 'left':
            self.x -= self.speed
        else:
            self.x += self.speed
        self.rect.topleft = (self.x, self.y)
        if (self.x + self.width < 0 or self.x > SCREEN_WIDTH or
            self.y + self.height < 0 or self.y > SCREEN_HEIGHT):
            self.alive = False

    def draw(self, screen):
        pygame.draw.rect(screen, COLOR_YELLOW, self.rect)

class Tank:
    # ... 接上面 Tank 类

    def can_shoot(self):
        return self.shoot_cooldown == 0

    def shoot(self, bullet_group):
        if not self.can_shoot():
            return None
        center_x = self.x + self.width // 2
        center_y = self.y + self.height // 2
        bullet = Bullet(center_x, center_y, self.direction, self.is_player, owner=self)
        bullet_group.append(bullet)
        return bullet
5.敌方AI 初始方向随机选择;每帧递减 change_dir_counter,减到0时随机改变方向并重置计数器(30~90帧);根据当前方向计算移动分量,调用 move();每次移动后,以3%的概率尝试射击(若冷却结束);射击冷却 shoot_delay 根据关卡难度不同(60/45/30帧)。
点击查看代码
class EnemyTank(Tank):
    def __init__(self, x, y, speed, shoot_delay):
        super().__init__(x, y, random.choice(['up', 'down', 'left', 'right']), COLOR_RED, speed, is_player=False)
        self.shoot_cooldown = random.randint(0, shoot_delay//2)
        self.change_dir_counter = random.randint(30, 90)
        self.shoot_delay = shoot_delay

    def update(self, walls, other_tanks, bullet_group):
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1

        self.change_dir_counter -= 1
        if self.change_dir_counter <= 0:
            self.direction = random.choice(['up', 'down', 'left', 'right'])
            self.change_dir_counter = random.randint(30, 90)

        dx, dy = 0, 0
        if self.direction == 'up':
            dy = -self.speed
        elif self.direction == 'down':
            dy = self.speed
        elif self.direction == 'left':
            dx = -self.speed
        else:
            dx = self.speed

        if dx != 0 or dy != 0:
            self.move(dx, dy, walls, other_tanks)

        if self.can_shoot() and random.randint(1, 100) < 3:
            self.shoot(bullet_group)
            self.shoot_cooldown = self.shoot_delay
6.游戏状态机 Game.state 取值 "MENU" 或 "PLAYING";主循环 run() 中根据状态调用不同的更新/绘制函数;菜单状态下监听鼠标点击事件(handle_menu_events),点击START后调用 reset_game() 并切换状态;游戏状态下监听键盘事件(handle_game_events):ESC返回菜单,R键(仅游戏结束时)重玩当前关卡。
点击查看代码
class Game:
    def __init__(self):
        self.state = "MENU"   # "MENU" 或 "PLAYING"
        # ... 其他初始化

    def run(self):
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                if self.state == "MENU":
                    self.handle_menu_events(event)
                else:
                    self.handle_game_events(event)

            if self.state == "MENU":
                self.draw_menu()
            else:
                self.update_game()
                self.draw_game()

            self.clock.tick(FPS)

    def handle_menu_events(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            # 按钮区域判断(见步骤7)
            if self.buttons["start"].collidepoint(pos):
                self.reset_game()
                self.state = "PLAYING"

    def handle_game_events(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                self.state = "MENU"
            if event.key == pygame.K_r and self.game_over:
                self.reset_game()
7.多模式与难度 菜单中通过按钮选择 selected_mode(SINGLE/COOP/VS)和 selected_level(1/2/3);reset_game() 根据模式创建不同数量的玩家坦克,并分配不同的控制键位(WASD+空格 或 方向键+右Shift);难度影响敌方速度(2或3)和射击延迟(60/45/30帧);双人对战模式下不生成敌方坦克,胜利条件为一方生命归零。
点击查看代码
class Game:
    def __init__(self):
        self.selected_level = 1
        self.selected_enemy_count = 3
        self.selected_mode = "SINGLE"   # "SINGLE", "COOP", "VS"

        # 按钮矩形
        self.buttons = {
            "level_down": pygame.Rect(220, 150, 40, 40),
            "level_up": pygame.Rect(300, 150, 40, 40),
            "enemy_down": pygame.Rect(220, 220, 40, 40),
            "enemy_up": pygame.Rect(300, 220, 40, 40),
            "mode_single": pygame.Rect(140, 290, 100, 40),
            "mode_coop":   pygame.Rect(260, 290, 100, 40),
            "mode_vs":     pygame.Rect(380, 290, 100, 40),
            "start":       pygame.Rect(300, 400, 200, 50)
        }

    def reset_game(self):
        # 根据模式创建玩家
        self.players.clear()
        if self.selected_mode == "SINGLE":
            p1 = PlayerTank((MAP_WIDTH//2 - 1)*GRID_SIZE, (MAP_HEIGHT-2)*GRID_SIZE,
                            COLOR_GREEN, {
                                'left': pygame.K_LEFT, 'right': pygame.K_RIGHT,
                                'up': pygame.K_UP, 'down': pygame.K_DOWN,
                                'shoot': pygame.K_SPACE
                            }, player_id=1)
            self.players.append(p1)
        elif self.selected_mode == "COOP":
            p1 = PlayerTank((MAP_WIDTH//2 - 2)*GRID_SIZE, (MAP_HEIGHT-2)*GRID_SIZE,
                            COLOR_GREEN, {
                                'left': pygame.K_a, 'right': pygame.K_d,
                                'up': pygame.K_w, 'down': pygame.K_s,
                                'shoot': pygame.K_SPACE
                            }, player_id=1)
            p2 = PlayerTank((MAP_WIDTH//2 + 1)*GRID_SIZE, (MAP_HEIGHT-2)*GRID_SIZE,
                            COLOR_CYAN, {
                                'left': pygame.K_LEFT, 'right': pygame.K_RIGHT,
                                'up': pygame.K_UP, 'down': pygame.K_DOWN,
                                'shoot': pygame.K_RSHIFT
                            }, player_id=2)
            self.players.extend([p1, p2])
        else:  # VS
            p1 = PlayerTank((MAP_WIDTH//2 - 2)*GRID_SIZE, (MAP_HEIGHT-2)*GRID_SIZE,
                            COLOR_GREEN, {
                                'left': pygame.K_a, 'right': pygame.K_d,
                                'up': pygame.K_w, 'down': pygame.K_s,
                                'shoot': pygame.K_SPACE
                            }, player_id=1)
            p2 = PlayerTank((MAP_WIDTH//2 + 1)*GRID_SIZE, (MAP_HEIGHT-2)*GRID_SIZE,
                            COLOR_CYAN, {
                                'left': pygame.K_LEFT, 'right': pygame.K_RIGHT,
                                'up': pygame.K_UP, 'down': pygame.K_DOWN,
                                'shoot': pygame.K_RSHIFT
                            }, player_id=2)
            self.players.extend([p1, p2])

        # 根据关卡设置敌方参数(仅在非VS模式)
        if self.selected_mode != "VS":
            if self.selected_level == 1:
                enemy_speed = BASE_ENEMY_SPEED
                enemy_shoot_delay = BASE_ENEMY_SHOOT_DELAY
            elif self.selected_level == 2:
                enemy_speed = 3
                enemy_shoot_delay = int(BASE_ENEMY_SHOOT_DELAY * 0.75)
            else:
                enemy_speed = 3
                enemy_shoot_delay = int(BASE_ENEMY_SHOOT_DELAY * 0.5)
            # 生成敌方坦克...
8.随机障碍生成 generate_random_walls(level, safe_cells): 根据等级确定墙块数量(1级15,2级30,3级50);生成所有网格坐标,排除 safe_cells 集合中的格子(玩家和敌方的出生点及其周围3×3区域);从剩余格子中随机抽取不重复的指定数量,创建 Wall 对象。 在 reset_game() 中收集所有安全格子(包括敌方起始位置,仅限非VS模式),调用该函数生成墙体。
点击查看代码
def generate_random_walls(level, safe_cells):
    wall_counts = {1: 15, 2: 30, 3: 50}
    count = wall_counts.get(level, 15)
    all_cells = [(x, y) for x in range(MAP_WIDTH) for y in range(MAP_HEIGHT)]
    available = [cell for cell in all_cells if cell not in safe_cells]
    chosen = random.sample(available, min(count, len(available)))
    walls = []
    for (gx, gy) in chosen:
        walls.append(Wall(gx, gy))
    return walls

# 在 Game.reset_game() 中调用
def reset_game(self):
    # 确定安全格子(玩家和敌方初始位置及其周围)
    safe_cells = set()
    # 玩家区域
    player_start_positions = [...]
    for (gx, gy) in player_start_positions:
        for dx in range(-1, 2):
            for dy in range(-1, 2):
                nx, ny = gx+dx, gy+dy
                if 0 <= nx < MAP_WIDTH and 0 <= ny < MAP_HEIGHT:
                    safe_cells.add((nx, ny))
    # 敌方区域(非VS模式)
    if self.selected_mode != "VS":
        enemy_start_positions = [(3,2), (8,2), (13,2), (5,5), (10,5), (2,8), (15,8)]
        for i in range(min(self.selected_enemy_count, len(enemy_start_positions))):
            gx, gy = enemy_start_positions[i]
            for dx in range(-1, 2):
                for dy in range(-1, 2):
                    nx, ny = gx+dx, gy+dy
                    if 0 <= nx < MAP_WIDTH and 0 <= ny < MAP_HEIGHT:
                        safe_cells.add((nx, ny))
    # 生成随机墙体
    self.walls = generate_random_walls(self.selected_level, safe_cells)
9.UI显示 使用 pygame.font.Font(None, size) 创建字体对象;在 draw_game() 中,遍历所有玩家,绘制生命值(格式:Player Lives: 3 或 P1 Lives: 2);单人/合作模式显示总分和剩余敌方数量;对战模式不显示分数;游戏结束时,根据胜利/失败显示不同颜色的文字(绿色胜利,红色失败),并提示按R重玩或ESC返回菜单。
点击查看代码
class Game:
    def draw_game(self):
        self.screen.fill(COLOR_BLACK)
        # 绘制墙壁、坦克、子弹、爆炸(略)

        # UI文本
        y = 10
        for i, p in enumerate(self.players):
            label = f"P{i+1}" if self.selected_mode != "SINGLE" else "Player"
            lives = p.lives if p.alive else 0
            lives_text = self.font.render(f"{label} Lives: {lives}", True, COLOR_WHITE)
            self.screen.blit(lives_text, (10, y))
            y += 30

        if self.selected_mode != "VS":
            total_score = sum(p.score for p in self.players)
            score_text = self.font.render(f"Score: {total_score}", True, COLOR_WHITE)
            self.screen.blit(score_text, (10, y))
            enemies_text = self.font.render(f"Enemies: {len(self.enemies)}", True, COLOR_WHITE)
            self.screen.blit(enemies_text, (10, y+30))

        # 游戏结束文字
        if self.game_over:
            msg = self.winner_text + "  Press R / ESC"
            color = COLOR_GREEN if "win" in self.winner_text.lower() or "Victory" in self.winner_text else COLOR_RED
            text = self.big_font.render(msg, True, color)
            rect = text.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))
            self.screen.blit(text, rect)

        pygame.display.flip()
10.设置与调试 典型问题及修复: 坦克重叠卡死:在 update_game() 中移动前和移动后各调用一次 separate_tanks(),强制分离。 斜向移动被挡后完全不动:采用分轴移动(先X后Y),允许部分方向移动。 对战模式下玩家子弹击中自己:为 Bullet 添加 owner 属性,碰撞时跳过发射者。 随机墙体堵住出生点:添加安全区域集合,墙体生成时排除这些格子。 子弹穿透坦克:增大子弹碰撞箱(6×6)并确保每帧更新,降低穿透概率。
点击查看代码
# 在 Game.update_game() 中,移动前后调用分离函数
def update_game(self):
    if self.game_over:
        return
    self.separate_tanks()          # 移动前分离
    keys = pygame.key.get_pressed()
    all_tanks = self.enemies + [p for p in self.players if p.alive]
    for p in self.players:
        if p.alive:
            other = [t for t in all_tanks if t is not p]
            p.update(keys, self.walls, other, self.bullets)
    for e in self.enemies:
        other = [t for t in all_tanks if t is not e]
        e.update(self.walls, other, self.bullets)
    for b in self.bullets:
        b.update()
    self.handle_collisions()
    self.separate_tanks()          # 移动后再次分离,防止重叠
四、实验结果 (一)实验视频 在下方的链接中,麻烦老师进B站看一看,谢谢老师😊 https://www.bilibili.com/video/BV1qiE56SExm/?spm_id_from=333.1387.homepage.video_card.click&vd_source=d317539e23c16de1e720ca7125033172 (二)结果分析 实验完整性:本次坦克大战游戏设计实验完整实现了预期目标。项目代码超过600行,涵盖超过五个核心模块,游戏可正常运行; 实验不足:敌方AI较为简单,子弹仍有穿透概率,缺乏音效与背景音乐; 未来展望:丰富游戏内容,利用Socket设计网络联机功能,添加音效与特效

五、全课内容总结
1.Python的含义
Python是一种跨平台、面向对象的高级程序设计语言,具有简洁、易读、丰富的第三方库等特点。它可以运行在Windows、Linux等多种操作系统上,变量无需提前声明类型,由系统自动推导。此外,我们还了解了Python的发展背景、应用场景及开发环境的搭建。
2.基础语法
注释:单行用 #,多行用 """。
保留字:像 if、else、for、def 这些不能拿来当变量名。
数据类型:整数、浮点数、字符串、布尔值、复数啥的,还可以用 int() 4.这种强行转换。
输入输出:input() 用来接收用户输入,print() 用来输出。
运算符:加减乘除、取余、比较、逻辑运算等。
代码规范:Python用缩进来表示代码块,这个写的时候要特别注意。
3.流程控制
条件判断:if、elif、else,根据条件走不同分支。
循环:while 循环,for 循环。
控制关键字:break 跳出循环,continue 跳过本次,pass 占位。
4.序列
列表:用 [],有序、可以改,能增删改查。
元组:用 (),有序、不能改。
字典:用 {},键值对形式,有点像查字典。
集合:也是 {},但里面的元素不能重复,而且无序。
切片:比如 sname[0:3:2],第一个数字是起始位置,第二个是结束位置(不包含),第三个是步长,左闭右开。
排序:list.sort() 直接改原列表,sorted() 会生成一个新副本。
5.字符串与正则表达式
字符串就是一串字符,用引号包起来。
可以用下标和切片来取一部分:字符串[起始:结束],下标从0开始。
常用方法:split() 分割、join() 合并、replace() 替换、strip() 去空格、count() 统计出现次数、find() 查找位置、upper()/lower() 大小写转换。
正则表达式:用来匹配或替换字符串。
6.函数
用 def 定义函数。
参数:有位置参数、关键字参数、默认参数,还有可变参数。
返回值用 return,可以一次返回多个值。
7.其他技术
socket:Socket=IP+端口
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
爬虫:爬取 + 解析。
用到的技术:网络请求模块、解析库;如果网站有反爬虫,可以通过加请求头信息等方式绕过。
(能回忆起来的就这些,求老师放过)

六、感想体会与建议
(一)感想体会
作为一个文科生,从对理科“深恶痛绝”到开开心心上Python,这门课真的给我留下了很深的印象。虽然只靠一学期的公选课可能只能学到一些皮毛,但确实已经带我对Python这门语言有了基本的了解,让我在未来面对Python的应用时可以有备而来,这对于数字化时代下的文科生是宝贵的能力。每次上课,我都会为代码的成功运行而欣喜,虽然它并不是我亲自写的,但我真切感受到我作为一个文科生,与严谨的代码和数字并不是永远割裂开的。每次经过老师指导调试代码的时候,我知道我虽然和老师不在同一个维度上,但在这时候想要写好代码精进自己的作业的心是真切的。快乐的时光总是短暂的,最后的课上老师放着电教四门口的丁香与桃花,不由得有些小破防,感慨着“年年岁岁花相似,岁岁年年人不同”,那就只能把幽默风趣热情温柔严谨细致的老师留给学弟学妹了(doge)。
(二)建议
老师真的已经超级超级好了,真的是为我们带来了更适合文科宝宝体质的Python课,如果硬要说建议的话,对于Python的基础功能和语言能不能设计一些实操类的小项目,让同学们能多动动手加深一下印象呢。剩下的真的没什么了,再要说就是老师一定要多喝热水,注意休息(
真的非常非常感谢老师啊,最后祝老师身体健康、事业顺利、家庭幸福、喜乐安宁!

posted @ 2026-06-09 20:07  塑料树  阅读(1)  评论(0)    收藏  举报