python使用Pygame库实现避障小人行走游戏

python使用Pygame库实现避障小人行走游戏

能避开障碍物行走的小人:

一个小人在画布上随机移动;

玩家可以绘制障碍物,小人会自动避开;

玩家可以使用橡皮擦擦除障碍物。

点击"走"按钮开始小人物移动;

点击"停"按钮停止小人物移动;

可以使用速度滑块调整小人物移动速度。

点击"画笔"按钮后,在游戏区域按住鼠标左键并拖动绘制障碍物。

点击"橡皮"按钮后,在游戏区域按住鼠标左键并拖动清除障碍物。

点击"重玩"按钮重置游戏。

这个游戏html版本,可见:

https://blog.csdn.net/cnds123/article/details/148573128

关于Python中的pygame游戏模块的介绍,可参见 https://blog.csdn.net/cnds123/article/details/119514520

游戏截图:

游戏源码如下:

python 复制代码
import pygame
import sys
import math
import random

# 初始化pygame
pygame.init()

# 屏幕设置
WIDTH, HEIGHT = 1100, 700  # 增加宽度以容纳说明区域
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Avoidance Character Game")

# 设置中文字体
try:
    # 尝试使用系统中文字体
    font = pygame.font.SysFont('simhei', 24)  # 黑体
    title_font = pygame.font.SysFont('simhei', 30)
    small_font = pygame.font.SysFont('simhei', 18)
except:
    # 如果系统字体不可用,使用默认字体
    font = pygame.font.Font(None, 24)
    title_font = pygame.font.Font(None, 30)
    small_font = pygame.font.Font(None, 18)

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (52, 152, 219)
RED = (231, 76, 60)
GREEN = (46, 204, 113)
LIGHT_BLUE = (173, 216, 230)
PINK = (255, 159, 243)
GRAY = (200, 200, 200)
BG_COLOR = (240, 240, 240)
DARK_BLUE = (74, 105, 189)
LIGHT_GRAY = (241, 242, 246)

# 游戏区域定义 - 放在左侧
GAME_AREA_WIDTH = 700
GAME_AREA = pygame.Rect(20, 150, GAME_AREA_WIDTH, HEIGHT - 200)

# 说明区域 - 放在右侧
INSTRUCTION_AREA = pygame.Rect(GAME_AREA.right + 20, 150, WIDTH - GAME_AREA.right - 40, HEIGHT - 200)

# 游戏状态
class GameState:
    def __init__(self):
        self.is_moving = False
        self.speed = 2
        self.tool = "brush"  # "brush" 或 "eraser"
        self.is_drawing = False
        self.step = 0
        
        # 障碍物表面
        self.obstacle_surface = pygame.Surface((GAME_AREA_WIDTH, GAME_AREA.height), pygame.SRCALPHA)
        self.obstacle_surface.fill((0, 0, 0, 0))  # 透明背景
        
        # 小人属性 - 初始位置在游戏区域内
        self.character = {
            "x": random.randint(GAME_AREA.left + 50, GAME_AREA.right - 50),
            "y": random.randint(GAME_AREA.top + 50, GAME_AREA.bottom - 50),
            "direction": random.uniform(0, 2 * math.pi),
            "size": 30
        }

# 创建游戏状态
game_state = GameState()

# 按钮类
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.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, border_radius=25)
        pygame.draw.rect(surface, BLACK, self.rect, 2, border_radius=25)
        
        text_surface = font.render(self.text, True, WHITE)
        text_rect = text_surface.get_rect(center=self.rect.center)
        surface.blit(text_surface, text_rect)
        
    def check_hover(self, pos):
        self.is_hovered = self.rect.collidepoint(pos)
        
    def is_clicked(self, pos, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            return self.rect.collidepoint(pos)
        return False

# 创建按钮
start_btn = Button(50, 50, 100, 50, "走", GREEN, (39, 174, 96))
stop_btn = Button(170, 50, 100, 50, "停", RED, (192, 57, 43))
brush_btn = Button(290, 50, 100, 50, "画笔", BLUE, (41, 128, 185))
eraser_btn = Button(410, 50, 100, 50, "橡皮", LIGHT_BLUE, (93, 173, 226))
reset_btn = Button(530, 50, 100, 50, "重玩", (155, 89, 182), (142, 68, 173))

buttons = [start_btn, stop_btn, brush_btn, eraser_btn, reset_btn]

# 滑块类
class Slider:
    def __init__(self, x, y, width, height, min_val, max_val, initial_val):
        self.rect = pygame.Rect(x, y, width, height)
        self.min_val = min_val
        self.max_val = max_val
        self.value = initial_val
        self.dragging = False
        self.knob_radius = 15
        
    def draw(self, surface):
        # 绘制滑轨
        pygame.draw.rect(surface, (211, 211, 211), self.rect, border_radius=10)
        
        # 计算滑块位置
        knob_x = self.rect.x + (self.value - self.min_val) / (self.max_val - self.min_val) * self.rect.width
        
        # 绘制滑块
        pygame.draw.circle(surface, BLUE, (int(knob_x), self.rect.centery), self.knob_radius)
        
        # 显示数值
        text = font.render(f"移动速度: {int(self.value)}", True, BLACK)
        surface.blit(text, (self.rect.x, self.rect.y - 30))
        
    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            mouse_pos = pygame.mouse.get_pos()
            knob_x = self.rect.x + (self.value - self.min_val) / (self.max_val - self.min_val) * self.rect.width
            knob_rect = pygame.Rect(knob_x - self.knob_radius, self.rect.centery - self.knob_radius, 
                                   self.knob_radius * 2, self.knob_radius * 2)
            if knob_rect.collidepoint(mouse_pos):
                self.dragging = True
                
        elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            self.dragging = False
            
        elif event.type == pygame.MOUSEMOTION and self.dragging:
            mouse_x = pygame.mouse.get_pos()[0]
            # 限制在滑轨范围内
            mouse_x = max(self.rect.x, min(mouse_x, self.rect.x + self.rect.width))
            # 计算新值
            self.value = self.min_val + (mouse_x - self.rect.x) / self.rect.width * (self.max_val - self.min_val)
            self.value = int(self.value)

# 创建滑块
speed_slider = Slider(650, 65, 200, 10, 1, 10, 2)

# 绘制网格背景
def draw_grid():
    grid_size = 30
    for x in range(GAME_AREA.left, GAME_AREA.right, grid_size):
        pygame.draw.line(screen, GRAY, (x, GAME_AREA.top), (x, GAME_AREA.bottom), 1)
    for y in range(GAME_AREA.top, GAME_AREA.bottom, grid_size):
        pygame.draw.line(screen, GRAY, (GAME_AREA.left, y), (GAME_AREA.right, y), 1)

# 绘制小人
def draw_character():
    char = game_state.character
    x, y = char["x"], char["y"]
    size = char["size"]
    
    # 身体
    pygame.draw.circle(screen, BLUE, (int(x), int(y)), size // 3)
    
    # 头部
    pygame.draw.circle(screen, PINK, (int(x), int(y - size // 2)), size // 3)
    
    # 腿
    leg_angle = math.sin(game_state.step * 0.1) * math.pi / 6 if game_state.is_moving else 0
    pygame.draw.line(screen, BLUE, (x - 5, y + size // 4), 
                    (x - 10 * math.cos(leg_angle), y + size // 1.2 * math.sin(leg_angle)), 4)
    pygame.draw.line(screen, BLUE, (x + 5, y + size // 4), 
                    (x + 10 * math.cos(-leg_angle), y + size // 1.2 * math.sin(-leg_angle)), 4)
    
    # 眼睛
    pygame.draw.circle(screen, BLACK, (int(x - 4), int(y - size // 2 - 2)), 2)
    pygame.draw.circle(screen, BLACK, (int(x + 4), int(y - size // 2 - 2)), 2)
    
    # 嘴巴
    pygame.draw.arc(screen, BLACK, (x - 3, y - size // 2, 6, 6), 0, math.pi, 2)

# 检测碰撞
def check_collision():
    char = game_state.character
    size = char["size"]
    
    # 创建一个小矩形区域来检测碰撞
    rect = pygame.Rect(char["x"] - size // 2, char["y"] - size // 2, size, size)
    
    # 检查这个区域内是否有非透明像素
    for x in range(int(rect.left), int(rect.right)):
        for y in range(int(rect.top), int(rect.bottom)):
            if GAME_AREA.left <= x < GAME_AREA.right and GAME_AREA.top <= y < GAME_AREA.bottom:
                # 获取像素的alpha值(需要调整坐标,因为障碍物表面只包含游戏区域)
                rel_x = x - GAME_AREA.left
                rel_y = y - GAME_AREA.top
                alpha = game_state.obstacle_surface.get_at((rel_x, rel_y))[3]
                if alpha > 128:  # 如果遇到非透明像素
                    return True
    return False

# 移动小人
def move_character():
    if not game_state.is_moving:
        return
    
    char = game_state.character
    old_x, old_y = char["x"], char["y"]
    
    # 计算新位置
    char["x"] += math.cos(char["direction"]) * game_state.speed
    char["y"] += math.sin(char["direction"]) * game_state.speed
    
    # 边界检测 - 限制在游戏区域内
    if char["x"] < GAME_AREA.left + char["size"] / 2:
        char["x"] = GAME_AREA.left + char["size"] / 2
        char["direction"] = math.pi - char["direction"]
    elif char["x"] > GAME_AREA.right - char["size"] / 2:
        char["x"] = GAME_AREA.right - char["size"] / 2
        char["direction"] = math.pi - char["direction"]
    
    if char["y"] < GAME_AREA.top + char["size"] / 2:
        char["y"] = GAME_AREA.top + char["size"] / 2
        char["direction"] = -char["direction"]
    elif char["y"] > GAME_AREA.bottom - char["size"] / 2:
        char["y"] = GAME_AREA.bottom - char["size"] / 2
        char["direction"] = -char["direction"]
    
    # 碰撞检测
    if check_collision():
        # 恢复位置
        char["x"], char["y"] = old_x, old_y
        # 改变方向
        char["direction"] = random.uniform(0, 2 * math.pi)
    
    # 随机改变方向(小概率)
    if random.random() < 0.02:
        char["direction"] += (random.random() - 0.5) * math.pi / 2
    
    # 增加步数
    game_state.step += 1

# 重置游戏
def reset_game():
    game_state.is_moving = False
    game_state.speed = 2
    game_state.tool = "brush"
    game_state.step = 0
    game_state.character["x"] = random.randint(GAME_AREA.left + 50, GAME_AREA.right - 50)
    game_state.character["y"] = random.randint(GAME_AREA.top + 50, GAME_AREA.bottom - 50)
    game_state.character["direction"] = random.uniform(0, 2 * math.pi)
    game_state.obstacle_surface.fill((0, 0, 0, 0))  # 清除障碍物
    speed_slider.value = 2

# 绘制状态信息
def draw_status():
    status = "行走中" if game_state.is_moving else "停止"
    text = font.render(f"当前状态: {status} | 速度: {game_state.speed}", True, BLACK)
    screen.blit(text, (WIDTH // 2 - 100, HEIGHT - 40))

# 绘制游戏说明 - 在右侧独立区域显示
def draw_instructions():
    # 绘制说明区域背景
    pygame.draw.rect(screen, LIGHT_GRAY, INSTRUCTION_AREA, border_radius=10)
    pygame.draw.rect(screen, DARK_BLUE, INSTRUCTION_AREA, 2, border_radius=10)
    
    instructions = [
        "游戏说明:",
        "• 点击'走'按钮开始小人物移动",
        "• 点击'停'按钮停止小人物移动", 
        "• 使用速度滑块调整小人物移动速度",
        "• 点击'画笔'按钮后,在游戏区域按住",
        "  鼠标左键并拖动绘制障碍物",
        "• 点击'橡皮'按钮后,在游戏区域按住",
        "  鼠标左键并拖动清除障碍物",
        "• 点击'重玩'按钮重置游戏",
        "",
        "当前工具: " + ("画笔" if game_state.tool == "brush" else "橡皮")
    ]
    
    # 在说明区域内显示
    start_x = INSTRUCTION_AREA.x + 15
    start_y = INSTRUCTION_AREA.y + 15
    line_height = 25
    
    for i, line in enumerate(instructions):
        text = small_font.render(line, True, BLACK)
        screen.blit(text, (start_x, start_y + i * line_height))

# 绘制标题
def draw_title():
    title_text = title_font.render("行走的小人会自动避开您绘制的障碍物", True, WHITE)
    title_rect = title_text.get_rect(center=(WIDTH // 2, 20))
    
    # 绘制标题背景
    pygame.draw.rect(screen, DARK_BLUE, (0, 0, WIDTH, 50))
    screen.blit(title_text, title_rect)

# 主游戏循环
clock = pygame.time.Clock()
last_pos = None

running = True
while running:
    mouse_pos = pygame.mouse.get_pos()
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
        # 处理按钮点击
        for button in buttons:
            button.check_hover(mouse_pos)
            if button.is_clicked(mouse_pos, event):
                if button == start_btn:
                    game_state.is_moving = True
                elif button == stop_btn:
                    game_state.is_moving = False
                elif button == brush_btn:
                    game_state.tool = "brush"
                elif button == eraser_btn:
                    game_state.tool = "eraser"
                elif button == reset_btn:
                    reset_game()
        
        # 处理滑块
        speed_slider.handle_event(event)
        game_state.speed = speed_slider.value
        
        # 处理绘制/擦除
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            # 检查是否在游戏区域内
            if GAME_AREA.collidepoint(mouse_pos):
                game_state.is_drawing = True
                last_pos = mouse_pos
                
                if game_state.tool == "brush":
                    # 转换为障碍物表面的相对坐标
                    rel_pos = (mouse_pos[0] - GAME_AREA.left, mouse_pos[1] - GAME_AREA.top)
                    pygame.draw.circle(game_state.obstacle_surface, BLUE, rel_pos, 2)
                elif game_state.tool == "eraser":
                    rel_pos = (mouse_pos[0] - GAME_AREA.left, mouse_pos[1] - GAME_AREA.top)
                    pygame.draw.circle(game_state.obstacle_surface, (0, 0, 0, 0), rel_pos, 10)
                    
        elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            game_state.is_drawing = False
            last_pos = None
            
        elif event.type == pygame.MOUSEMOTION and game_state.is_drawing:
            # 检查是否在游戏区域内
            if GAME_AREA.collidepoint(mouse_pos):
                if last_pos:
                    if game_state.tool == "brush":
                        rel_last = (last_pos[0] - GAME_AREA.left, last_pos[1] - GAME_AREA.top)
                        rel_current = (mouse_pos[0] - GAME_AREA.left, mouse_pos[1] - GAME_AREA.top)
                        pygame.draw.line(game_state.obstacle_surface, BLUE, rel_last, rel_current, 4)
                    elif game_state.tool == "eraser":
                        rel_last = (last_pos[0] - GAME_AREA.left, last_pos[1] - GAME_AREA.top)
                        rel_current = (mouse_pos[0] - GAME_AREA.left, mouse_pos[1] - GAME_AREA.top)
                        pygame.draw.line(game_state.obstacle_surface, (0, 0, 0, 0), rel_last, rel_current, 20)
                last_pos = mouse_pos
    
    # 更新游戏状态
    move_character()
    
    # 绘制
    screen.fill(BG_COLOR)
    
    # 绘制标题
    draw_title()
    
    # 绘制游戏区域
    pygame.draw.rect(screen, WHITE, GAME_AREA)
    pygame.draw.rect(screen, DARK_BLUE, GAME_AREA, 3)
    
    # 绘制网格
    draw_grid()
    
    # 绘制障碍物(需要调整位置到游戏区域)
    screen.blit(game_state.obstacle_surface, (GAME_AREA.left, GAME_AREA.top))
    
    # 绘制小人
    draw_character()
    
    # 绘制UI元素
    for button in buttons:
        button.draw(screen)
    
    speed_slider.draw(screen)
    draw_status()
    draw_instructions()  # 在右侧独立区域显示
    
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()
相关推荐
jieyu11195 小时前
Python 实战:Web 漏洞 Python POC 代码及原理详解(2)
python·web安全
说话的鲸鱼5 小时前
‌Python+WhisperX:医疗语音识别的精准与高效实践
python·语音识别·xcode
java1234_小锋6 小时前
PyTorch2 Python深度学习 - 张量(Tensor)的定义与操作
开发语言·python·深度学习·pytorch2
小白学大数据6 小时前
Python爬虫定时任务:自动化抓取豆瓣每日最新短评
爬虫·python·自动化
qc17526 小时前
PyCharm + 远程调试路径映射总结(以 diffusers 为例)
ide·python·pycharm
壹号用户7 小时前
python学习之正则表达式
python·学习·正则表达式
汤姆yu7 小时前
2026版基于python的旅游景点推荐系统
开发语言·python·景点推荐
程序员大雄学编程7 小时前
《程序员AI之路:从Python起步》完全学习导航
人工智能·python
xiaojimao18 小时前
Django在服务端的部署(无废话)
后端·python·django