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

Bevy应用在WASM和桌面端的响应时间测量不一致问题

问题:Bevy开发的心理学实验响应时间偶发对数型错误

我使用Bevy引擎开发了一款可在WASM和桌面端运行的简易心理学实验,用于测量椭圆显示到用户输入的响应时间。但发现响应时间测量结果有时会出现对数型错误,该问题偶发、难以复现,WASM和桌面端均存在此现象。

核心时间处理代码

#[derive(Resource)]
struct TrialState {
    start_time: Instant,
}

impl Default for TrialState {
    fn default() -> Self {
        TrialState {
            start_time: Instant::now(),
        }
    }
}

fn refresh_ellipses(
    // ... 部分代码省略 ...
) {
    if experiment_state.ellipses_drawn && (keys.just_pressed(KeyCode::Key1) || keys.just_pressed(KeyCode::Key0) || keys.just_pressed(KeyCode::Space)) {
        trial_state.start_time = Instant::now();
    }
    // ... 部分代码省略 ...
}

fn update_user_responses(
    // ... 其他参数省略 ...
) {
    if keys.just_pressed(KeyCode::Key1) || keys.just_pressed(KeyCode::Key0) || keys.just_pressed(KeyCode::Space) {
        let elapsed = trial_state.start_time.elapsed().as_secs_f32();
        // ... 函数剩余部分省略 ...
    }
}

CSV结果记录相关代码

#[derive(Default, Resource)]
struct ExperimentState {
    final_result: Vec<(usize, usize, String, f32)>,
    // ... 其他字段省略 ...
}

fn update_user_responses(
    keys: Res<Input<KeyCode>>,
    mut experiment_state: ResMut<ExperimentState>,
    trial_state: Res<TrialState>,
    mut app_state: ResMut<AppState>,
) {
    // ... 部分代码省略 ...

    if keys.just_pressed(KeyCode::Key1) || keys.just_pressed(KeyCode::Key0) || keys.just_pressed(KeyCode::Space) {
        let elapsed = trial_state.start_time.elapsed().as_secs_f32();
        experiment_state.final_result.push((num_left, num_right, result, elapsed));
        // ... 函数剩余部分省略 ...
    }

    if experiment_state.num_trials == TOTAL_TRIAL {
        print_final_results(&experiment_state.final_result);
    }
}

fn print_final_results(final_results: &Vec<(usize, usize, String, f32)>) {
    // ... 前置代码省略 ...

    let mean_correct_rt: f32 = final_results
    .iter()
    .filter(|(_, _, is_correct, _)| is_correct == "Correct") 
    .map(|(_, _, _, response_time)| response_time)
    .sum::<f32>() / final_results.len() as f32;

问题分析与解决方案

核心问题:系统执行顺序不确定导致的时序错误

当前代码中,同一个按键事件同时被两个独立系统处理:

  • refresh_ellipses系统负责更新下一次试验的start_time
  • update_user_responses系统负责计算当前试验的响应时间

Bevy中未指定顺序的系统可能并行执行或按任意顺序运行,这会导致两种错误情况:

  • 若refresh_ellipses先执行:先将start_time更新为当前时间,随后update_user_responses计算的是"刚更新的时间到当前时间"的耗时,结果接近0,属于明显错误
  • 若update_user_responses先执行:先计算当前试验的正确耗时,再更新start_time,结果正常

这就是响应时间偶发错误的根本原因。

其他辅助问题:均值计算逻辑错误

在print_final_results中,计算正确响应时间均值时,分母用了总试验数final_results.len(),但正确的分母应该是过滤后的正确试验数量,否则会导致均值被错误拉低。

解决方案

  1. 合并逻辑到单一系统:将更新start_time和记录响应时间的逻辑放在同一个系统中,明确执行顺序,彻底避免时序问题。示例修改方向:
fn handle_trial_flow(
    keys: Res<Input<KeyCode>>,
    mut experiment_state: ResMut<ExperimentState>,
    mut trial_state: ResMut<TrialState>,
) {
    if experiment_state.ellipses_drawn && (keys.just_pressed(KeyCode::Key1) || keys.just_pressed(KeyCode::Key0) || keys.just_pressed(KeyCode::Space)) {
        // 1. 先记录当前试验的响应时间
        let elapsed = trial_state.start_time.elapsed().as_secs_f32();
        experiment_state.final_result.push((num_left, num_right, result, elapsed));
        
        // 2. 再更新start_time为当前时间,用于下一次试验
        trial_state.start_time = Instant::now();
        
        // ... 其他试验流程逻辑 ...
    }
}
  1. 修正均值计算逻辑:
fn print_final_results(final_results: &Vec<(usize, usize, String, f32)>) {
    // ... 前置代码省略 ...

    let correct_trials: Vec<&f32> = final_results
        .iter()
        .filter(|(_, _, is_correct, _)| is_correct == "Correct") 
        .map(|(_, _, _, response_time)| response_time)
        .collect();
    
    let mean_correct_rt: f32 = if correct_trials.is_empty() {
        0.0
    } else {
        correct_trials.iter().sum::<&f32>() / correct_trials.len() as f32
    };
}
  1. 初始化时确保start_time正确:在第一次椭圆绘制完成后再设置start_time,避免使用资源创建时的初始时间(可能距离椭圆显示已有较长时间)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:51:12