
简介这份资源是E-prime系列课程的第五讲配套样例包面向心理学实验编程初学者与需要进阶实验设计技术的研究人员帮助解决移动窗口、多字符反应、随机方位、N-back、双任务等常见实验范式的实现难题。压缩包共46个文件约4.2MB包含es3与ebs3实验源文件、edat3数据文件、wndpos窗口位置文件、xml实验顾问报告、xsl样式表、txt说明及jpg示例图片覆盖从实验搭建到数据记录与结果核查的完整流程。已有623人学习下载说明该样例在同类课程中具有较高参考价值。读者可直接打开各实验文件对照移动窗口技术、同种材料不连续出现设计、鼠标选择技术、lnLine刺激反应时记录等模块理解E-prime在控件布局、刺激呈现与反应采集上的具体实现方式并借助ExperimentAdvisorReport快速定位设计问题适合作为课程练习与自主实验开发的模板参考。1. 从一份 E-prime 实验设计样例包说起它到底能帮你省下多少试错时间如果你正在用 E-prime 做心理学实验大概率经历过这样的场景程序跑通了但刺激呈现时间总差那么几毫秒或者被试按键后数据记录的位置对不上再或者明明在 List 里设好了条件运行时却只循环了第一行。这些问题往往不是 E-prime 本身有 bug而是实验设计里几个关键设置没踩对点。这份「E-prime 系列课程 5实验设计常用技术-样例」压缩包就是围绕这类高频痛点整理的实操素材。它不厚但里面每个样例都对应一个具体的技术点——从 List 嵌套、刺激随机化到反应时记录、Inline 脚本控制基本覆盖了认知心理学实验里最常翻车的几个环节。适合已经能打开 E-prime、能拖控件但一遇到复杂逻辑就卡壳的人也适合想回头把实验设计规范重新捋一遍的老手。下面我按自己拆包复现的顺序把里面几个核心样例的技术逻辑和参数设置讲清楚。2. 拆开样例包List 嵌套与刺激呈现的底层逻辑2.1 为什么你的 List 总是只跑第一行E-prime 里最容易被低估的控件就是 List。很多人把它当成一个简单的表格填几行条件就完事结果运行时发现所有试次都用了第一行的参数。根本原因在于 List 的层级关系和调用方式没理清。E-prime 的 List 本质是一个嵌套结构上层 List 负责定义条件组合下层 List 负责具体试次循环。如果你把条件直接写在一个 List 里又没有设置正确的 Reset 和 Exit 条件程序就会在第一次循环后直接跳出。常见做法是在 SessionProc 里放一个主 List把不同条件组作为它的行每个条件组再调用一个下级 List用来控制该条件下的试次重复次数。这样做的代价是结构变复杂但好处是条件之间的隔离性更好改一个条件不会影响其他条件。我一般会在主 List 的每一行后面加一个 Proc 控件把该条件对应的刺激呈现、反应收集、反馈逻辑都塞进去这样调试的时候可以单独禁用某一行快速定位问题。2.2 刺激呈现时间的精度控制从 Refresh 到 DurationE-prime 的计时精度一直是玄学话题。有人测出来偏差 2ms有人测出来偏差 20ms差别往往不在代码而在几个关键参数的设置。样例包里有一个专门针对时间精度的实验核心思路是把刺激的 Duration 设为 0然后用 Inline 脚本控制呈现和消失的时机而不是依赖控件的默认 Duration。具体操作上先把 Display 控件的 Duration 改成 0然后在它前面加一个 Inline 脚本用Stimulus.OnsetTime和Stimulus.OffsetTime来记录实际呈现时间。如果需要在特定时间点强制刷新可以用Display.Refresh方法。参数上关键是把 PreRelease 设为 0把 Duration 交给脚本控制。这样做的代价是脚本复杂度上升但换来的是对呈现时间的直接控制而不是被 E-prime 的内部调度逻辑牵着走。 在 Display 控件前插入的 Inline 脚本 Dim onsetTime As Long Dim offsetTime As Long 记录刺激开始呈现的时间戳 onsetTime Clock.Read TextDisplay1.OnsetTime onsetTime 手动控制呈现时长这里设为 200ms TextDisplay1.Duration 200 TextDisplay1.Run 记录刺激消失的时间戳 offsetTime Clock.Read TextDisplay1.OffsetTime offsetTime 把实际呈现时长写入日志方便后续核对 c.SetAttrib ActualDuration, offsetTime - onsetTime这段脚本的逻辑是先读取当前时钟把刺激的 OnsetTime 手动赋值然后设置 Duration 并运行。运行结束后再读一次时钟把差值写入日志。参数上Clock.Read返回的是毫秒级时间戳Duration的单位也是毫秒。注意TextDisplay1.Run会阻塞后续代码直到呈现结束所以 offsetTime 的读取时机是准确的。如果你把这段脚本放在 Display 控件之前记得把 Display 控件的 Duration 也设为 0否则会重复计时。2.3 反应收集与正确率计算从 InputMask 到 Accuracy反应收集的坑主要集中在两点一是按键映射不对二是正确率计算逻辑写错。样例包里有一个 Go/No-Go 范式的样例里面用 InputMask 控件来限制允许的按键然后在 Inline 脚本里比对反应和正确答案。关键参数是 InputMask 的Mask属性它决定了哪些键会被记录。比如你只想记录 F 和 J 两个键就把 Mask 设为{F}{J}其他键会被忽略。然后在 Inline 脚本里用InputMask.RT获取反应时用InputMask.RESP获取实际按键再和 List 里预设的 CorrectAnswer 做比对。 在 InputMask 控件后插入的 Inline 脚本 Dim correctAnswer As String Dim actualResponse As String Dim isCorrect As Integer 从 List 中读取当前试次的正确答案 correctAnswer c.GetAttrib(CorrectAnswer) 获取被试的实际按键 actualResponse InputMask1.RESP 比对并写入正确率 If actualResponse correctAnswer Then isCorrect 1 Else isCorrect 0 End If 把结果写入日志 c.SetAttrib IsCorrect, isCorrect c.SetAttrib RT, InputMask1.RT这里c.GetAttrib是从当前试次的 List 行里读属性InputMask1.RESP返回的是字符串形式的按键名。注意如果被试没有按键RESP会返回空字符串这时候isCorrect会被设为 0这是符合预期的。如果你需要区分「漏报」和「错报」可以再加一个判断如果actualResponse 就单独标记为 Miss。3. Inline 脚本进阶用代码控制实验流程的四个关键点3.1 条件分支用 If-Then 控制不同试次走不同路径E-prime 的图形界面能做的事有限一旦遇到「如果被试反应正确就走 A 路径错误就走 B 路径」这种逻辑就必须上 Inline 脚本。样例包里有一个视觉搜索实验里面用 If-Then 结构实现了根据反应正确与否跳转到不同反馈界面。核心思路是在关键节点插入一个 Inline 脚本读取当前试次的属性然后设置一个标志变量。后续的 Proc 控件根据这个标志变量决定是否执行。具体实现上可以用c.SetAttrib写入一个自定义属性然后在 Proc 的 Enable 条件里引用它。 根据反应正确与否设置分支标志 Dim isCorrect As Integer isCorrect c.GetAttrib(IsCorrect) If isCorrect 1 Then c.SetAttrib FeedbackType, positive Else c.SetAttrib FeedbackType, negative End If这段脚本把FeedbackType写入当前试次的属性表后续的反馈界面可以根据这个属性显示不同的文字或图片。参数上c.SetAttrib的第一个参数是属性名第二个是值属性名可以自定义只要在后续的控件里能引用到就行。注意这个属性只在当前试次有效下一个试次会被新的值覆盖。3.2 循环控制用 Loop 和 Exit 实现自适应难度自适应难度是很多实验的刚需比如被试连续答对 3 题就升一级连续答错 2 题就降一级。E-prime 本身没有现成的自适应控件但可以用 Inline 脚本配合 List 的循环来实现。样例包里有一个 n-back 任务的样例里面用了一个计数器变量来跟踪连续正确次数。实现方式是在 List 的每一行后面加一个 Inline 脚本读取当前试次的正确性更新计数器。如果计数器达到阈值就修改下一个 Block 的难度参数。这里的关键是c.SetAttrib的作用域——它只影响当前试次要影响后续试次需要把变量写到全局的c对象里或者用Context对象。 自适应难度控制脚本 Dim consecutiveCorrect As Integer Dim currentLevel As Integer 从全局变量读取当前状态 consecutiveCorrect c.GetAttrib(ConsecutiveCorrect) currentLevel c.GetAttrib(CurrentLevel) 更新连续正确次数 If c.GetAttrib(IsCorrect) 1 Then consecutiveCorrect consecutiveCorrect 1 Else consecutiveCorrect 0 End If 判断是否升级 If consecutiveCorrect 3 Then currentLevel currentLevel 1 consecutiveCorrect 0 End If 写回全局变量 c.SetAttrib ConsecutiveCorrect, consecutiveCorrect c.SetAttrib CurrentLevel, currentLevel这段脚本的逻辑是每次试次结束后更新连续正确次数达到 3 次就升级并重置计数器。参数上ConsecutiveCorrect和CurrentLevel需要在实验开始前初始化可以在 SessionProc 的第一个 Inline 脚本里设初值。注意c.SetAttrib写入的属性在同一个 Session 内是持久的但跨 Session 不会保留所以如果你需要跨被试保留难度状态得用文件读写。3.3 数据记录把自定义变量写进 E-prime 日志E-prime 默认的日志只记录控件自带的数据比如反应时、按键、呈现时间。但实验里往往需要记录一些自定义变量比如被试的组别、当前难度级别、刺激的具体属性。这些数据如果不手动写入后期分析时就得靠试次序号去猜非常痛苦。样例包里有一个专门的日志记录脚本核心是用c.SetAttrib把自定义变量写进当前试次的属性表E-prime 会自动把这些属性输出到 edat 文件里。关键是要在实验开始前用c.SetAttrib初始化这些变量否则日志里会出现空值。 实验开始前的初始化脚本 c.SetAttrib SubjectGroup, experimental c.SetAttrib CurrentLevel, 1 c.SetAttrib ConsecutiveCorrect, 0 每个试次结束后的记录脚本 c.SetAttrib StimulusType, c.GetAttrib(StimType) c.SetAttrib BlockNumber, c.GetAttrib(BlockNum)初始化脚本放在 SessionProc 的最前面记录脚本放在每个试次的末尾。参数上属性名不要用 E-prime 的保留字比如RT、RESP、OnsetTime这些已经被系统占用了自定义属性最好加个前缀比如Custom_。注意 edat 文件里的列名就是属性名所以命名要规范方便后期在 SPSS 或 R 里直接读取。3.4 调试技巧用 Debug.Print 和日志文件定位问题E-prime 的调试体验不算友好脚本报错时往往只给一个行号不告诉你具体哪里出了问题。样例包里有一个调试脚本的示例核心是用Debug.Print把关键变量的值输出到 E-prime 的 Output 窗口或者用文件读写把调试信息写到外部文件。 调试脚本示例 Dim debugMsg As String debugMsg Trial c.GetAttrib(TrialNum) : RT c.GetAttrib(RT) , Correct c.GetAttrib(IsCorrect) Debug.Print debugMsg 如果需要写入外部文件 Dim fso As Object Dim logFile As Object Set fso CreateObject(Scripting.FileSystemObject) Set logFile fso.OpenTextFile(C:\debug_log.txt, 8, True) logFile.WriteLine debugMsg logFile.CloseDebug.Print的输出可以在 E-prime 的 Output 窗口看到适合快速调试。如果要长期记录就用文件读写注意OpenTextFile的第二个参数 8 表示追加模式第三个参数 True 表示文件不存在时自动创建。路径最好用绝对路径相对路径在 E-prime 里的工作目录不确定容易翻车。4. 避坑与排查E-prime 实验设计里最容易翻车的五个点4.1 现象程序运行到一半突然卡死没有任何报错原因最常见的是 Inline 脚本里出现了死循环或者 List 的 Exit 条件设置不当导致无限循环。E-prime 在运行时不会主动检测死循环所以一旦进入就卡死。解决在 Inline 脚本里加一个计数器超过一定次数就强制退出。比如在循环里加If loopCount 1000 Then Exit Do。另外检查 List 的 Exit 条件确保每个 List 都有明确的终止条件不要依赖默认行为。4.2 现象反应时数据明显偏大比实际按键时间晚了几百毫秒原因InputMask 控件的 Duration 设得太长或者 PreRelease 设得太大。E-prime 的 InputMask 默认会等待一段时间如果 Duration 是 5000ms那即使被试 200ms 就按键了RT 也可能被记录成 5000ms 减去某个值。解决把 InputMask 的 Duration 设为 0让它无限等待直到被试按键或超时。同时把 PreRelease 设为 0避免提前释放。然后在 Inline 脚本里用InputMask1.RT获取实际反应时这个值是基于按键时间戳的不受 Duration 影响。4.3 现象edat 文件里某些列全是空值原因自定义属性没有在实验开始前初始化或者属性名拼写不一致。E-prime 对属性名大小写敏感StimType和Stimtype会被当成两个不同的属性。解决在 SessionProc 的第一个 Inline 脚本里把所有自定义属性都初始化一遍赋一个默认值。然后在后续脚本里引用时确保属性名完全一致。可以在 E-prime 的 List 里先定义好这些属性这样脚本里引用时会有提示减少拼写错误。4.4 现象刺激呈现时间不稳定有时 200ms有时 220ms原因E-prime 的计时精度受显示器刷新率影响。如果 Duration 不是刷新周期的整数倍实际呈现时间会有波动。比如 60Hz 显示器刷新周期是 16.67ms200ms 不是它的整数倍实际呈现可能是 200ms 或 216.67ms。解决把 Duration 设为刷新周期的整数倍比如 200ms 改成 200.04ms12 个周期或者直接用 Inline 脚本控制在垂直同步信号到来时刷新。另外把显示器的刷新率固定为 60Hz 或 120Hz不要用动态刷新率。4.5 现象被试按键后程序没有反应或者记录了错误的按键原因InputMask 的 Mask 属性设置不对或者键盘布局和预期不一致。比如你设了{F}{J}但被试用的是小键盘或者输入法处于中文状态按键会被拦截。解决在实验开始前提醒被试切换输入法到英文状态并且用主键盘区的按键。Mask 属性里可以加上{SPACE}或{ENTER}作为确认键。如果还是有问题可以在 Inline 脚本里用InputMask1.RESP打印实际按键看看被试到底按了什么。5. 从样例到自己的实验一个可复用的参数配置模板拆完这几个样例我最大的感受是E-prime 的灵活性很高但代价是很多设置需要手动对齐。与其每次从零开始调不如整理一份自己的参数模板把常用的配置固化下来。下面这张表是我根据样例包里的设置结合自己踩过的坑整理的一份关键参数对照表。控件/属性推荐值说明Display.Duration0交给 Inline 脚本控制避免默认计时误差Display.PreRelease0避免提前释放导致计时不准InputMask.Duration0无限等待直到被试按键或超时InputMask.Mask{F}{J}只记录允许的按键其他键忽略List.Exit自定义条件不要依赖默认明确写出终止条件Inline 脚本位置控件前/后前置脚本用于初始化后置脚本用于记录自定义属性前缀Custom_避免和系统属性冲突日志文件路径绝对路径相对路径在 E-prime 里不可靠这份模板不是万能的但能覆盖大部分常见实验场景。我一般会在新建实验时先把这些参数设好然后再根据具体需求微调。比如视觉搜索实验需要更精确的呈现时间就把 Display 的 Duration 设为 0用脚本控制如果是问卷类实验对时间精度要求不高就可以用默认设置省事。还有一个习惯每次改完脚本先跑一个只有 3 个试次的迷你版本确认流程和数据记录都正常再跑完整实验。这样即使出问题也能快速定位不用在几百个试次里大海捞针。从那以后我每次拿到新的 E-prime 样例都强制走一遍「迷你试次验证」的流程省下了不少返工时间。希望这些拆解对你有用。本文还有配套的精品资源点击获取