如何在Pygame中实现时间与弧长按比例联动的圆形环形倒计时

Pygame 带弧长联动的圆形倒计时实现
其实这个效果的核心难点根本不是手动计算弧长,很多人一开始把问题想复杂了——觉得要先算圆环总周长、再逐帧计算要缩短多少长度,完全没必要。Pygame本身的绘制API已经把这块逻辑封装好了,你只需要把时间占比对应到绘制的角度范围,天然就能保证弧长和剩余时间严格等比例。
核心实现步骤
- 先固定几个全局参数:总倒计时时长、圆环的圆心坐标、圆环的内外半径、弧的颜色、背景色。这里要对齐Pygame的时间单位,统一用毫秒计算,避免单位不统一导致的比例计算错误。
- 搞懂Pygame的
draw.arc()的角度规则:函数接受的角度是弧度值,0弧度对应的位置是屏幕的3点钟方向,角度沿着逆时针方向递增;因为屏幕坐标系Y轴是向下的,实际绘制出来的视觉旋转方向是顺时针。如果要做常见的“从12点钟方向开始顺时针逐步缩短”的圆环,只要把绘制的起始角度偏移-π/2弧度(也就是90度),就能把起点转到正上方。 - 保证弧长和时间的同步逻辑:每帧计算当前剩余时间,除以总时长得到当前的剩余占比,这个占比直接乘以整圆的总弧度
2π,就是当前需要绘制的弧的总跨度。因为整个圆的周长和弧度是完全线性对应的,所以画出来的弧长永远和剩余时间占比完全一致,不会出现视觉和进度脱节的问题。 - 性能优化细节:
- 不要靠
draw.arc()的width参数画很粗的线条,边缘会有明显的锯齿。更顺滑的效果是先画一个大的实心圆当圆环的底色,再叠一个小一圈的和背景同色的实心圆,把中间挖空成环形,再在上面画对应进度的弧。 - 中心的时间数字不要每帧都重新生成字体渲染对象,可以提前初始化字体对象,每次只更新文字内容,把文字块的位置用
get_rect(center=圆心坐标)锚定居中,减少不必要的性能消耗。如果需要更精细的倒计时显示(比如精确到0.1秒),只要调整文字的渲染格式就行,因为弧的计算是基于实时剩余毫秒值的,不管数字显示的是整数秒还是小数秒,视觉进度都会和时间严格同步,不需要额外修改绘制逻辑。
- 不要靠
最小可运行示例代码
import pygame import math pygame.init() screen = pygame.display.set_mode((500, 500)) clock = pygame.time.Clock() # 倒计时基础配置 TOTAL_DURATION = 10 * 1000 # 总时长10秒,单位毫秒 start_timestamp = pygame.time.get_ticks() CENTER_POS = (250, 250) OUTER_RADIUS = 150 INNER_RADIUS = 130 PROGRESS_COLOR = (0, 200, 255) BASE_RING_COLOR = (60, 60, 60) BG_COLOR = (30, 30, 30) running = True while running: screen.fill(BG_COLOR) # 计算实时剩余时间 elapsed = pygame.time.get_ticks() - start_timestamp remaining = max(0, TOTAL_DURATION - elapsed) progress_ratio = remaining / TOTAL_DURATION # 绘制底层完整环形 pygame.draw.circle(screen, BASE_RING_COLOR, CENTER_POS, OUTER_RADIUS) pygame.draw.circle(screen, BG_COLOR, CENTER_POS, INNER_RADIUS) # 绘制对应进度的弧 if remaining > 0: start_angle = -math.pi / 2 # 起始点对齐12点钟方向 end_angle = start_angle + progress_ratio * 2 * math.pi arc_rect = pygame.Rect( CENTER_POS[0] - OUTER_RADIUS, CENTER_POS[1] - OUTER_RADIUS, OUTER_RADIUS * 2, OUTER_RADIUS * 2 ) pygame.draw.arc( screen, PROGRESS_COLOR, arc_rect, start_angle, end_angle, width=OUTER_RADIUS - INNER_RADIUS ) # 绘制中心时间数字 font = pygame.font.SysFont(None, 60) time_surface = font.render(f"{int(remaining / 1000)}", True, (255, 255, 255)) time_rect = time_surface.get_rect(center=CENTER_POS) screen.blit(time_surface, time_rect) # 事件处理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.display.flip() clock.tick(60) pygame.quit()
常见踩坑点
- 很多人第一次写的时候会把角度的方向搞反,最后画出来的弧是逆时针缩短的,只要记住把起始角度偏移
-π/2,就符合常规的倒计时视觉习惯,不需要额外做坐标翻转。 - 计算时间占比的时候一定要统一单位,比如全程用毫秒就不要中途换成秒,不然很容易出现进度比例算错,弧长和时间对不上的问题。
- 倒计时结束的时候要把剩余时间的最小值卡到0,不然时间流逝超过总时长后,计算出来的占比会变成负数,导致绘制的弧角度异常。
内容的提问来源于stack exchange,提问作者Salem
相关产品推荐
相关产品推荐

