在 PYGAME 中创建一个反应时间测试游戏

问题描述

我正在尝试使用 pygame for gui 在 python 中创建一个反应时间测试游戏。因为,我对这项技术相当陌生,所以我被困在代码的一部分上,即如何注册更多按键,然后相应地记录时间。

这是我目前的代码

import pygame
from datetime import datetime
import time
import random
pygame.init()

screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("Reaction Time Test")

font = pygame.font.SysFont(None,30)

text = font.render("PRESS ANY KEY TO START TEST",(255,255,255))

w = font.render("PRESS ANY KEY",(0,0))
count = 0
screen.blit(text,(150,240))
running = True
while running:
    pygame.display.flip()
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            pygame.quit()
        
        if count <= 5 and event.type == pygame.KEYDOWN:
             
                screen.fill(pygame.Color("black"))
                pygame.display.flip()
                wait_time = random.randint(1,4)
                time.sleep(wait_time)
                reaction_start = datetime.Now()
                print(reaction_start)
                screen.blit(w,(225,200))
                count = count + 1
                
                if event.type == pygame.KEYDOWN:
                    reaction_end = datetime.Now()
                    print(reaction_end)
                    t = reaction_end - reaction_start
                    print(t)
                    f = font.render("REACTION TIME: "+ str(t),255))
                    screen.blit(f,300))

             
        if count > 5:
            screen.fill(pygame.Color("black"))
            pygame.display.flip()
            s = font.render("AVERAGE REACTION TIME IS: ",255))
            screen.blit(s,200))
            pygame.display.flip()
                

我坚持的部分是这个代码片段

if count <= 5 and event.type == pygame.KEYDOWN:
             
                screen.fill(pygame.Color("black"))
                pygame.display.flip()
                wait_time = random.randint(1,300))

它几乎同时注册reaction_start和reaction_end,不会等待按键。

这当前将语句“PRESS ANY KEY”和“REACTION TIME:”打印在一起,但是当我将 screen.fill(pygame.Color("black") 和 pygame.display.flip() 语句放入时在 screen.blit(f) 之前,它只会显示 REACTION TIME: 而不是“PRESS ANY KEY”

解决方法

在pygame中可以通过调用pygame.time.get_ticks()获取系统时间,它返回自调用pygame.init()以来的毫秒数。

不要试图在应用程序循环中延迟或等待。添加一个 game_state 变量,它可以具有“start”、“wait”和“wait_for_reaction”状态。

在应用程序循环开始时获取当前时间:

while running:
    current_time = pygame.time.get_ticks()

设置按键按下的开始时间:

if event.type == pygame.KEYDOWN:
    if game_state == "start":
        game_state = "wait"
        start_time = current_time + random.randint(1000,4000)

当当前时间大于或等于开始时间时,将game_state形式的“wait”改为“wait_for_reaction”:

if game_state == "wait":
    if current_time >= start_time:
        game_state = "wait_for_reaction" 

计算反应时间并重新启动进程,当再次按下一个键时:

if event.type == pygame.KEYDOWN:
    # [...]
    
    if game_state == "wait_for_reaction": 
        game_state = "wait" 
        reaction_time = (current_time - start_time) / 1000
        start_time = current_time + random.randint(1000,4000)
        count += 1
        average_time = (average_time * (count-1) + reaction_time) / count
        r_surf = font.render(f"REACTION TIME: {reaction_time:.03f}",(255,255,255))
        ar_surf = font.render(f"AVERAGE REACTION TIME IS: {average_time:.03f}",255))

在每一帧中重新绘制场景,具体取决于游戏状态:

while running:
    # [...]

    screen.fill(pygame.Color("black"))
    
    center = screen.get_rect().center
    if game_state == "start":
        screen.blit(text,text.get_rect(center = center))
    if game_state == "wait_for_reaction":
        screen.blit(w,w.get_rect(center = center))
    if r_surf:
        screen.blit(r_surf,r_surf.get_rect(center = (center[0],350)))
    if ar_surf:
        screen.blit(ar_surf,ar_surf.get_rect(center = (center[0],400)))

    pygame.display.flip()

完整示例:

import pygame
import random
pygame.init()

screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("Reaction Time Test")

font = pygame.font.SysFont(None,30)

text = font.render("PRESS ANY KEY TO START TEST",255))
w = font.render("PRESS ANY KEY",(0,0))
r_surf = None
ar_surf = None

game_state = "start"
start_time = 0
average_time = 0

count = 0
running = True
while running:
    current_time = pygame.time.get_ticks()
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            pygame.quit()
        if event.type == pygame.KEYDOWN:
            if game_state == "start":
                game_state = "wait" 
                start_time = current_time + random.randint(1000,4000)
            if game_state == "wait_for_reaction": 
                game_state = "wait" 
                reaction_time = (current_time - start_time) / 1000
                start_time = current_time + random.randint(1000,4000)
                count += 1
                average_time = (average_time * (count-1) + reaction_time) / count
                r_surf = font.render(f"REACTION TIME: {reaction_time:.03f}",255))
                ar_surf = font.render(f"AVERAGE REACTION TIME IS: {average_time:.03f}",255))

    if game_state == "wait":
        if current_time >= start_time:
            game_state = "wait_for_reaction"        

    screen.fill(pygame.Color("black"))
    
    center = screen.get_rect().center
    if game_state == "start":
        screen.blit(text,400)))

    pygame.display.flip()