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

基于Pygame的打字游戏用户输入无响应问题修复请求

问题描述

我正在开发一款打字游戏,通关条件为用户输入指定提示文本且打字速度≥35WPM。先完成了控制台运行版本,后续基于Pygame开发GUI版本,但GUI版本中用户输入文本后,代码未执行文本匹配与通关判断逻辑。我怀疑是if user_text == displayprompt:判断失效,尝试调整缩进、用列表存储字符对比等方法仍未解决,请求修复代码错误。

控制台版本代码
from time import time
from random import randint

def typingErrors(prompt):
    global iwords

    words = prompt.split()
    errors = 0

    for i in range(len(iwords)):
        if i in (0, len(iwords)-1):
            if iwords[i] == words[i]:
                continue
            else:
                errors +=1
        else:
            if iwords[i] == words[i]:
                if (iwords[i+1] == words[i+1]) & (iwords[i-1] == words[i-1]):
                    continue
                else:
                    errors += 1
            else:
                errors += 1
    return errors

def typingSpeed(iprompt, stime, etime):
    global time
    global iwords

    iwords = iprompt.split()
    twords = len(iwords)
    speed = twords / time

    return speed

def timeElapsed(stime, etime):
    time = etime - stime

    return time

def GeneratePrompt():
    with open(r"C:\Users\adarshrsm\Desktop\prompts.txt", 'r') as file:
        prompts=file.readlines()
        for i in prompts:
            if len(i)>=10 and len(i)<30:
                try:
                    index = randint(0,(len(prompts)))
                    finalprompt=prompts[index]
                except IndexError:
                    GeneratePrompt()
    return finalprompt


if __name__ == '__main__':
    #prompt = input("Enter Prompt: ")
    prompt=GeneratePrompt()
    print("Type this to open the chest:- '", prompt, "'")
    input("press ENTER when you are ready!")
    stime = time()
    iprompt = input()
    etime = time()
    time = round(timeElapsed(stime, etime), 2)
    speed = typingSpeed(iprompt, stime, etime)
    errors = typingErrors(prompt)
    accuracy = ((len(iwords)-errors)/len(iwords))*100
    wpm = round(speed,2)*60

    if accuracy == 100 and wpm >= 35:
        print("You have passed")
    else:
        print("You have failed")              
GUI版本原始代码
import pygame
from sys import exit
from time import time
from random import randint
#import RPGtext

#--------------------------Functions--------------------------------

def GeneratePrompt():
    with open(r"C:\Users\adarshrsm\Desktop\prompts.txt", 'r') as file:
        prompts=file.readlines()
        for i in prompts:
            if len(i)>=10 and len(i)<25:
                try:
                    index = randint(0,(len(prompts))+1)
                    finalprompt=prompts[index]
                except IndexError or UnboundLocalError:
                    GeneratePrompt()
    return finalprompt


def typingSpeed(iprompt, stime, etime):
    global time
    global iwords

    iwords = iprompt.split()
    twords = len(iwords)
    speed = twords / time

    return speed

def timeElapsed(stime, etime):
    time = etime - stime

    return time       


#-------------------------GUI and main game loop-----------------------------------

pygame.init()
Win = pygame.display.set_mode((1000,900))
clock = pygame.time.Clock()
chest_left_tilt = pygame.image.load(r"C:\Users\adarshrsm\Downloads\chest_left_tilt.png")
chest_right_tilt = pygame.image.load(r"C:\Users\adarshrsm\Downloads\right tilt.png")
chest_no_tilt = pygame.image.load(r"C:\Users\adarshrsm\Downloads\no tilt.png")
display_textbox = pygame.image.load(r"C:\Users\adarshrsm\Downloads\final textbox.png")
display_textbox.set_alpha(100)
txtf = pygame.font.Font(r"C:\Users\adarshrsm\Downloads\game font.ttf",40)
snip = txtf.render('',True,(0,0,0))
EnterPrompt = txtf.render("Enter this code to open the chest: ",True,(0,0,0))
EnterPrompt.set_alpha(200)
EnterLine = "Press Enter to start"
counter = 0
speed = 3
TextRendered = False
imgtick = 1
displayprompt = GeneratePrompt()
text = txtf.render(displayprompt,True,(0,0,0))
user_text = ''  
GameStatus = True


while GameStatus == True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()                   
    if imgtick%100 == 0:
        Win.blit(chest_left_tilt,(0,0))
        imgtick+=1
    elif imgtick%100 != 0:
        Win.blit(chest_right_tilt,(0,0))
        imgtick+=1
        
    Win.blit(display_textbox,(100,50))    
    Win.blit(EnterPrompt,(150,100))
    
    if counter < speed * len(displayprompt):
        counter+=1
    elif counter >= speed * len(displayprompt):
        done = True
    snip = txtf.render(displayprompt[0:counter//speed],True,(0,0,0))
    EnterLineRender = txtf.render(EnterLine[0:counter//speed],True,(0,0,0))
    EnterLineRender.set_alpha(200)
    Win.blit(snip,(150,150))
    Win.blit(EnterLineRender,(150,200))
    user_text_list = []
    for event in pygame.event.get():    
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_BACKSPACE:
                user_text = user_text[:-1]
            elif event.key == pygame.K_ESCAPE:
                pygame.quit()
                exit()
            elif event.key == pygame.K_RETURN:
                stime = time()
            else:
                user_text += event.unicode
    
    if user_text == displayprompt:
        iprompt = user_text
        etime = time()
        time = round(timeElapsed(stime, etime), 2)
        speed = typingSpeed(iprompt, stime, etime)
        wpm = round(speed,2)*60 
                
        if wpm >=35:
            snip_win = txtf.render('',True,(0,0,0))
            counter_win = 0
            result_win = "You have unlocked the chest ! "
            if counter_win < speed * len(result_win):
                counter_win+=1
            elif counter_win >= speed * len(result_win):
                done = True
                snip_win = txtf.render(result_win[0:counter_win//speed],True,(0,0,0))
                Win.blit(snip_win,(150,350))
            else:
                snip_lose = txtf.render('',True,(0,0,0))
                counter_lose = 0
                result_lose = "You have failed to unlock the chest ! "
                if counter_lose < speed * len(result_lose):
                    counter_lose+=1
                elif counter_lose >= speed * len(result_lose):
                    done = True
                snip_lose = txtf.render(result_lose[0:counter_lose//speed],True,(0,0,0))
                Win.blit(snip_lose,(150,350))                    
                 
    user_text_surface = txtf.render(user_text,True,(0,0,0))        
    Win.blit(user_text_surface, (150,250)) 

    pygame.display.update()
    clock.tick(100)
问题分析与修复代码

核心问题点

  1. 事件队列重复处理:主循环中存在两个pygame.event.get()调用,第一个会取走所有QUIT事件,第二个可能无法获取完整的键盘输入事件,导致stime未正确记录、用户输入不完整。
  2. 提示文本换行符问题:readlines()读取的每行文本末尾包含\n换行符,用户输入不会包含该字符,直接对比永远不相等。
  3. 通关逻辑缩进错误:胜利/失败的判断逻辑缩进错误,导致失败分支永远不会执行;结果文本渲染仅执行一次,无法显示动画效果。
  4. 未初始化计时变量:若用户未按Enter就输入文本,stime未定义会引发报错。
  5. 提示文本生成逻辑缺陷:原函数可能选中不符合长度要求的文本,递归调用未返回结果。

修复后的完整GUI代码

import pygame
from sys import exit
from time import time
from random import randint

#--------------------------Functions--------------------------------
def GeneratePrompt():
    with open(r"C:\Users\adarshrsm\Desktop\prompts.txt", 'r') as file:
        # 先过滤符合长度要求的提示文本,再随机选择
        valid_prompts = [p.rstrip('\n') for p in file.readlines() if 10 <= len(p.strip()) <=25]
        if not valid_prompts:
            raise ValueError("No valid prompts found in the file")
        index = randint(0, len(valid_prompts)-1)
        return valid_prompts[index]

def typingSpeed(iprompt, stime, etime):
    global iwords
    iwords = iprompt.split()
    twords = len(iwords)
    elapsed_time = etime - stime
    # 避免除以0
    if elapsed_time <=0:
        return 0
    speed = twords / elapsed_time
    return speed

#-------------------------GUI and main game loop-----------------------------------
pygame.init()
Win = pygame.display.set_mode((1000,900))
clock = pygame.time.Clock()

# 加载资源(路径请根据实际情况调整)
chest_left_tilt = pygame.image.load(r"C:\Users\adarshrsm\Downloads\chest_left_tilt.png")
chest_right_tilt = pygame.image.load(r"C:\Users\adarshrsm\Downloads\right tilt.png")
display_textbox = pygame.image.load(r"C:\Users\adarshrsm\Downloads\final textbox.png")
display_textbox.set_alpha(100)
txtf = pygame.font.Font(r"C:\Users\adarshrsm\Downloads\game font.ttf",40)

# 初始化变量
EnterPrompt = txtf.render("Enter this code to open the chest: ",True,(0,0,0))
EnterPrompt.set_alpha(200)
EnterLine = "Press Enter to start"
counter = 0
text_speed = 3  # 重命名避免和打字速度变量冲突
imgtick = 1
displayprompt = GeneratePrompt()
user_text = ''  
GameStatus = True
stime = None  # 初始化计时开始时间
game_result = None  # 存储游戏结果:"win"或"lose"
result_counter = 0
result_text = ""

while GameStatus == True:
    # 统一处理所有事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_BACKSPACE:
                user_text = user_text[:-1]
            elif event.key == pygame.K_ESCAPE:
                pygame.quit()
                exit()
            elif event.key == pygame.K_RETURN:
                if stime is None:
                    # 第一次按Enter开始计时
                    stime = time()
                    EnterLine = "Start typing!"
                else:
                    # 按Enter提交输入(可选,也可自动匹配)
                    pass
            else:
                user_text += event.unicode

    # 绘制背景和静态元素
    if imgtick % 100 == 0:
        Win.blit(chest_left_tilt,(0,0))
    else:
        Win.blit(chest_right_tilt,(0,0))
    imgtick +=1
    Win.blit(display_textbox,(100,50))    
    Win.blit(EnterPrompt,(150,100))

    # 绘制提示文本动画
    if counter < text_speed * len(displayprompt):
        counter +=1
    displayed_prompt = displayprompt[0:counter//text_speed]
    snip = txtf.render(displayed_prompt,True,(0,0,0))
    Win.blit(snip,(150,150))

    # 绘制提示行
    EnterLineRender = txtf.render(EnterLine,True,(0,0,0))
    EnterLineRender.set_alpha(200)
    Win.blit(EnterLineRender,(150,200))

    # 绘制用户输入
    user_text_surface = txtf.render(user_text,True,(0,0,0))        
    Win.blit(user_text_surface, (150,250))

    # 游戏结果判断逻辑
    if game_result is None and stime is not None:
        if user_text == displayprompt:
            etime = time()
            speed = typingSpeed(user_text, stime, etime)
            wpm = round(speed *60, 2)
            if wpm >=35:
                game_result = "win"
                result_text = "You have unlocked the chest ! "
            else:
                game_result = "lose"
                result_text = "You have failed to unlock the chest ! "

    # 绘制结果动画
    if game_result is not None:
        if result_counter < text_speed * len(result_text):
            result_counter +=1
        displayed_result = result_text[0:result_counter//text_speed]
        result_surface = txtf.render(displayed_result,True,(0,0,0))
        Win.blit(result_surface,(150,350))

    pygame.display.update()
    clock.tick(100)

关键修改说明

  1. 合并事件处理:将所有事件处理放在一个for event in pygame.event.get()循环中,确保所有输入事件被正确捕获。
  2. 处理换行符:在GeneratePrompt中用rstrip('\n')去掉文本末尾的换行符,同时过滤掉空行和不符合长度的文本。
  3. 修复计时逻辑:初始化stime为None,仅在第一次按Enter时开始计时,避免未计时就触发判断。
  4. 重构结果渲染:将结果动画逻辑移到主循环中,确保每一帧都更新渲染状态,显示完整的打字动画。
  5. 变量名冲突修复:将原speed变量重命名为text_speed,避免和打字速度计算的speed变量冲突。
  6. 优化提示文本生成:先过滤有效提示再随机选择,避免选中不符合要求的文本,同时移除无效的递归调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:55:54