编写目标
使用pygame
仿照B站上经常可以刷到的小球能否在规定时间内逃脱
环境设置与窗口初始化
安装pygame
pip install pygame
导入必要的模块并初始化 Pygame
导入模块
导入pygame
和 sys
模块,其中 pygame
用于游戏开发,sys
用于系统退出。
import pygame
import sys
初始化 Pygame并设置屏幕宽度和高度还有颜色
使用 pygame.init()
初始化所有 Pygame
模块。
# 初始化 Pygame
pygame.init()
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")# 窗口标题
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
时钟与帧率
使用 pygame.time.Clock()
控制游戏的帧率。
clock = pygame.time.Clock()
FPS = 60
主循环
一个无限循环,用于处理事件、更新游戏状态和绘制内容。按下窗口的关闭按钮将退出循环并关闭游戏。
running = True
while running:
clock.tick(FPS) # 控制游戏帧率
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 填充背景
screen.fill(WHITE)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
整体代码
import pygame
import sys
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 主循环
running = True
while running:
clock.tick(FPS) # 控制游戏帧率
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 填充背景
screen.fill(WHITE)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
运行效果
绘制大圆
在窗口中央绘制一个静止的大圆。这个大圆将作为小球活动的边界,设定大圆的半径为300像素。
定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2) #保证位于屏幕的中心
BIG_CIRCLE_RADIUS = 300 # 大圆半径
大圆绘制代码
使用 pygame.draw.circle()
函数在屏幕上绘制大圆。
- screen:绘制的目标表面。
- BLACK:圆的颜色。
- CENTER:圆心位置。
- BIG_CIRCLE_RADIUS:圆的半径。
- 2:圆的线宽,设置为2像素。
pygame.draw.circle(screen, BLACK, CENTER, BIG_CIRCLE_RADIUS, 2) # 线宽为2
整体代码
import pygame
import sys
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
BIG_CIRCLE_RADIUS = 300 # 大圆半径
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 主循环
running = True
while running:
clock.tick(FPS) # 控制游戏帧率
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 填充背景
screen.fill(WHITE)
# 画大圆
pygame.draw.circle(screen, BLACK, CENTER, BIG_CIRCLE_RADIUS, 2) # 线宽为2
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
创建小球类并绘制小球
创建一个 SmallCircle
类,用于管理小球的属性和行为。然后,在窗口中绘制一个静止的小球。
SmallCircle
初始化小球的半径、颜色和位置。初始位置设定在大圆的中心。draw
方法:使用 pygame.draw.circle() 在指定表面绘制小球。
class SmallCircle:
def __init__(self, radius=10, color=RED):
self.radius = radius
self.color = color
self.pos = list(CENTER) # 初始位置在大圆中心
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
实例化并显示
small_circle = SmallCircle()
这里是实例化的小球
# 画小球
small_circle.draw(screen)
在主循环中绘制小球
整体代码
import pygame
import sys
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
BIG_CIRCLE_RADIUS = 300 # 大圆半径
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED):
self.radius = radius
self.color = color
self.pos = list(CENTER) # 初始位置在大圆中心
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 实例化小球
small_circle = SmallCircle()
# 主循环
running = True
while running:
clock.tick(FPS) # 控制游戏帧率
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 填充背景
screen.fill(WHITE)
# 画大圆
pygame.draw.circle(screen, BLACK, CENTER, BIG_CIRCLE_RADIUS, 2) # 线宽为2
# 画小球
small_circle.draw(screen)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
红色的小球位于大圆的中心位置。此时,小球是静止的,没有运动。
实现小球的运动与碰撞检测
为小球添加运动能力,使其以固定速度向随机方向移动,同时,需要检测小球是否碰到大圆的边界,并在碰撞时反弹。
SmallCircle添加速度和移动逻辑
- 速度设置:构建
set_random_velocity
方法为小球设置一个随机方向的速度向量。通过生成一个随机角度angle
,并计算水平和垂直速度分量。 - 位置更新与碰撞检测:构建
update
方法,更新小球的位置,根据当前速度向量vel
;计算小球与大圆中心的距离dist
;如果小球触及或超过大圆边界(dist + self.radius >= BIG_CIRCLE_RADIUS)
,则反射其速度方向,实现弹跳效果。 - 反射逻辑:计算小球与大圆中心的夹角 angle,更新速度向量,使其朝相反方向移动。
整体代码
import pygame
import sys
import math
import random
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
BIG_CIRCLE_RADIUS = 300 # 大圆半径
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 工具函数
def distance(p1, p2):
"""计算两点之间的距离"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def normalize(vec):
"""归一化一个向量"""
mag = math.hypot(vec[0], vec[1])
if mag == 0:
return [0, 0]
return [vec[0] / mag, vec[1] / mag]
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self):
"""为小球设置一个随机的速度方向"""
angle = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(angle),
self.speed * math.sin(angle)
]
def update(self):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, CENTER)
# 检测碰撞:如果小球触及大圆边界
if dist + self.radius >= BIG_CIRCLE_RADIUS:
# 计算反射角
angle = math.atan2(self.pos[1] - CENTER[1], self.pos[0] - CENTER[0])
# 反射方向
self.vel[0] = -self.speed * math.cos(angle)
self.vel[1] = -self.speed * math.sin(angle)
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 实例化小球
small_circle = SmallCircle()
# 主循环
running = True
while running:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新小球位置
small_circle.update()
# 填充背景
screen.fill(WHITE)
# 画大圆
pygame.draw.circle(screen, BLACK, CENTER, BIG_CIRCLE_RADIUS, 2) # 线宽为2
# 画小球
small_circle.draw(screen)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
小球从大圆中心向随机方向移动,并在碰到大圆边界时反弹,持续在大圆内部弹跳。
添加旋转缺口的大圆
在大圆上添加一个不断旋转的缺口。当小球通过这个缺口时,可以实现移除大圆。
BigCircle
创建 BigCircle 类,管理大圆的缺口旋转和绘制。
初始化
定义中心位置、半径、缺口宽度和旋转速度。
update 方法
根据时间增量 dt
更新缺口的角度,确保缺口持续旋转。
is_within_notch 方法
判断给定角度是否位于当前缺口区域内。
draw 方法
绘制大圆的边界,计算缺口的外环和内环点,使用多边形绘制缺口区域(用白色填充,覆盖大圆的一部分)。
class BigCircle:
def __init__(self, center, radius, notch_width_deg=30, angular_speed_deg_per_sec=30):
self.center = center
self.radius = radius
self.notch_width = math.radians(notch_width_deg) # 缺口宽度,转换为弧度
self.angular_speed = math.radians(angular_speed_deg_per_sec) # 旋转速度,弧度/秒
self.current_angle = 0 # 当前缺口角度
def update(self, dt):
self.current_angle += self.angular_speed * dt
self.current_angle %= 2 * math.pi # 保持角度在0到2π之间
def is_within_notch(self, angle):
angle = angle % (2 * math.pi)
start_angle = self.current_angle
end_angle = (self.current_angle + self.notch_width) % (2 * math.pi)
if start_angle < end_angle:
return start_angle <= angle <= end_angle
else:
# 缺口跨越了0度
return angle >= start_angle or angle <= end_angle
def draw(self, surface):
# 画大圆
pygame.draw.circle(surface, BLACK, self.center, self.radius, 2)
# 绘制缺口区域(用背景色填充)
notch_points = []
num_points = 30 # 缺口的平滑程度
outer_radius = self.radius
inner_radius = self.radius - 10 # 缺口的厚度
# 计算缺口的外环点
for i in range(num_points + 1):
angle = self.current_angle + (self.notch_width * i) / num_points
x = self.center[0] + outer_radius * math.cos(angle)
y = self.center[1] + outer_radius * math.sin(angle)
notch_points.append((x, y))
# 计算缺口的内环点
for i in range(num_points + 1):
angle = self.current_angle + self.notch_width - (self.notch_width * i) / num_points
x = self.center[0] + inner_radius * math.cos(angle)
y = self.center[1] + inner_radius * math.sin(angle)
notch_points.append((x, y))
# 绘制缺口区域
pygame.draw.polygon(surface, WHITE, notch_points)
SmallCircle
update
更新小球的位置,计算小球与大圆中心的距离和角度,检测是否碰到大圆边界,如果小球通过缺口(is_within_notch
返回 True),则重置小球位置,否则,反弹小球的速度方向。
bounce
计算反射后的速度方向,使小球反弹回大圆内部。
reset_position
将小球位置重置到大圆中心,并设置新的随机速度方向。
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self):
"""为小球设置一个随机的速度方向"""
angle = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(angle),
self.speed * math.sin(angle)
]
def update(self, big_circle):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, self.center())
# 计算小球相对于大圆中心的角度
angle = math.atan2(self.pos[1] - self.center()[1], self.pos[0] - self.center()[0])
# 检测是否碰到大圆边界
if dist + self.radius >= big_circle.radius:
if big_circle.is_within_notch(angle):
# 通过缺口,重置小球位置 删除大圆
big_circles.remove(big_circle)
self.reset_position()
else:
# 反弹
self.bounce(angle)
def center(self):
"""返回大圆的中心位置"""
return CENTER
def bounce(self, angle):
"""反弹小球的速度方向"""
# 反射角度
reflected_angle = angle + math.pi
self.vel[0] = self.speed * math.cos(reflected_angle)
self.vel[1] = self.speed * math.sin(reflected_angle)
def reset_position(self):
"""重置小球到大圆中心,并设置新方向"""
self.pos = list(CENTER)
self.set_random_velocity()
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
整体代码
import pygame
import sys
import math
import random
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
BIG_CIRCLE_RADIUS = 300 # 大圆半径
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 工具函数
def distance(p1, p2):
"""计算两点之间的距离"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def normalize(vec):
"""归一化一个向量"""
mag = math.hypot(vec[0], vec[1])
if mag == 0:
return [0, 0]
return [vec[0] / mag, vec[1] / mag]
# 大圆类
class BigCircle:
def __init__(self, center, radius, notch_width_deg=30, angular_speed_deg_per_sec=30):
self.center = center
self.radius = radius
self.notch_width = math.radians(notch_width_deg) # 缺口宽度,转换为弧度
self.angular_speed = math.radians(angular_speed_deg_per_sec) # 旋转速度,弧度/秒
self.current_angle = 0 # 当前缺口角度
def update(self, dt):
"""更新缺口的角度"""
self.current_angle += self.angular_speed * dt
self.current_angle %= 2 * math.pi # 保持角度在0到2π之间
def is_within_notch(self, angle):
"""判断给定角度是否在缺口区域内"""
angle = angle % (2 * math.pi)
start_angle = self.current_angle
end_angle = (self.current_angle + self.notch_width) % (2 * math.pi)
if start_angle < end_angle:
return start_angle <= angle <= end_angle
else:
# 缺口跨越了0度
return angle >= start_angle or angle <= end_angle
def draw(self, surface):
# 画大圆
pygame.draw.circle(surface, BLACK, self.center, self.radius, 2)
# 绘制缺口区域(用背景色填充)
notch_points = []
num_points = 30 # 缺口的平滑程度
outer_radius = self.radius
inner_radius = self.radius - 10 # 缺口的厚度
# 计算缺口的外环点
for i in range(num_points + 1):
angle = self.current_angle + (self.notch_width * i) / num_points
x = self.center[0] + outer_radius * math.cos(angle)
y = self.center[1] + outer_radius * math.sin(angle)
notch_points.append((x, y))
# 计算缺口的内环点
for i in range(num_points + 1):
angle = self.current_angle + self.notch_width - (self.notch_width * i) / num_points
x = self.center[0] + inner_radius * math.cos(angle)
y = self.center[1] + inner_radius * math.sin(angle)
notch_points.append((x, y))
# 绘制缺口区域
pygame.draw.polygon(surface, WHITE, notch_points)
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self):
"""为小球设置一个随机的速度方向"""
angle = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(angle),
self.speed * math.sin(angle)
]
def update(self, big_circle):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, self.center())
# 计算小球相对于大圆中心的角度
angle = math.atan2(self.pos[1] - self.center()[1], self.pos[0] - self.center()[0])
# 检测是否碰到大圆边界
if dist + self.radius >= big_circle.radius:
if big_circle.is_within_notch(angle):
# 通过缺口,重置小球位置
self.reset_position()
big_circles.remove(big_circle)
else:
# 反弹
self.bounce(angle)
def center(self):
"""返回大圆的中心位置"""
return CENTER
def bounce(self, angle):
"""反弹小球的速度方向"""
# 反射角度
reflected_angle = angle + math.pi
self.vel[0] = self.speed * math.cos(reflected_angle)
self.vel[1] = self.speed * math.sin(reflected_angle)
def reset_position(self):
"""重置小球到大圆中心,并设置新方向"""
self.pos = list(CENTER)
self.set_random_velocity()
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 实例化大圆和小球
big_circle = BigCircle(center=CENTER, radius=BIG_CIRCLE_RADIUS)
small_circle = SmallCircle()
# 主循环
running = True
while running:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新大圆缺口
big_circle.update(dt)
# 更新小球位置
small_circle.update(big_circle)
# 填充背景
screen.fill(WHITE)
# 画大圆及其缺口
big_circle.draw(screen)
# 画小球
small_circle.draw(screen)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
管理多个大圆和小球
引入多个大圆和小球。每个大圆将有不同的半径、旋转速度和缺口宽度,而小球也可以有多个实例。
创建多个大圆
使用循环创建多个 BigCircle 实例,每个大圆的半径逐渐增大,缺口宽度和旋转速度随机分配。 将大圆按半径从大到小排序,确保在绘制时较大的大圆先绘制,避免被较小的大圆覆盖。
设置最大半径和最小半径
MIN_RADIUS_INIT = 30
MAX_RADIUS = 400
创建多个大圆
# 创建多个大圆实例
num_big_circles = 3 # 创建3个大圆
big_circles = []
radius_increment = (MAX_RADIUS - MIN_RADIUS_INIT) / num_big_circles
current_radius = MIN_RADIUS_INIT
for _ in range(num_big_circles):
current_radius += radius_increment
angular_speed = random.uniform(15, 45) # 15到45度每秒
notch_width_deg = random.uniform(20, 30) # 缺口宽度在20到30度之间
big_circle = BigCircle(center=CENTER, radius=current_radius, notch_width_deg=notch_width_deg, angular_speed_deg_per_sec=angular_speed)
big_circles.append(big_circle)
# 按照半径从大到小排序,以确保较大的大圆先绘制
big_circles.sort(key=lambda bc: bc.radius, reverse=True)
创建多个小球
使用列表推导创建多个 SmallCircle 实例,颜色随机选择。
num_circles = 2 # 创建2个小球
colors = [RED, BLUE, GREEN]
small_circles = [
SmallCircle(radius=10, color=random.choice(colors), speed=4)
for _ in range(num_circles)
]
更新与绘制
在主循环中,更新所有大圆和小球的位置。 绘制所有大圆和小球到屏幕上。
running = True
while running:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新所有大圆缺口
for big_circle in big_circles:
big_circle.update(dt)
# 更新所有小球位置
for circle in small_circles:
circle.update(big_circles)
# 填充背景
screen.fill(WHITE)
# 画所有大圆及其缺口(从大到小绘制,以避免覆盖问题)
for big_circle in big_circles:
big_circle.draw(screen)
# 画所有小球
for circle in small_circles:
circle.draw(screen)
# 更新显示
pygame.display.flip()
整体代码
import pygame
import sys
import math
import random
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
MIN_RADIUS_INIT = 30
MAX_RADIUS = 400
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 工具函数
def distance(p1, p2):
"""计算两点之间的距离"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def normalize(vec):
"""归一化一个向量"""
mag = math.hypot(vec[0], vec[1])
if mag == 0:
return [0, 0]
return [vec[0] / mag, vec[1] / mag]
# 大圆类
class BigCircle:
def __init__(self, center, radius, notch_width_deg=30, angular_speed_deg_per_sec=30):
self.center = center
self.radius = radius
self.notch_width = math.radians(notch_width_deg) # 缺口宽度,转换为弧度
self.angular_speed = math.radians(angular_speed_deg_per_sec) # 旋转速度,弧度/秒
self.current_angle = 0 # 当前缺口角度
def update(self, dt):
"""更新缺口的角度"""
self.current_angle += self.angular_speed * dt
self.current_angle %= 2 * math.pi # 保持角度在0到2π之间
def is_within_notch(self, angle):
"""判断给定角度是否在缺口区域内"""
angle = angle % (2 * math.pi)
start_angle = self.current_angle
end_angle = (self.current_angle + self.notch_width) % (2 * math.pi)
if start_angle < end_angle:
return start_angle <= angle <= end_angle
else:
# 缺口跨越了0度
return angle >= start_angle or angle <= end_angle
def draw(self, surface):
# 画大圆
pygame.draw.circle(surface, BLACK, self.center, self.radius, 2)
# 绘制缺口区域(用背景色填充)
notch_points = []
num_points = 30 # 缺口的平滑程度
outer_radius = self.radius
inner_radius = self.radius - 10 # 缺口的厚度
# 计算缺口的外环点
for i in range(num_points + 1):
angle = self.current_angle + (self.notch_width * i) / num_points
x = self.center[0] + outer_radius * math.cos(angle)
y = self.center[1] + outer_radius * math.sin(angle)
notch_points.append((x, y))
# 计算缺口的内环点
for i in range(num_points + 1):
angle = self.current_angle + self.notch_width - (self.notch_width * i) / num_points
x = self.center[0] + inner_radius * math.cos(angle)
y = self.center[1] + inner_radius * math.sin(angle)
notch_points.append((x, y))
# 绘制缺口区域
pygame.draw.polygon(surface, WHITE, notch_points)
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self):
"""为小球设置一个随机的速度方向"""
angle = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(angle),
self.speed * math.sin(angle)
]
def update(self, big_circles):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, self.center())
# 计算小球相对于大圆中心的角度
angle = math.atan2(self.pos[1] - self.center()[1], self.pos[0] - self.center()[0])
# 按照大圆半径从大到小排序
sorted_big_circles = sorted(big_circles, key=lambda bc: bc.radius, reverse=True)
for big_circle in sorted_big_circles:
if dist + self.radius >= big_circle.radius:
if big_circle.is_within_notch(angle):
# 通过缺口,重置小球位置
self.reset_position()
big_circles.remove(big_circle)
else:
# 反弹
self.bounce(angle)
break # 处理完一个大圆后跳出循环
def center(self):
"""返回大圆的中心位置"""
return CENTER
def bounce(self, angle):
"""反弹小球的速度方向"""
# 反射角度
reflected_angle = angle + math.pi
self.vel[0] = self.speed * math.cos(reflected_angle)
self.vel[1] = self.speed * math.sin(reflected_angle)
def reset_position(self):
"""重置小球到大圆中心,并设置新方向"""
self.pos = list(CENTER)
self.set_random_velocity()
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 创建多个大圆实例
num_big_circles = 3 # 创建3个大圆
big_circles = []
radius_increment = (MAX_RADIUS - MIN_RADIUS_INIT) / num_big_circles
current_radius = MIN_RADIUS_INIT
for _ in range(num_big_circles):
current_radius += radius_increment
angular_speed = random.uniform(15, 45) # 15到45度每秒
notch_width_deg = random.uniform(20, 30) # 缺口宽度在20到30度之间
big_circle = BigCircle(center=CENTER, radius=current_radius, notch_width_deg=notch_width_deg, angular_speed_deg_per_sec=angular_speed)
big_circles.append(big_circle)
# 按照半径从大到小排序,以确保较大的大圆先绘制
big_circles.sort(key=lambda bc: bc.radius, reverse=True)
# 创建多个小球
num_circles = 2 # 创建2个小球
colors = [RED, BLUE, GREEN]
small_circles = [
SmallCircle(radius=10, color=random.choice(colors), speed=4)
for _ in range(num_circles)
]
# 主循环
running = True
while running:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新所有大圆缺口
for big_circle in big_circles:
big_circle.update(dt)
# 更新所有小球位置
for circle in small_circles:
circle.update(big_circles)
# 填充背景
screen.fill(WHITE)
# 画所有大圆及其缺口(从大到小绘制,以避免覆盖问题)
for big_circle in big_circles:
big_circle.draw(screen)
# 画所有小球
for circle in small_circles:
circle.draw(screen)
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()
添加计时器与游戏状态
通过小球的移动清除所有大圆。游戏将根据时间和大圆的数量来判断胜利或失败。
字体设置
加载“微软雅黑”字体,如果不可用则使用默认字体,并在控制台打印提示信息。
# 字体设置
pygame.font.init()
try:
font = pygame.font.SysFont("Microsoft YaHei", 36)
large_font = pygame.font.SysFont("Microsoft YaHei", 72)
except:
# 如果微软雅黑字体不可用,使用默认字体
font = pygame.font.SysFont(None, 36)
large_font = pygame.font.SysFont(None, 72)
print("微软雅黑字体不可用,已使用默认字体。")
计时器
TOTAL_TIME:设定游戏的总时长为100秒。
remaining_time:跟踪剩余时间。
# 计时器设置 TOTAL_TIME = 100 # 总时间为100秒 remaining_time = TOTAL_TIME
主循环更新
游戏状态
state 变量用于管理游戏的不同状态,包括运行中 ("running")、胜利 ("victory") 和失败 ("failure")。
状态检测
如果游戏状态为 "running",则更新计时器、所有大圆和小球的位置。
检查计时器是否到达0,若是则将状态设置为 "failure"。
检查是否所有大圆都被移除,若是则将状态设置为 "victory"。
绘制与显示
运行中:绘制所有大圆、小球和剩余时间。
胜利:显示“胜利!”的文字,并提示用户按任意键退出。
失败:显示“失败!”的文字,并提示用户按任意键退出。
while True:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if state == "running":
# 更新计时器
remaining_time -= dt
if remaining_time <= 0:
remaining_time = 0
state = "failure"
# 更新所有大圆
for big_circle in big_circles:
big_circle.update(dt)
# 更新所有小球
for circle in small_circles:
circle.update(big_circles)
# 检查是否所有大圆都被移除
if len(big_circles) == 0:
state = "victory"
# 填充背景
screen.fill(WHITE)
if state == "running":
# 画所有大圆及其缺口(从大到小绘制,以避免覆盖问题)
for big_circle in big_circles:
big_circle.draw(screen)
# 画所有小球
for circle in small_circles:
circle.draw(screen)
# 绘制计时器
timer_text = font.render(f"剩余时间: {int(remaining_time)}s", True, BLACK)
screen.blit(timer_text, (10, 10))
elif state == "victory":
# 绘制胜利画面
victory_text = large_font.render("胜利!", True, GREEN)
text_rect = victory_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(victory_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
elif state == "failure":
# 绘制失败画面
failure_text = large_font.render("失败!", True, RED)
text_rect = failure_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(failure_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
# 如果游戏结束(胜利或失败),等待用户按键退出
if state in ["victory", "failure"]:
keys = pygame.key.get_pressed()
if any(keys):
pygame.quit()
sys.exit()
# 更新显示
pygame.display.flip()
整体代码
import pygame
import sys
import math
import random
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
GRAY = (200, 200, 200)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
MIN_RADIUS_INIT = 30
MAX_RADIUS = 400
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 字体设置
pygame.font.init()
try:
font = pygame.font.SysFont("Microsoft YaHei", 36)
large_font = pygame.font.SysFont("Microsoft YaHei", 72)
except:
# 如果微软雅黑字体不可用,使用默认字体
font = pygame.font.SysFont(None, 36)
large_font = pygame.font.SysFont(None, 72)
print("微软雅黑字体不可用,已使用默认字体。")
# 工具函数
def distance(p1, p2):
"""计算两点之间的距离"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def normalize(vec):
"""归一化一个向量"""
mag = math.hypot(vec[0], vec[1])
if mag == 0:
return [0, 0]
return [vec[0] / mag, vec[1] / mag]
# 大圆类
class BigCircle:
def __init__(self, center, radius, notch_width_deg=30, angular_speed_deg_per_sec=30):
self.center = center
self.radius = radius
self.notch_width = math.radians(notch_width_deg) # 缺口宽度,转换为弧度
self.angular_speed = math.radians(angular_speed_deg_per_sec) # 旋转速度,弧度/秒
self.current_angle = 0 # 当前缺口角度
def update(self, dt):
"""更新缺口的角度"""
self.current_angle += self.angular_speed * dt
self.current_angle %= 2 * math.pi # 保持角度在0到2π之间
def is_within_notch(self, angle):
"""判断给定角度是否在缺口区域内"""
angle = angle % (2 * math.pi)
start_angle = self.current_angle
end_angle = (self.current_angle + self.notch_width) % (2 * math.pi)
if start_angle < end_angle:
return start_angle <= angle <= end_angle
else:
# 缺口跨越了0度
return angle >= start_angle or angle <= end_angle
def draw(self, surface):
# 画大圆
pygame.draw.circle(surface, BLACK, self.center, self.radius, 2)
# 绘制缺口区域(用背景色填充)
notch_points = []
num_points = 30 # 缺口的平滑程度
outer_radius = self.radius
inner_radius = self.radius - 10 # 缺口的厚度
# 计算缺口的外环点
for i in range(num_points + 1):
angle = self.current_angle + (self.notch_width * i) / num_points
x = self.center[0] + outer_radius * math.cos(angle)
y = self.center[1] + outer_radius * math.sin(angle)
notch_points.append((x, y))
# 计算缺口的内环点
for i in range(num_points + 1):
angle = self.current_angle + self.notch_width - (self.notch_width * i) / num_points
x = self.center[0] + inner_radius * math.cos(angle)
y = self.center[1] + inner_radius * math.sin(angle)
notch_points.append((x, y))
# 绘制缺口区域
pygame.draw.polygon(surface, WHITE, notch_points)
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self, incoming_angle=None, spread_deg=90):
"""
为小球设置一个新的速度方向。
如果传入了 `incoming_angle`,则新方向为 `incoming_angle + π ± spread_deg`。
否则,设置为完全随机方向。
"""
if incoming_angle is not None:
# 计算反方向,并在其基础上随机偏离 ±spread_deg
spread_rad = math.radians(spread_deg)
min_angle = incoming_angle + math.pi - spread_rad
max_angle = incoming_angle + math.pi + spread_rad
min_angle %= 2 * math.pi
max_angle %= 2 * math.pi
if min_angle < max_angle:
direction = random.uniform(min_angle, max_angle)
else:
# 角度范围跨越了0度
direction = random.uniform(min_angle, 2 * math.pi) if random.random() < (
(2 * math.pi - min_angle) / (2 * math.pi - min_angle + max_angle)) else random.uniform(0, max_angle)
else:
# 完全随机方向
direction = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(direction),
self.speed * math.sin(direction)
]
def update(self, big_circles):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, self.center())
# 计算小球相对于大圆中心的角度
angle = math.atan2(self.pos[1] - self.center()[1], self.pos[0] - self.center()[0])
# 按照大圆半径从大到小排序
sorted_big_circles = sorted(big_circles, key=lambda bc: bc.radius, reverse=True)
for big_circle in sorted_big_circles:
if dist + self.radius >= big_circle.radius:
if big_circle.is_within_notch(angle):
# 通过缺口,重置小球位置
self.reset_position()
else:
# 处理小球与大圆的碰撞反弹(方向随机)
incoming_angle = math.atan2(self.vel[1], self.vel[0])
self.set_random_velocity(incoming_angle, spread_deg=15)
# 调整位置避免卡入边界
overlap = dist + self.radius - big_circle.radius
normal = normalize([self.pos[0] - self.center()[0], self.pos[1] - self.center()[1]])
self.pos[0] -= overlap * normal[0]
self.pos[1] -= overlap * normal[1]
break # 处理完一个大圆后跳出循环
def center(self):
"""返回大圆的中心位置"""
return CENTER
def reset_position(self):
"""重置小球到大圆中心,并设置新方向"""
self.pos = list(CENTER)
self.set_random_velocity()
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 创建多个大圆实例
num_big_circles = 3 # 创建3个大圆
big_circles = []
radius_increment = (MAX_RADIUS - MIN_RADIUS_INIT) / num_big_circles
current_radius = MIN_RADIUS_INIT
for _ in range(num_big_circles):
current_radius += radius_increment
angular_speed = random.uniform(15, 45) # 15到45度每秒
notch_width_deg = random.uniform(20, 30) # 缺口宽度在20到30度之间
big_circle = BigCircle(center=CENTER, radius=current_radius, notch_width_deg=notch_width_deg, angular_speed_deg_per_sec=angular_speed)
big_circles.append(big_circle)
# 按照半径从大到小排序,以确保较大的大圆先绘制
big_circles.sort(key=lambda bc: bc.radius, reverse=True)
# 创建多个小球
num_circles = 2 # 创建2个小球
colors = [RED, BLUE, GREEN]
small_circles = [
SmallCircle(radius=10, color=random.choice(colors), speed=4)
for _ in range(num_circles)
]
# 计时器设置
TOTAL_TIME = 100 # 总时间为100秒
remaining_time = TOTAL_TIME
# 游戏状态
state = "running" # 可以是 "running", "victory", "failure"
# 主循环
while True:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if state == "running":
# 更新计时器
remaining_time -= dt
if remaining_time <= 0:
remaining_time = 0
state = "failure"
# 更新所有大圆
for big_circle in big_circles:
big_circle.update(dt)
# 更新所有小球
for circle in small_circles:
circle.update(big_circles)
# 检查是否所有大圆都被移除
if len(big_circles) == 0:
state = "victory"
# 填充背景
screen.fill(WHITE)
if state == "running":
# 画所有大圆及其缺口(从大到小绘制,以避免覆盖问题)
for big_circle in big_circles:
big_circle.draw(screen)
# 画所有小球
for circle in small_circles:
circle.draw(screen)
# 绘制计时器
timer_text = font.render(f"剩余时间: {int(remaining_time)}s", True, BLACK)
screen.blit(timer_text, (10, 10))
elif state == "victory":
# 绘制胜利画面
victory_text = large_font.render("胜利!", True, GREEN)
text_rect = victory_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(victory_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
elif state == "failure":
# 绘制失败画面
failure_text = large_font.render("失败!", True, RED)
text_rect = failure_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(failure_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
# 如果游戏结束(胜利或失败),等待用户按键退出
if state in ["victory", "failure"]:
keys = pygame.key.get_pressed()
if any(keys):
pygame.quit()
sys.exit()
# 更新显示
pygame.display.flip()
最终代码
import pygame
import sys
import math
import random
# 初始化 Pygame
pygame.init()
# 设置屏幕尺寸
WIDTH, HEIGHT = 1200, 900
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("小球逃脱")
# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
GRAY = (200, 200, 200)
# 定义大圆的参数
CENTER = (WIDTH // 2, HEIGHT // 2)
MIN_RADIUS_INIT = 30
MAX_RADIUS = 400
# 时钟控制帧率
clock = pygame.time.Clock()
FPS = 60
# 字体设置
pygame.font.init()
try:
font = pygame.font.SysFont("Microsoft YaHei", 36)
large_font = pygame.font.SysFont("Microsoft YaHei", 72)
except:
# 如果微软雅黑字体不可用,使用默认字体
font = pygame.font.SysFont(None, 36)
large_font = pygame.font.SysFont(None, 72)
print("微软雅黑字体不可用,已使用默认字体。")
# 工具函数
def distance(p1, p2):
"""计算两点之间的距离"""
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def normalize(vec):
"""归一化一个向量"""
mag = math.hypot(vec[0], vec[1])
if mag == 0:
return [0, 0]
return [vec[0] / mag, vec[1] / mag]
# 大圆类
class BigCircle:
def __init__(self, center, radius, notch_width_deg=30, angular_speed_deg_per_sec=30):
self.center = center
self.radius = radius
self.notch_width = math.radians(notch_width_deg) # 缺口宽度,转换为弧度
self.angular_speed = math.radians(angular_speed_deg_per_sec) # 旋转速度,弧度/秒
self.current_angle = 0 # 当前缺口角度
def update(self, dt):
"""更新缺口的角度"""
self.current_angle += self.angular_speed * dt
self.current_angle %= 2 * math.pi # 保持角度在0到2π之间
def is_within_notch(self, angle):
"""判断给定角度是否在缺口区域内"""
angle = angle % (2 * math.pi)
start_angle = self.current_angle
end_angle = (self.current_angle + self.notch_width) % (2 * math.pi)
if start_angle < end_angle:
return start_angle <= angle <= end_angle
else:
# 缺口跨越了0度
return angle >= start_angle or angle <= end_angle
def draw(self, surface):
# 画大圆
pygame.draw.circle(surface, BLACK, self.center, self.radius, 2)
# 绘制缺口区域(用背景色填充)
notch_points = []
num_points = 30 # 缺口的平滑程度
outer_radius = self.radius
inner_radius = self.radius - 10 # 缺口的厚度
# 计算缺口的外环点
for i in range(num_points + 1):
angle = self.current_angle + (self.notch_width * i) / num_points
x = self.center[0] + outer_radius * math.cos(angle)
y = self.center[1] + outer_radius * math.sin(angle)
notch_points.append((x, y))
# 计算缺口的内环点
for i in range(num_points + 1):
angle = self.current_angle + self.notch_width - (self.notch_width * i) / num_points
x = self.center[0] + inner_radius * math.cos(angle)
y = self.center[1] + inner_radius * math.sin(angle)
notch_points.append((x, y))
# 绘制缺口区域
pygame.draw.polygon(surface, WHITE, notch_points)
# 小球类
class SmallCircle:
def __init__(self, radius=10, color=RED, speed=4):
self.radius = radius
self.color = color
self.speed = speed # 固定速度大小
self.pos = list(CENTER) # 初始位置在大圆中心
self.set_random_velocity()
def set_random_velocity(self, incoming_angle=None, spread_deg=90):
"""
为小球设置一个新的速度方向。
如果传入了 `incoming_angle`,则新方向为 `incoming_angle + π ± spread_deg`。
否则,设置为完全随机方向。
"""
if incoming_angle is not None:
# 计算反方向,并在其基础上随机偏离 ±spread_deg
spread_rad = math.radians(spread_deg)
min_angle = incoming_angle + math.pi - spread_rad
max_angle = incoming_angle + math.pi + spread_rad
min_angle %= 2 * math.pi
max_angle %= 2 * math.pi
if min_angle < max_angle:
direction = random.uniform(min_angle, max_angle)
else:
# 角度范围跨越了0度
direction = random.uniform(min_angle, 2 * math.pi) if random.random() < (
(2 * math.pi - min_angle) / (2 * math.pi - min_angle + max_angle)) else random.uniform(0, max_angle)
else:
# 完全随机方向
direction = random.uniform(0, 2 * math.pi)
self.vel = [
self.speed * math.cos(direction),
self.speed * math.sin(direction)
]
def update(self, big_circles):
"""更新小球的位置,并处理与大圆的碰撞"""
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
# 计算与大圆中心的距离
dist = distance(self.pos, self.center())
# 计算小球相对于大圆中心的角度
angle = math.atan2(self.pos[1] - self.center()[1], self.pos[0] - self.center()[0])
# 按照大圆半径从大到小排序
sorted_big_circles = sorted(big_circles, key=lambda bc: bc.radius, reverse=True)
for big_circle in sorted_big_circles:
if dist + self.radius >= big_circle.radius:
if big_circle.is_within_notch(angle):
# 通过缺口,重置小球位置
self.reset_position()
big_circles.remove(big_circle)
else:
# 处理小球与大圆的碰撞反弹(方向随机)
incoming_angle = math.atan2(self.vel[1], self.vel[0])
self.set_random_velocity(incoming_angle, spread_deg=15)
# 调整位置避免卡入边界
overlap = dist + self.radius - big_circle.radius
normal = normalize([self.pos[0] - self.center()[0], self.pos[1] - self.center()[1]])
self.pos[0] -= overlap * normal[0]
self.pos[1] -= overlap * normal[1]
break # 处理完一个大圆后跳出循环
def center(self):
"""返回大圆的中心位置"""
return CENTER
def reset_position(self):
"""重置小球到大圆中心,并设置新方向"""
self.pos = list(CENTER)
self.set_random_velocity()
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
# 创建多个大圆实例
num_big_circles = 3 # 创建3个大圆
big_circles = []
radius_increment = (MAX_RADIUS - MIN_RADIUS_INIT) / num_big_circles
current_radius = MIN_RADIUS_INIT
for _ in range(num_big_circles):
current_radius += radius_increment
angular_speed = random.uniform(15, 45) # 15到45度每秒
notch_width_deg = random.uniform(20, 30) # 缺口宽度在20到30度之间
big_circle = BigCircle(center=CENTER, radius=current_radius, notch_width_deg=notch_width_deg, angular_speed_deg_per_sec=angular_speed)
big_circles.append(big_circle)
# 按照半径从大到小排序,以确保较大的大圆先绘制
big_circles.sort(key=lambda bc: bc.radius, reverse=True)
# 创建多个小球
num_circles = 2 # 创建2个小球
colors = [RED, BLUE, GREEN]
small_circles = [
SmallCircle(radius=10, color=random.choice(colors), speed=4)
for _ in range(num_circles)
]
# 计时器设置
TOTAL_TIME = 100 # 总时间为100秒
remaining_time = TOTAL_TIME
# 游戏状态
state = "running" # 可以是 "running", "victory", "failure"
# 代码延续自上一阶段
# 主循环
while True:
dt = clock.tick(FPS) / 1000 # 获取时间增量(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if state == "running":
# 更新计时器
remaining_time -= dt
if remaining_time <= 0:
remaining_time = 0
state = "failure"
# 更新所有大圆
for big_circle in big_circles:
big_circle.update(dt)
# 更新所有小球
for circle in small_circles:
circle.update(big_circles)
# 检查是否所有大圆都被移除
if len(big_circles) == 0:
state = "victory"
# 填充背景
screen.fill(WHITE)
if state == "running":
# 画所有大圆及其缺口(从大到小绘制,以避免覆盖问题)
for big_circle in big_circles:
big_circle.draw(screen)
# 画所有小球
for circle in small_circles:
circle.draw(screen)
# 绘制计时器
timer_text = font.render(f"剩余时间: {int(remaining_time)}s", True, BLACK)
screen.blit(timer_text, (10, 10))
elif state == "victory":
# 绘制胜利画面
victory_text = large_font.render("胜利!", True, GREEN)
text_rect = victory_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(victory_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
elif state == "failure":
# 绘制失败画面
failure_text = large_font.render("失败!", True, RED)
text_rect = failure_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
screen.blit(failure_text, text_rect)
# 提示用户按下任意键退出
prompt_text = font.render("按任意键退出", True, BLACK)
prompt_rect = prompt_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(prompt_text, prompt_rect)
# 如果游戏结束(胜利或失败),等待用户按键退出
if state in ["victory", "failure"]:
keys = pygame.key.get_pressed()
if any(keys):
pygame.quit()
sys.exit()
# 更新显示
pygame.display.flip()