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()