You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyGame移动圆形鼠标停留时长统计异常及命中识别问题

鼠标在移动圆形区域内停留时长统计问题

我想统计鼠标在移动圆形区域内的停留时长,当前逻辑是记录鼠标离开圆形的时间,与上次离开时间(首次则与开始时间)做差累加,但最终结果始终接近程序运行总时长(10秒)。另外,注释掉move_circle函数使用静止圆形时,无法识别任何命中。

原代码

import pygame
import time
import random
import math
from time import perf_counter_ns as timer

pygame.init()
  
# Define the background colour
# using RGB color coding.
black = (0, 0, 0)
red = (255, 10, 75)
  
# Define the dimensions of
# screen object(width,height)
screenx = 1200
screeny = 1000
screen = pygame.display.set_mode((screenx, screeny))
  
# Set the caption of the screen
pygame.display.set_caption('Geeksforgeeks')
  
# Fill the background colour to the screen
screen.fill(black)
  
# Update the display using flip
pygame.display.flip()
  
# Variable to keep our game loop running
running = True

end = 10
nano = 1000000000

def clear_screen():
    screen.fill(black)
    pygame.display.flip()

class circle():
    def __init__(self, radius):
        self.velocity = random.randint(1, 10)
        self.theta = random.randint(0, 360)
        self.radius = radius
        self.alive = False
        
    def draw_circle(self, radius):
        self.x = random.randint(0, screenx)
        self.y = random.randint(0, screeny)
        pygame.draw.circle(screen, red, (random.randint(0, 1200), random.randint(0, 1200)), radius)
        pygame.display.update()
        self.alive = True

    def move_circle(self):
        clear_screen()
        self.x += self.velocity * math.cos(self.theta)
        self.y += self.velocity * math.sin(self.theta)
        pygame.draw.circle(screen, red, (self.x, self.y), self.radius)
        pygame.display.update()
        time.sleep(0.1)

    def change_velocity(self):
        self.velocity = random.randint(1, 1)
        self.theta = random.randint(0, 360)

    def check_collision(self):
        if (self.x - self.radius) < 0 or (self.x + self.radius) > screenx:
            self.x = random.randint(0, screenx)
        if (self.y - self.radius) < 0 or (self.y + self.radius) > screeny:
            self.y = random.randint(0, screeny)

    def mouse_in_red(self, mouse_x, mouse_y):
        if (mouse_x - self.x)**2 + (mouse_y - self.y)**2 < self.radius**2:
            return True
        else:
            return False

target = circle(25)
# game loop
start = timer()
crnt_time = start
times = [start]
target.draw_circle(25)
while running:
    if target.alive == False:
        target.draw_circle(25)
        time.sleep(1)
    else:
        if random.randint(0, 100) == 1:
            target.change_velocity()
        target.move_circle()
        target.check_collision()
        if target.mouse_in_red(pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]):
            print('hit')
            continue
        else:
            print(f'miss',{(timer() - crnt_time)/nano})
            times.append(timer() - crnt_time)
            crnt_time = timer()
    if (timer() - start)/nano > end:
        running = False
        print(f'total time spent on target(out of ',{end},'seconds):', {(sum(times[1:]))/nano})
        print(f'average time spent on target:', {(sum(times[1:])/len(times[1:]))/nano})
        #for i in range(len(times[1:])):
        #    print(f'time spent on target {i}:', {(times[i])/nano})
    # for loop through the event queue  
    for event in pygame.event.get():
    
        # Check for QUIT event      
        if event.type == pygame.QUIT:
            running = False

原输出结果

total time spent on target(out of  {10} seconds): {10.0042299}
average time spent on target: {0.22736886136363638}

问题根源

  • 统计逻辑完全颠倒:当前代码累加的是鼠标不在圆形内的时间,却误当成停留时长,结果自然接近总运行时间。
  • 静止圆形命中失效:draw_circle方法中,先给self.x/self.y赋值随机数,但绘制时又用了新的随机坐标,导致鼠标命中判断的位置和屏幕实际显示的圆形位置完全不一致。
  • 其他隐性问题:
    • 传入math.cos/math.sin的是角度而非弧度,导致圆形移动方向错误。
    • time.sleep(0.1)阻塞游戏循环,影响时间统计准确性和交互流畅度。
    • 碰撞检测直接重置坐标,不符合物理移动逻辑。

修复后的代码

import pygame
import random
import math
from time import perf_counter_ns as timer

pygame.init()

black = (0, 0, 0)
red = (255, 10, 75)

screenx = 1200
screeny = 1000
screen = pygame.display.set_mode((screenx, screeny))
pygame.display.set_caption('Geeksforgeeks')
screen.fill(black)
pygame.display.flip()

running = True
end = 10
nano = 1000000000

def clear_screen():
    screen.fill(black)
    pygame.display.flip()

class circle():
    def __init__(self, radius):
        self.velocity = random.randint(1, 10)
        # 初始角度转为弧度
        self.theta = math.radians(random.randint(0, 360))
        self.radius = radius
        self.alive = False
        
    def draw_circle(self, radius):
        self.x = random.randint(radius, screenx - radius)
        self.y = random.randint(radius, screeny - radius)
        # 用self.x/self.y绘制,保证显示与判断位置一致
        pygame.draw.circle(screen, red, (self.x, self.y), radius)
        pygame.display.update()
        self.alive = True

    def move_circle(self):
        clear_screen()
        self.x += self.velocity * math.cos(self.theta)
        self.y += self.velocity * math.sin(self.theta)
        pygame.draw.circle(screen, red, (self.x, self.y), self.radius)
        pygame.display.update()

    def change_velocity(self):
        self.velocity = random.randint(1, 10)
        self.theta = math.radians(random.randint(0, 360))

    def check_collision(self):
        # 碰到边界反弹,而非重置坐标
        if (self.x - self.radius) < 0 or (self.x + self.radius) > screenx:
            self.theta = math.pi - self.theta
        if (self.y - self.radius) < 0 or (self.y + self.radius) > screeny:
            self.theta = -self.theta
        # 确保圆形不超出边界
        self.x = max(self.radius, min(self.x, screenx - self.radius))
        self.y = max(self.radius, min(self.y, screeny - self.radius))

    def mouse_in_red(self, mouse_x, mouse_y):
        return (mouse_x - self.x)**2 + (mouse_y - self.y)**2 < self.radius**2

target = circle(25)
start = timer()
in_circle_start = None
total_in_time = 0

target.draw_circle(25)
clock = pygame.time.Clock()  # 用帧率控制替代sleep

while running:
    clock.tick(60)  # 限制帧率为60fps
    current_time = timer()
    
    if not target.alive:
        target.draw_circle(25)
    else:
        if random.randint(0, 100) == 1:
            target.change_velocity()
        target.move_circle()
        target.check_collision()
        
        mouse_pos = pygame.mouse.get_pos()
        is_in = target.mouse_in_red(mouse_pos[0], mouse_pos[1])
        
        if is_in:
            if in_circle_start is None:
                # 记录进入圆形的时间点
                in_circle_start = current_time
            print('hit')
        else:
            if in_circle_start is not None:
                # 计算停留时长并累加
                duration = current_time - in_circle_start
                total_in_time += duration
                in_circle_start = None
            print(f'miss')
    
    # 检查是否到达结束时间
    if (current_time - start)/nano > end:
        running = False
        # 补上最后一段在圆内的时间(如果未离开)
        if in_circle_start is not None:
            total_in_time += current_time - in_circle_start
        print(f'总停留时长(总运行时长{end}秒): {total_in_time/nano:.3f}秒')
    
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

pygame.quit()

修复说明

  1. 修正统计逻辑:
    • 新增in_circle_start变量记录鼠标进入圆形的时间点
    • 鼠标离开圆形时计算停留时长并累加,程序结束时补全最后一段停留时间
  2. 修复静止圆形命中问题:
    • draw_circle方法中,绘制圆形使用self.x/self.y,保证显示位置与判断位置一致
  3. 其他优化:
    • 将角度转为弧度,修正圆形移动方向
    • 用pygame.time.Clock()替代time.sleep,保证帧率稳定且不阻塞循环
    • 修改碰撞检测为边界反弹,符合物理移动逻辑
    • 限制圆形坐标范围,避免超出屏幕边界

内容的提问来源于stack exchange,提问作者Gulliver Brower

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 14:20:32