20242319 实验四《Python程序设计》实验报告

20242319 2024-2025-2 《Python程序设计》实验四报告

课程:《Python程序设计》
班级:2423
姓名:万翔宇
学号:20242319
实验教师:王志强
实验日期:2025年6月1日
必修/选修:公选课

前言

我从很小的时候就开始接触游戏了,但给我童年带来回忆最多的还得是4399小游戏。
在这里面,我最喜欢玩的双人游戏就是坦克动荡2,加上我一直以来都想做一款属于自己的游戏,于是我想通过这次的Python实验,设计一款双人坦克对战的游戏

(当然还是比较简陋的)

一.实验内容

设计一个双人坦克对战的游戏,对战双方能在本地设备上使用不同按键进行操作

二. 实验过程及结果

(一)安装pygame

想要用python写游戏,pygame超万能!

pip install pygame 

(二)引入数据库和函数

除了pygame,由于游戏中不能光是两辆坦克对轰,我们还要在地图上添加一些障碍物来增添游戏的多样性与可玩性,所以引入了random库;
此外,坦克的各种移动,发射炮弹等等都涉及到各种计算,所以又引入了math库......

import pygame 
import random 
import sys 
import os 
import math 
 
# 初始化pygame 
pygame.init() 

(三)设置好游戏的运行窗口和基本参数

# 游戏窗口设置 
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 
screen = pygame.display.set_mode((SCREEN_WIDTH,  SCREEN_HEIGHT))
pygame.display.set_caption(" 坦克大战 - 双人对战")
 
# 颜色定义 
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
GRAY = (128, 128, 128)
LIGHT_BLUE = (100, 100, 255)
LIGHT_RED = (255, 100, 100)
 
# 游戏参数 
FPS = 60 
clock = pygame.time.Clock() 

(四)定义具体函数

1.创建玩家坦克

我通过自己在电脑的作图软件上绘图,制作了游戏中双方的坦克模型:


然后在程序中加载坦克图像

        image_path = os.path.join("assets",  f"{name}.png")
        if not os.path.exists(image_path): 
            raise FileNotFoundError(f"图像文件 {image_path} 不存在")
            
        image = pygame.image.load(image_path).convert_alpha() 

最后定义玩家坦克的各项基本参数

        self.speed  = 2  
        self.health  = 100 
        self.shoot_cooldown  = 0 
        self.shoot_delay  = 30  
        ...

2.坦克的移动

一开始,我采用的是通过上下左右键来让坦克上下左右移动,但后来觉得这样不够灵活,有点死板,所以将坦克的移动改为360°全方位可动。
玩家的具体操作就是左右方向键控制坦克旋转,上下方向键控制前进后退,实现坦克全图可跑。
但这样的话代码就会涉及到坦克移动的方向向量,旋转角度,还要保证坦克旋转时始终保持在一个中心点上

        # 处理旋转 
        if self.rotate_left:   
            self.angle  = (self.angle  - 3) % 360
            self.update_direction()  
        if self.rotate_right:   
            self.angle  = (self.angle  + 3) % 360
            self.update_direction()  
        
        # 旋转图像并保持中心点 
        old_center = self.rect.center   
        self.image  = pygame.transform.rotate(self.original_image,  -self.angle)  
        self.rect  = self.image.get_rect(center=old_center)  
        
        # 处理移动 - 使用方向向量确保方向正确 
        if self.move_forward  or self.move_backward:  
            move_vector = self.direction  * self.speed   
            
            if self.move_forward:  
                self.rect.x  += move_vector.x 
                self.rect.y  += move_vector.y 
            else:
                self.rect.x  -= move_vector.x 
                self.rect.y  -= move_vector.y 
            
            # 确保不会移出屏幕 
            self.rect.clamp_ip(pygame.Rect(0,  0, SCREEN_WIDTH, SCREEN_HEIGHT))
 
    def update_direction(self):
        # 根据角度更新方向向量 
        rad = math.radians(self.angle)  
        self.direction  = pygame.math.Vector2(math.sin(rad),  -math.cos(rad)).normalize()

3.炮弹

既然是对战,那攻击方式就不能缺了。关于坦克发射的炮弹,为了增加可玩性,并提高玩家的操作上限,我给炮弹增加了可反弹机制,让双方在对战时能有更多的策略,而不是只是一昧攻击

            # 子弹碰撞检测 
            for bullet in bullets:
                # 先移动子弹 
                bullet.rect.x  += bullet.dx  
                bullet.rect.y  += bullet.dy  
                
                # 检查是否碰到墙壁 
                wall_hit = None 
                for wall in obstacles:
                    if bullet.rect.colliderect(wall.rect): 
                        wall_hit = wall 
                        break 
                
                if wall_hit:
                    # 计算碰撞法线 
                    dx = bullet.rect.centerx  - wall_hit.rect.centerx  
                    dy = bullet.rect.centery  - wall_hit.rect.centery  
                    
                    if abs(dx) > abs(dy):
                        normal_x = 1 if dx > 0 else -1 
                        normal_y = 0 
                    else:
                        normal_x = 0 
                        normal_y = 1 if dy > 0 else -1 
                    
                    # 回退子弹到碰撞前的位置 
                    bullet.rect.x  -= bullet.dx  
                    bullet.rect.y  -= bullet.dy  
                    
                    # 执行反弹 
                    bullet.bounce(normal_x,  normal_y)

4.生成障碍物

障碍物采用随机生成,增加游戏的可玩性

# 障碍物类
class Obstacle(pygame.sprite.Sprite): 
    def __init__(self, x, y, width, height):
        super().__init__()
        self.image  = pygame.Surface((width, height))
        self.image.fill(GRAY) 
        self.rect  = self.image.get_rect(topleft=(x,  y))

5.游戏结束条件

最后,双人对战总会有一个赢家,在双方激烈的战斗后,某一方的血量会被消耗为0,此时另一方胜利,游戏结束 game over!

            if player.health  <= 0:
                player.kill() 
                game_over = True 
                winner = player2 if player == player1 else player1 

6.游戏菜单

虽然游戏可以直接点开即玩,但为了游戏的美观性,让它看上去像那么个样,我还是给它设计了一个游戏主菜单,让玩家选择进行游戏还是退出(虽然确实没什么用。。。)

def main_menu():
    buttons = [
        Button(SCREEN_WIDTH//2-100, SCREEN_HEIGHT//2-60, 200, 50, "双人对战", LIGHT_BLUE, BLUE),
        Button(SCREEN_WIDTH//2-100, SCREEN_HEIGHT//2+20, 200, 50, "退出游戏", LIGHT_RED, RED)
    ]
    
    title_font = pygame.font.Font(font_name,  48) if font_name else pygame.font.SysFont(None,  48)
    title = title_font.render(" 坦克大战", True, YELLOW)
    
    control_font = pygame.font.Font(font_name,  18) if font_name else pygame.font.SysFont(None,  18)
    controls = [
        ("P1: WASD旋转移动, 空格射击", (SCREEN_WIDTH//2, SCREEN_HEIGHT-80)),
        ("P2: 方向键旋转移动, 回车射击", (SCREEN_WIDTH//2, SCREEN_HEIGHT-40))
    ]

(五)完整实现代码

import pygame 
import random 
import sys 
import os 
import math 
 
# 初始化pygame 
pygame.init() 
 
# 解决中文显示问题 
def init_font():
    """初始化中文字体支持"""
    font_paths = {
        "windows": "C:/Windows/Fonts/simhei.ttf", 
        "mac": "/System/Library/Fonts/STHeiti Medium.ttc", 
        "linux": "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", 
        "fallback": os.path.join("assets",  "fonts", "wqy-microhei.ttc") 
    }
    
    for path in font_paths.values(): 
        if os.path.exists(path): 
            try:
                test_font = pygame.font.Font(path,  20)
                if test_font.size(" 测试")[0] > 0:
                    return path, 20 
            except:
                continue 
    
    try:
        sys_fonts = ["microsoftyahei", "simhei", "arialuni"]
        for name in sys_fonts:
            font = pygame.font.SysFont(name,  20)
            if font.size(" 测试")[0] > 0:
                return name, 20 
    except:
        pass 
    
    return None, 24 
 
font_name, font_size = init_font()
 
# 游戏窗口设置 
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 
screen = pygame.display.set_mode((SCREEN_WIDTH,  SCREEN_HEIGHT))
pygame.display.set_caption(" 坦克大战 - 双人对战")
 
# 颜色定义 
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
GRAY = (128, 128, 128)
LIGHT_BLUE = (100, 100, 255)
LIGHT_RED = (255, 100, 100)
 
# 游戏参数 
FPS = 60 
clock = pygame.time.Clock() 
 
# 加载图像函数 
def load_image(name, scale=1, angle=0):
    try:
        # 尝试加载图像 
        image_path = os.path.join("assets",  f"{name}.png")
        if not os.path.exists(image_path): 
            raise FileNotFoundError(f"图像文件 {image_path} 不存在")
            
        image = pygame.image.load(image_path).convert_alpha() 
        
        # 缩放 
        if scale != 1:
            size = image.get_size() 
            image = pygame.transform.scale(image,  (int(size[0]*scale), int(size[1]*scale)))
        
        # 旋转 
        if angle != 0:
            image = pygame.transform.rotate(image,  angle)
            
        return image 
    except Exception as e:
        print(f"加载图像失败: {e},使用备用图形")
        surf = pygame.Surface((40, 40), pygame.SRCALPHA)
        color = BLUE if "1" in name else GREEN if "2" in name else RED 
        pygame.draw.rect(surf,  color, (0, 0, 40, 40))
        pygame.draw.rect(surf,  BLACK, (0, 0, 40, 40), 2)
        # 添加炮管标识方向 (默认朝上)
        pygame.draw.rect(surf,  BLACK, (15, 0, 10, 20))
        return surf 
 
# 玩家坦克类 
class PlayerTank(pygame.sprite.Sprite):  
    def __init__(self, x, y, player_num=1):
        super().__init__()
        self.player_num  = player_num 
        self.angle  = 0  # 当前角度(0度朝上,顺时针增加)
        
        # 加载坦克图像 
        tank_image_name = f"tank{player_num}"
        try:
            self.original_image  = load_image(tank_image_name)
            # 检查是否是默认图像 
            if isinstance(self.original_image,  pygame.Surface) and self.original_image.get_size()  == (40, 40):
                print(f"坦克{player_num}使用默认图形")
        except Exception as e:
            print(f"无法加载坦克{player_num}图像: {e},使用默认图形")
            color = BLUE if player_num == 1 else GREEN 
            self.original_image  = pygame.Surface((40, 40), pygame.SRCALPHA)
            pygame.draw.rect(self.original_image,  color, (0, 0, 40, 40))
            pygame.draw.rect(self.original_image,  BLACK, (0, 0, 40, 40), 2)
            # 添加炮管标识方向 (默认朝上)
            pygame.draw.rect(self.original_image,  BLACK, (15, 0, 10, 20))
        
        self.image  = self.original_image   
        self.rect  = self.image.get_rect(center=(x,  y))
        self.speed  = 2  
        self.health  = 100 
        self.shoot_cooldown  = 0 
        self.shoot_delay  = 30  
        self.move_forward  = False 
        self.move_backward  = False 
        self.rotate_left  = False 
        self.rotate_right  = False 
        self.direction  = pygame.math.Vector2(0,  -1)  # 初始方向朝上 
 
    def update(self):
        if self.shoot_cooldown  > 0:
            self.shoot_cooldown  -= 1 
        
        # 处理旋转 
        if self.rotate_left:   
            self.angle  = (self.angle  - 3) % 360
            self.update_direction()  
        if self.rotate_right:   
            self.angle  = (self.angle  + 3) % 360
            self.update_direction()  
        
        # 旋转图像并保持中心点 
        old_center = self.rect.center   
        self.image  = pygame.transform.rotate(self.original_image,  -self.angle)  
        self.rect  = self.image.get_rect(center=old_center)  
        
        # 处理移动 - 使用方向向量确保方向正确 
        if self.move_forward  or self.move_backward:  
            move_vector = self.direction  * self.speed   
            
            if self.move_forward:  
                self.rect.x  += move_vector.x 
                self.rect.y  += move_vector.y 
            else:
                self.rect.x  -= move_vector.x 
                self.rect.y  -= move_vector.y 
            
            # 确保不会移出屏幕 
            self.rect.clamp_ip(pygame.Rect(0,  0, SCREEN_WIDTH, SCREEN_HEIGHT))
 
    def update_direction(self):
        # 根据角度更新方向向量 
        rad = math.radians(self.angle)  
        self.direction  = pygame.math.Vector2(math.sin(rad),  -math.cos(rad)).normalize()  
 
    def shoot(self):
        if self.shoot_cooldown  == 0:
            self.shoot_cooldown  = self.shoot_delay   
            # 使用方向向量确保子弹方向正确 
            bullet_speed = 5  # 子弹速度 
            dx = self.direction.x  * bullet_speed 
            dy = self.direction.y  * bullet_speed 
            return Bullet(self.rect.centerx,  self.rect.centery,  dx, dy, f"player{self.player_num}")  
        return None 
 
# 子弹类 
class Bullet(pygame.sprite.Sprite): 
    def __init__(self, x, y, dx, dy, owner):
        super().__init__()
        self.image  = pygame.Surface((6, 6))
        self.image.fill(YELLOW) 
        self.rect  = self.image.get_rect(center=(x,  y))
        self.dx,  self.dy  = dx, dy 
        self.owner  = owner 
        self.distance_traveled  = 0 
        self.max_distance  = 800 
        self.bounce_count  = 0 
        self.max_bounces  = 3 
 
    def update(self):
        self.rect.x  += self.dx  
        self.rect.y  += self.dy  
        self.distance_traveled  += math.sqrt(self.dx**2  + self.dy**2) 
        
        if (self.rect.right  < 0 or self.rect.left  > SCREEN_WIDTH or 
                self.rect.bottom  < 0 or self.rect.top  > SCREEN_HEIGHT):
            self.kill() 
        
        if self.distance_traveled  >= self.max_distance: 
            self.kill() 
 
    def bounce(self, normal_x, normal_y):
        if self.bounce_count  >= self.max_bounces: 
            self.kill() 
            return 
        
        dot_product = self.dx  * normal_x + self.dy  * normal_y 
        self.dx  = self.dx  - 2 * dot_product * normal_x 
        self.dy  = self.dy  - 2 * dot_product * normal_y 
        
        self.rect.x  += self.dx  * 2 
        self.rect.y  += self.dy  * 2 
        
        self.bounce_count  += 1 
 
# 障碍物类 
class Obstacle(pygame.sprite.Sprite): 
    def __init__(self, x, y, width, height):
        super().__init__()
        self.image  = pygame.Surface((width, height))
        self.image.fill(GRAY) 
        self.rect  = self.image.get_rect(topleft=(x,  y))
 
# 迷宫生成器 
class MazeGenerator:
    @staticmethod 
    def generate_maze():
        obstacles = pygame.sprite.Group() 
        wall_thickness = 20 
        min_gap = 80 
        
        # 外围围墙 
        obstacles.add(Obstacle(0,  0, SCREEN_WIDTH, wall_thickness))
        obstacles.add(Obstacle(0,  SCREEN_HEIGHT-wall_thickness, SCREEN_WIDTH, wall_thickness))
        obstacles.add(Obstacle(0,  0, wall_thickness, SCREEN_HEIGHT))
        obstacles.add(Obstacle(SCREEN_WIDTH-wall_thickness,  0, wall_thickness, SCREEN_HEIGHT))
        
        safe_radius = 120 
        safe_zones = [
            pygame.Rect(SCREEN_WIDTH//4-safe_radius, SCREEN_HEIGHT//2-safe_radius, safe_radius*2, safe_radius*2),
            pygame.Rect(SCREEN_WIDTH*3//4-safe_radius, SCREEN_HEIGHT//2-safe_radius, safe_radius*2, safe_radius*2)
        ]
        
        cell_size = 200 
        vertical_walls = []
        horizontal_walls = []
        
        for x in range(cell_size, SCREEN_WIDTH-cell_size, cell_size):
            skip = any(abs(x - zone.centerx)  < safe_radius for zone in safe_zones)
            if not skip:
                length = random.choice([cell_size,  cell_size*2])
                y = random.randint(cell_size,  SCREEN_HEIGHT-cell_size-length)
                vertical_walls.append(pygame.Rect(x,  y, wall_thickness, length))
        
        for y in range(cell_size, SCREEN_HEIGHT-cell_size, cell_size):
            skip = any(abs(y - zone.centery)  < safe_radius for zone in safe_zones)
            if not skip:
                length = random.choice([cell_size,  cell_size*2])
                x = random.randint(cell_size,  SCREEN_WIDTH-cell_size-length)
                horizontal_walls.append(pygame.Rect(x,  y, length, wall_thickness))
        
        for wall in vertical_walls + horizontal_walls:
            if any(wall.colliderect(zone)  for zone in safe_zones):
                continue 
                
            if wall.width  > wall.height: 
                gap_pos = random.randint(wall.x+40,  wall.x+wall.width-40-min_gap) 
                obstacles.add(Obstacle(wall.x,  wall.y, gap_pos-wall.x, wall_thickness))
                obstacles.add(Obstacle(gap_pos+min_gap,  wall.y, wall.x+wall.width-gap_pos-min_gap,  wall_thickness))
            else:
                gap_pos = random.randint(wall.y+40,  wall.y+wall.height-40-min_gap) 
                obstacles.add(Obstacle(wall.x,  wall.y, wall_thickness, gap_pos-wall.y))
                obstacles.add(Obstacle(wall.x,  gap_pos+min_gap, wall_thickness, wall.y+wall.height-gap_pos-min_gap)) 
        
        for _ in range(3):
            while True:
                x1 = random.randint(100,  SCREEN_WIDTH-100)
                y1 = random.randint(100,  SCREEN_HEIGHT-100)
                x2 = random.randint(100,  SCREEN_WIDTH-100)
                y2 = random.randint(100,  SCREEN_HEIGHT-100)
                
                safe = True 
                for zone in safe_zones:
                    if (min(x1,x2) < zone.right  and max(x1,x2) > zone.left  and 
                        min(y1,y2) < zone.bottom  and max(y1,y2) > zone.top): 
                        safe = False 
                        break 
                
                if safe:
                    steps = max(abs(x2-x1), abs(y2-y1))
                    for i in range(steps):
                        x = x1 + (x2-x1)*i//steps 
                        y = y1 + (y2-y1)*i//steps 
                        if i % 5 == 0:
                            obstacles.add(Obstacle(x,  y, wall_thickness, wall_thickness))
                    break 
        
        return obstacles 
 
# 按钮类 
class Button:
    def __init__(self, x, y, width, height, text, color, hover_color):
        self.rect  = pygame.Rect(x, y, width, height)
        self.text  = text 
        self.color  = color 
        self.hover_color  = hover_color 
        self.font  = pygame.font.Font(font_name,  24) if font_name else pygame.font.SysFont(None,  24)
        self.is_hovered  = False 
 
    def draw(self, surface):
        color = self.hover_color  if self.is_hovered  else self.color  
        pygame.draw.rect(surface,  color, self.rect) 
        pygame.draw.rect(surface,  WHITE, self.rect,  2)
        text_surf = self.font.render(self.text,  True, WHITE)
        surface.blit(text_surf,  text_surf.get_rect(center=self.rect.center)) 
 
    def check_hover(self, pos):
        self.is_hovered  = self.rect.collidepoint(pos) 
        return self.is_hovered  
 
    def is_clicked(self, pos, event):
        return event.type  == pygame.MOUSEBUTTONDOWN and event.button  == 1 and self.rect.collidepoint(pos) 
 
# 主菜单 
def main_menu():
    buttons = [
        Button(SCREEN_WIDTH//2-100, SCREEN_HEIGHT//2-60, 200, 50, "双人对战", LIGHT_BLUE, BLUE),
        Button(SCREEN_WIDTH//2-100, SCREEN_HEIGHT//2+20, 200, 50, "退出游戏", LIGHT_RED, RED)
    ]
    
    title_font = pygame.font.Font(font_name,  48) if font_name else pygame.font.SysFont(None,  48)
    title = title_font.render(" 坦克大战", True, YELLOW)
    
    control_font = pygame.font.Font(font_name,  18) if font_name else pygame.font.SysFont(None,  18)
    controls = [
        ("P1: WASD旋转移动, 空格射击", (SCREEN_WIDTH//2, SCREEN_HEIGHT-80)),
        ("P2: 方向键旋转移动, 回车射击", (SCREEN_WIDTH//2, SCREEN_HEIGHT-40))
    ]
 
    running = True 
    while running:
        mouse_pos = pygame.mouse.get_pos() 
        
        for event in pygame.event.get(): 
            if event.type  == pygame.QUIT:
                pygame.quit() 
                sys.exit() 
            
            if buttons[0].is_clicked(mouse_pos, event):
                local_multiplayer_game()
            elif buttons[1].is_clicked(mouse_pos, event):
                pygame.quit() 
                sys.exit() 
 
        for btn in buttons:
            btn.check_hover(mouse_pos) 
 
        screen.fill(BLACK) 
        screen.blit(title,  (SCREEN_WIDTH//2 - title.get_width()//2,  100))
        
        for btn in buttons:
            btn.draw(screen) 
        
        for text, (x, y) in controls:
            text_surf = control_font.render(text,  True, WHITE)
            screen.blit(text_surf,  (x - text_surf.get_width()//2,  y))
        
        pygame.display.flip() 
        clock.tick(FPS) 
 
# 双人游戏模式 
def local_multiplayer_game():
    all_sprites = pygame.sprite.Group() 
    players = pygame.sprite.Group() 
    bullets = pygame.sprite.Group() 
    obstacles = MazeGenerator.generate_maze() 
    all_sprites.add(obstacles) 
 
    # 创建玩家坦克 
    player1 = PlayerTank(SCREEN_WIDTH//4, SCREEN_HEIGHT//2, 1)
    player2 = PlayerTank(SCREEN_WIDTH*3//4, SCREEN_HEIGHT//2, 2)
    players.add(player1,  player2)
    all_sprites.add(player1,  player2)
 
    game_over = False 
    winner = None 
    font = pygame.font.Font(font_name,  font_size) if font_name else pygame.font.SysFont(None,  font_size)
 
    running = True 
    while running:
        clock.tick(FPS) 
        
        # 事件处理 
        for event in pygame.event.get(): 
            if event.type  == pygame.QUIT:
                pygame.quit() 
                sys.exit() 
            elif event.type  == pygame.KEYDOWN:
                if event.key  == pygame.K_ESCAPE:
                    return 
                elif event.key  == pygame.K_r and game_over:
                    return local_multiplayer_game()
                
                # 玩家1控制 
                if event.key  == pygame.K_w:
                    player1.move_forward  = True 
                elif event.key  == pygame.K_s:
                    player1.move_backward  = True 
                elif event.key  == pygame.K_a:
                    player1.rotate_left  = True 
                elif event.key  == pygame.K_d:
                    player1.rotate_right  = True 
                elif event.key  == pygame.K_SPACE and player1.alive(): 
                    if bullet := player1.shoot(): 
                        bullets.add(bullet) 
                        all_sprites.add(bullet) 
                
                # 玩家2控制 
                if event.key  == pygame.K_UP:
                    player2.move_forward  = True 
                elif event.key  == pygame.K_DOWN:
                    player2.move_backward  = True 
                elif event.key  == pygame.K_LEFT:
                    player2.rotate_left  = True 
                elif event.key  == pygame.K_RIGHT:
                    player2.rotate_right  = True 
                elif event.key  == pygame.K_RETURN and player2.alive(): 
                    if bullet := player2.shoot(): 
                        bullets.add(bullet) 
                        all_sprites.add(bullet) 
            
            elif event.type  == pygame.KEYUP:
                # 玩家1释放按键 
                if event.key  == pygame.K_w:
                    player1.move_forward  = False 
                elif event.key  == pygame.K_s:
                    player1.move_backward  = False 
                elif event.key  == pygame.K_a:
                    player1.rotate_left  = False 
                elif event.key  == pygame.K_d:
                    player1.rotate_right  = False 
                
                # 玩家2释放按键 
                if event.key  == pygame.K_UP:
                    player2.move_forward  = False 
                elif event.key  == pygame.K_DOWN:
                    player2.move_backward  = False 
                elif event.key  == pygame.K_LEFT:
                    player2.rotate_left  = False 
                elif event.key  == pygame.K_RIGHT:
                    player2.rotate_right  = False 
 
        if not game_over:
            # 保存当前位置用于碰撞检测后回退 
            old_pos = {player: player.rect.copy()  for player in players}
            
            # 更新所有精灵 
            all_sprites.update() 
 
            # 子弹碰撞检测 
            for bullet in bullets:
                # 先移动子弹 
                bullet.rect.x  += bullet.dx  
                bullet.rect.y  += bullet.dy  
                
                # 检查是否碰到墙壁 
                wall_hit = None 
                for wall in obstacles:
                    if bullet.rect.colliderect(wall.rect): 
                        wall_hit = wall 
                        break 
                
                if wall_hit:
                    # 计算碰撞法线 
                    dx = bullet.rect.centerx  - wall_hit.rect.centerx  
                    dy = bullet.rect.centery  - wall_hit.rect.centery  
                    
                    if abs(dx) > abs(dy):
                        normal_x = 1 if dx > 0 else -1 
                        normal_y = 0 
                    else:
                        normal_x = 0 
                        normal_y = 1 if dy > 0 else -1 
                    
                    # 回退子弹到碰撞前的位置 
                    bullet.rect.x  -= bullet.dx  
                    bullet.rect.y  -= bullet.dy  
                    
                    # 执行反弹 
                    bullet.bounce(normal_x,  normal_y)
                
                # 检查是否碰到玩家 
                for player in players:
                    if bullet.rect.colliderect(player.rect)  and \
                       ((player == player1 and bullet.owner  == "player2") or \
                        (player == player2 and bullet.owner  == "player1")):
                        player.health  -= 10 
                        bullet.kill() 
                        if player.health  <= 0:
                            player.kill() 
                            game_over = True 
                            winner = player2 if player == player1 else player1 
                        break 
 
            # 玩家与墙壁碰撞检测 
            for player in players:
                if pygame.sprite.spritecollide(player,  obstacles, False):
                    player.rect  = old_pos[player]
 
            # 玩家间碰撞检测 
            if pygame.sprite.collide_rect(player1,  player2):
                player1.rect,  player2.rect  = old_pos[player1], old_pos[player2]
 
        # 渲染 
        screen.fill(BLACK) 
        all_sprites.draw(screen) 
        
        # 显示玩家生命值 
        screen.blit(font.render(f"P1:  {player1.health}",  True, BLUE), (10, 10))
        screen.blit(font.render(f"P2:  {player2.health}",  True, GREEN), (SCREEN_WIDTH-100, 10))
        
        # 游戏结束显示 
        if game_over:
            winner_text = font.render(f"{'P1'  if winner == player1 else 'P2'}获胜!", 
                                    True, BLUE if winner == player1 else GREEN)
            screen.blit(winner_text,  (SCREEN_WIDTH//2-50, SCREEN_HEIGHT//2-20))
            restart_text = font.render(" 按R键重新开始", True, WHITE)
            screen.blit(restart_text,  (SCREEN_WIDTH//2-restart_text.get_width()//2,  SCREEN_HEIGHT//2+20)) 
 
        pygame.display.flip() 
 
if __name__ == "__main__":
    main_menu()

程序代码托管到码云

三. 实验过程中遇到的问题和解决过程

  • 问题1:在编写坦克移动的代码时总是出错,按下方向键后坦克的图片旋转了,但是它实际的前进方向和炮弹发射方向却都是镜像相反的,操作起来感觉坦克在乱飞,在这里卡了很久

  • 问题1解决方案:后来通过一步步慢慢读代码,才发现是因为少写了个“-”负号,一直没检查出来T_T

  • 问题2:制作游戏菜单时,用于提示的中文字符无法显示

  • 问题2解决方案:通过询问AI,AI建议我尝试使用绝对路径测试是否能显示中文,结果成功了!

四. Python课程总结与感悟

当窗外的风声与电脑散热器的嗡鸣交织时,我对着屏幕上即将完成的Python实验报告,突然意识到这段学习旅程真的要画上句点了。

十二周的课程像被精心调教的递归函数,每次调用都留下更深的印记————真的很幸运能在大学阶段遇见王志强老师这样一位"三超导师"——超耐心的答疑、超专业的授课、超体贴的关怀。

还记得第一次用"手势签到"时的慌乱,到后来每周三晚上期待这充满仪式感的开场。

王老师不仅用生动的动画和详实的Word文档(doge)化解抽象概念,更在实验课上一行行手把手带我们写代码。这种"授人以渔"的态度,让我理解了Python哲学中"明确优于隐晦"的真谛。

有一句话您告诉我们的也一直在我学习Python的时候浮现在我脑海中————“人生苦短,我用Python!”

我也在学习Python的过程中领悟到Python是一门多么强大的语言,在今后的日子里我也会更加努力学习,进一步挖掘Python的独特魅力,发现Python的简洁之美。

王老师您不仅是我学习Python的引路人,更是用幽默化解焦虑的人生导师。这门课最大的魔力,是让我从"为学分而学"变成"为热爱而码"——这大概就是教育的最高境界吧!

😃

参考资料

posted @ 2025-06-07 21:29  小万万元  阅读(53)  评论(0)    收藏  举报