import pygame import sys import random import os

初始化Pygame

pygame.init()

-------------------------- 游戏常量配置 --------------------------

屏幕设置

SCREEN_WIDTH = 800 SCREEN_HEIGHT = 400 FPS = 60 GRAVITY = 0.8 JUMP_FORCE = -18 CROUCH_SPEED = 0.5

颜色定义

WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) GRAY = (200, 200, 200)

游戏状态枚举

class GameState: MENU = 0 PLAYING = 1 PAUSED = 2 GAME_OVER = 3

-------------------------- 资源加载(提前创建文件夹) --------------------------

创建必要的文件夹

if not os.path.exists("assets"): os.makedirs("assets") os.makedirs("assets/sounds")

尝试加载音效(如果没有音效文件则跳过)

try: # 跳跃音效 jump_sound = pygame.mixer.Sound("assets/sounds/jump.wav") # 碰撞音效 hit_sound = pygame.mixer.Sound("assets/sounds/hit.wav") # 计分音效 score_sound = pygame.mixer.Sound("assets/sounds/score.wav") except: # 没有音效文件时使用空函数替代 class DummySound: def play(self): pass jump_sound = DummySound() hit_sound = DummySound() score_sound = DummySound()

-------------------------- 玩家类 --------------------------

class Player(pygame.sprite.Sprite): def init(self): super().init() # 角色尺寸(正常/下蹲状态) self.normal_size = (60, 80) self.crouch_size = (60, 40)

    # 创建简易角色图形(替代图片)
    self.normal_image = pygame.Surface(self.normal_size)
    self.normal_image.fill(BLUE)
    # 绘制面部特征
    pygame.draw.circle(self.normal_image, WHITE, (30, 30), 15)  # 脸
    pygame.draw.circle(self.normal_image, BLACK, (25, 25), 3)   # 左眼
    pygame.draw.circle(self.normal_image, BLACK, (35, 25), 3)   # 右眼
    pygame.draw.arc(self.normal_image, BLACK, (25, 35, 10, 5), 0, 3.14, 2)  # 嘴
    
    self.crouch_image = pygame.Surface(self.crouch_size)
    self.crouch_image.fill(BLUE)
    # 下蹲状态的面部
    pygame.draw.circle(self.crouch_image, WHITE, (30, 20), 10)
    pygame.draw.circle(self.crouch_image, BLACK, (25, 18), 2)
    pygame.draw.circle(self.crouch_image, BLACK, (35, 18), 2)
    
    self.image = self.normal_image
    self.rect = self.image.get_rect()
    self.rect.x = 100
    self.rect.y = SCREEN_HEIGHT - self.normal_size[1] - 20
    
    # 物理属性
    self.velocity_y = 0
    self.on_ground = True
    self.is_crouching = False
    self.crouch_timer = 0

def update(self):
    # 应用重力
    self.velocity_y += GRAVITY
    self.rect.y += self.velocity_y
    
    # 地面碰撞检测
    ground_level = SCREEN_HEIGHT - self.normal_size[1] - 20
    if self.rect.y >= ground_level:
        self.rect.y = ground_level
        self.velocity_y = 0
        self.on_ground = True
    
    # 处理下蹲逻辑
    if self.is_crouching:
        self.crouch_timer += 1
        if self.crouch_timer > FPS * 0.5:  # 最多下蹲0.5秒
            self.stand_up()
    else:
        self.crouch_timer = 0

def jump(self):
    """玩家跳跃"""
    if self.on_ground and not self.is_crouching:
        self.velocity_y = JUMP_FORCE
        self.on_ground = False
        jump_sound.play()

def crouch(self):
    """玩家下蹲"""
    if self.on_ground and not self.is_crouching:
        self.is_crouching = True
        self.image = self.crouch_image
        # 调整位置避免穿地
        self.rect.y = SCREEN_HEIGHT - self.crouch_size[1] - 20

def stand_up(self):
    """玩家站起"""
    if self.is_crouching:
        self.is_crouching = False
        self.image = self.normal_image
        self.rect.y = SCREEN_HEIGHT - self.normal_size[1] - 20

-------------------------- 障碍物类 --------------------------

class Obstacle(pygame.sprite.Sprite): def init(self): super().init() # 随机生成不同类型的障碍物 self.obstacle_type = random.choice(["small", "medium", "tall"])

    if self.obstacle_type == "small":
        self.size = (40, 40)
    elif self.obstacle_type == "medium":
        self.size = (50, 60)
    else:  # tall
        self.size = (30, 80)
    
    # 创建障碍物图形
    self.image = pygame.Surface(self.size)
    self.image.fill(RED)
    # 添加纹理
    for i in range(0, self.size[0], 10):
        pygame.draw.line(self.image, BLACK, (i, 0), (i, self.size[1]), 2)
    
    self.rect = self.image.get_rect()
    self.rect.x = SCREEN_WIDTH
    self.rect.y = SCREEN_HEIGHT - self.size[1] - 20
    
    # 随机速度(增加游戏难度)
    self.speed = random.randint(8, 12)

def update(self):
    """更新障碍物位置"""
    self.rect.x -= self.speed
    # 移除超出屏幕的障碍物
    if self.rect.right < 0:
        self.kill()

-------------------------- 背景类 --------------------------

class Background: def init(self): self.layers = [] # 创建多层背景(视差效果) for i in range(3): layer = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) layer.fill((135 + i20, 206 + i10, 235 + i5)) # 渐变天空蓝 # 添加云朵 for j in range(5 + i): cloud = pygame.Surface((80 - i10, 40 - i5), pygame.SRCALPHA) pygame.draw.ellipse(cloud, WHITE, (0, 0, 80 - i10, 40 - i5)) cloud_x = random.randint(0, SCREEN_WIDTH) cloud_y = random.randint(50, 150 - i30) layer.blit(cloud, (cloud_x, cloud_y)) self.layers.append({ "surface": layer, "x": 0, "speed": 1 + i*0.5 # 不同层不同速度 })

    # 创建地面
    self.ground = pygame.Surface((SCREEN_WIDTH, 20))
    self.ground.fill(GREEN)
    # 添加地面纹理
    for i in range(0, SCREEN_WIDTH, 20):
        pygame.draw.rect(self.ground, (0, 150, 0), (i, 0, 10, 20))

def update(self):
    """更新背景位置(滚动效果)"""
    for layer in self.layers:
        layer["x"] -= layer["speed"]
        if layer["x"] <= -SCREEN_WIDTH:
            layer["x"] = 0

def draw(self, screen):
    """绘制背景"""
    for layer in self.layers:
        screen.blit(layer["surface"], (layer["x"], 0))
        screen.blit(layer["surface"], (layer["x"] + SCREEN_WIDTH, 0))
    # 绘制地面
    screen.blit(self.ground, (0, SCREEN_HEIGHT - 20))

-------------------------- 游戏主类 --------------------------

class ParkourGame: def init(self): # 创建游戏窗口 self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("高级跑酷游戏")

    # 游戏时钟
    self.clock = pygame.time.Clock()
    
    # 初始化游戏状态
    self.game_state = GameState.MENU
    self.score = 0
    self.high_score = 0
    self.last_score_time = pygame.time.get_ticks()
    
    # 加载高分记录
    self.load_high_score()
    
    # 创建游戏对象
    self.background = Background()
    self.all_sprites = pygame.sprite.Group()
    self.obstacles = pygame.sprite.Group()
    
    # 创建玩家
    self.player = Player()
    self.all_sprites.add(self.player)
    
    # 障碍物生成计时器
    self.obstacle_timer = 0
    self.min_obstacle_interval = 800  # 最小生成间隔(毫秒)
    
    # 字体设置
    self.font_large = pygame.font.Font(None, 72)
    self.font_medium = pygame.font.Font(None, 48)
    self.font_small = pygame.font.Font(None, 36)

def load_high_score(self):
    """加载高分记录"""
    try:
        with open("high_score.txt", "r") as f:
            self.high_score = int(f.read())
    except:
        self.high_score = 0

def save_high_score(self):
    """保存高分记录"""
    if self.score > self.high_score:
        self.high_score = self.score
        with open("high_score.txt", "w") as f:
            f.write(str(self.high_score))

def spawn_obstacle(self):
    """生成新的障碍物"""
    current_time = pygame.time.get_ticks()
    if current_time - self.obstacle_timer > self.min_obstacle_interval:
        obstacle = Obstacle()
        self.all_sprites.add(obstacle)
        self.obstacles.add(obstacle)
        self.obstacle_timer = current_time
        # 随着游戏进行,减小生成间隔(增加难度)
        self.min_obstacle_interval = max(300, self.min_obstacle_interval - 5)

def reset_game(self):
    """重置游戏状态"""
    # 保存高分
    self.save_high_score()
    
    # 重置分数和计时器
    self.score = 0
    self.obstacle_timer = 0
    self.min_obstacle_interval = 800
    self.last_score_time = pygame.time.get_ticks()
    
    # 清除所有障碍物
    for obstacle in self.obstacles:
        obstacle.kill()
    
    # 重置玩家位置
    self.player.rect.x = 100
    self.player.rect.y = SCREEN_HEIGHT - self.player.normal_size[1] - 20
    self.player.velocity_y = 0
    self.player.on_ground = True
    self.player.is_crouching = False
    
    # 设置游戏状态为运行中
    self.game_state = GameState.PLAYING

def handle_events(self):
    """处理游戏事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            self.save_high_score()
            pygame.quit()
            sys.exit()
        
        # 键盘事件
        if event.type == pygame.KEYDOWN:
            # ESC键暂停/继续
            if event.key == pygame.K_ESCAPE:
                if self.game_state == GameState.PLAYING:
                    self.game_state = GameState.PAUSED
                elif self.game_state == GameState.PAUSED:
                    self.game_state = GameState.PLAYING
            
            # R键重启游戏
            if event.key == pygame.K_r:
                self.reset_game()
            
            # 空格键跳跃
            if event.key == pygame.K_SPACE and self.game_state == GameState.PLAYING:
                self.player.jump()
            
            # 下方向键下蹲
            if event.key == pygame.K_DOWN and self.game_state == GameState.PLAYING:
                self.player.crouch()
            
            # 回车键开始游戏(菜单界面)
            if event.key == pygame.K_RETURN and self.game_state == GameState.MENU:
                self.reset_game()
        
        # 释放下蹲键
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                self.player.stand_up()

def update_game(self):
    """更新游戏逻辑"""
    if self.game_state == GameState.PLAYING:
        # 更新背景
        self.background.update()
        
        # 更新所有精灵
        self.all_sprites.update()
        
        # 生成障碍物
        self.spawn_obstacle()
        
        # 计分逻辑(每0.5秒加1分)
        current_time = pygame.time.get_ticks()
        if current_time - self.last_score_time > 500:
            self.score += 1
            self.last_score_time = current_time
            if self.score % 10 == 0:  # 每10分播放一次计分音效
                score_sound.play()
        
        # 碰撞检测
        if pygame.sprite.spritecollide(self.player, self.obstacles, False):
            hit_sound.play()
            self.game_state = GameState.GAME_OVER
    
    # 菜单和结束界面不需要更新游戏逻辑

def draw_game(self):
    """绘制游戏画面"""
    # 绘制背景
    self.background.draw(self.screen)
    
    if self.game_state == GameState.PLAYING:
        # 绘制所有精灵
        self.all_sprites.draw(self.screen)
        
        # 绘制分数
        score_text = self.font_medium.render(f"分数: {self.score}", True, BLACK)
        self.screen.blit(score_text, (20, 20))
        
        # 绘制高分
        high_score_text = self.font_small.render(f"最高分: {self.high_score}", True, BLACK)
        self.screen.blit(high_score_text, (20, 70))
    
    elif self.game_state == GameState.MENU:
        # 绘制开始菜单
        title_text = self.font_large.render("PYTHON 跑酷游戏", True, BLACK)
        start_text = self.font_medium.render("按 ENTER 开始游戏", True, BLACK)
        instruction_text = self.font_small.render("空格跳跃 | 下方向键下蹲 | ESC暂停 | R重启", True, BLACK)
        
        self.screen.blit(title_text, (SCREEN_WIDTH//2 - title_text.get_width()//2, 100))
        self.screen.blit(start_text, (SCREEN_WIDTH//2 - start_text.get_width()//2, 200))
        self.screen.blit(instruction_text, (SCREEN_WIDTH//2 - instruction_text.get_width()//2, 300))
    
    elif self.game_state == GameState.PAUSED:
        # 绘制暂停界面
        pause_text = self.font_large.render("游戏暂停", True, BLACK)
        resume_text = self.font_medium.render("按 ESC 继续 | 按 R 重启", True, BLACK)
        
        # 半透明遮罩
        mask = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        mask.fill(WHITE)
        mask.set_alpha(128)
        self.screen.blit(mask, (0, 0))
        
        self.screen.blit(pause_text, (SCREEN_WIDTH//2 - pause_text.get_width()//2, 150))
        self.screen.blit(resume_text, (SCREEN_WIDTH//2 - resume_text.get_width()//2, 250))
    
    elif self.game_state == GameState.GAME_OVER:
        # 绘制游戏结束界面
        game_over_text = self.font_large.render("游戏结束", True, RED)
        score_text = self.font_medium.render(f"最终分数: {self.score}", True, BLACK)
        high_score_text = self.font_medium.render(f"最高分: {self.high_score}", True, BLACK)
        restart_text = self.font_small.render("按 R 重新开始 | 按 ESC 返回菜单", True, BLACK)
        
        # 半透明遮罩
        mask = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        mask.fill(WHITE)
        mask.set_alpha(128)
        self.screen.blit(mask, (0, 0))
        
        self.screen.blit(game_over_text, (SCREEN_WIDTH//2 - game_over_text.get_width()//2, 100))
        self.screen.blit(score_text, (SCREEN_WIDTH//2 - score_text.get_width()//2, 200))
        self.screen.blit(high_score_text, (SCREEN_WIDTH//2 - high_score_text.get_width()//2, 250))
        self.screen.blit(restart_text, (SCREEN_WIDTH//2 - restart_text.get_width()//2, 300))
    
    # 更新屏幕显示
    pygame.display.flip()

def run(self):
    """游戏主循环"""
    while True:
        # 处理事件
        self.handle_events()
        
        # 更新游戏状态
        self.update_game()
        
        # 绘制游戏画面
        self.draw_game()
        
        # 控制帧率
        self.clock.tick(FPS)

-------------------------- 启动游戏 --------------------------

if name == "main": game = ParkourGame() game.run()

1 条评论

  • 1