中级教程(JavaScript):视觉搜索
关于 OpenSesame
OpenSesame 是一个用户友好型的程序,用于开发心理学、神经科学和实验经济学的行为实验。对于初学者来说,OpenSesame 有一个全面的图形化点选界面。对于高级用户,OpenSesame 支持 Python(仅限桌面)和 JavaScript(桌面和浏览器)。
OpenSesame 可根据 通用公共许可证 v3 免费获取。
关于本教程
本教程展示了如何使用 OpenSesame 创建一个基本的视觉搜索实验 (Mathôt, Schreij, & Theeuwes, 2012)。我们将使用图形界面和 JavaScript 来开发一个可以在浏览器中在线运行的实验。建议您已经有一些 OpenSesame 和 JavaScript 的使用经验。本教程大约需要一个小时。
本教程的基于 Python 的版本也可以使用。如果您不需要在线运行您的实验,那么 Python 教程可能是您需要的:
资源
- 下载 — 本教程假设您运行的是 OpenSesame 4.0.0 或更新版本以及 OSWeb 2.0 或更新版本。您可以从以下链接下载最新版本的 OpenSesame:
- 文档 — 专门的文档网站可以在以下链接找到:
- 论坛 — 支持论坛可以在以下链接找到:
- Sigmund -- SigmundAI 是一个具有 OpenSesame 专家知识的人工智能助手,可以在以下链接找到:
实验
在本教程中,您将创建一个基本的视觉搜索实验。该实验类似于 Treisman and Gelade (1980) 的经典视觉搜索研究,但并非完全相同。
在开始构建您自己的实验之前,您已经可以参与这个实验。这将让您对在本教程中所进行的工作有一个很好的了解。
在这个实验中,参与者需要搜索一个目标物体,它可以是黄色方块、黄色圆圈、蓝色方块或蓝色圆圈;目标的身份会在实验的不同阶段中变化。参与者通过按右箭头键(存在)或左箭头键(不存在)来指示目标是否存在。
除了目标以外,还会展示零个或多个干扰物。有三种条件,条件决定了有哪些干扰物:
- 在联结条件下,干扰物可以具有任何形状和颜色,唯一的限制是干扰物不能与目标完全相同。因此,例如,如果目标是一个黄色方块,那么干扰物就是黄色圆圈、蓝色圆圈和蓝色方块。
- 在形状特征条件下,干扰物的形状与目标不同,但可以是任何颜色。所以,例如,如果目标是一个黄色方块,那么干扰物就是黄色圆圈和蓝色圆圈。
- 在颜色特征条件下,干扰物可以是任何形状,但颜色与目标不同。所以,例如,如果目标是一个黄色方块,那么干扰物就是蓝色方块和蓝色圆圈。
每次试验后都会立即显示反馈:正确响应后显示绿点,错误响应后显示红点。在每个实验阶段之后,会显示关于平均响应时间和准确度的详细反馈。
像这样的实验显示了两个典型的发现:
- 在连接条件中找到目标所需的时间比在两个特征条件中要多。
- 在连接条件中,随着干扰物数量的增加,反应时间也会增加。这表明人们逐项搜索目标;这被称为串行搜索。
- 在特征条件下(形状和颜色都有),随着干扰物数量的增加,反应时间不会增加,或者几乎不会增加。这表明人们可以同时处理整个显示内容;这被称为并行搜索。
根据Treisman和Gelade的特征整合理论,这些结果反映了在连接条件下,您需要组合或绑定每个对象的颜色和形状。这种绑定需要注意力,因此您需要将注意力从一个对象转移到另一个对象;这很慢,解释了反应时间取决于有多少对象。相反,在特征条件下,颜色和形状不需要绑定,因此整个显示可以在不将注意力引向每一个对象的情况下进行一次扫描。
实验设计
此设计:
- 是在主题内,因为所有参与者都进行了所有条件
- 是完全交叉(或全因子),因为所有条件的组合都出现
- 有三个条件(或因子):
- 在块内变化:
set_size
有三个级别(1、5、15),或SS3condition
有三个级别(连接、特征_形状、特征_颜色),或CN3target_present
有两个级别(存在、不存在),或TP2
- 在块之间变化:
target_shape
有两个级别(正方形、圆形),或TS2target_color
有两个级别(黄色、蓝色),或TC2
- 在块内变化:
- 有N个主题,或SN
您可以将此设计写为SN×SS3×CN3×TP2×TS2×TC2
有关实验设计符号的更多信息,请参阅:
第1步:创建实验的基本结构
启动OpenSesame,在“开始!”选项卡中选择扩展模板。此模板提供了许多认知心理学实验通常具有的基本结构,例如我们将在此处创建的实验。
扩展模板包含一些我们不需要的项目。删除以下项目:
- about_this_template
- practice_loop
- end_of_practice
删除这些项目后,它们仍可在“未使用的项目”回收站中看到。要永久删除这些项目,请单击“未使用的项目”回收站,然后单击“永久删除未使用的项目”按钮。
最后,为实验起一个好名字,例如“视觉搜索”。要做到这一点,请打开通用属性选项卡(在概览区域中点击“扩展模板”)并单击实验名称进行编辑。
还将OpenSesame配置为在浏览器上运行实验,而非桌面上。
概览区域现在应该看起来像Figure 2:
第2步:定义在块之间变化的实验变量
如上所述,在我们的实验中,有两个变量在块之间变化:target_shape
和target_color
。因此,我们需要在experimental_loop中定义这些变量。为了理解为什么,请考虑图Figure 2中显示的结构,从底部(即最缩进的级别)开始。
- trial_sequence 对应一个单独的试验
- block_loop 对应一组试验
- 因此,在trial_sequence的每次运行中,此处定义的变量会有所不同;换句话说,在block_loop中定义的变量在__块内__变化。
- block_sequence 对应一组试验,其前面是对反馈变量的重置,后面是参与者的反馈
- experimental_loop 对应多组试验
- 因此,在block_sequence的每次运行中,此处定义的变量会有所不同;换句话说,在experimental_loop中定义的变量在__块之间__变化。
- experiment 对应整个实验,即一个指导屏幕,然后是多组试验,最后是实验结束屏幕
点击experimental loop,并定义:
target_shape
,可以是'square'或'circle';和target_color
,可以是'yellow'或'blue'。
我们有一个全因子设计,这意味着所有2 × 2 = 4的组合都必须发生。experimental_loop的表格现在应该如Figure 3所示:
步骤3:在每个试验块开始时给出指导
现在,实验以一个单独的instructions屏幕开始。在我们的案例中,我们希望在每组试验之前给予指导,告诉参与者要查找什么目标(因为目标之间的身份有所不同)。
将指令移至block_sequence
因此,拿起instructions项目并将其拖到block_sequence上。一个弹出窗口会出现,问您是要:
- 将项目插入到block_sequence中,在这种情况下,instructions将成为block_sequence的第一个项目;或者
- 在block_sequence之后插入项目,在这种情况下,instructions将移到block_sequence之后的某个位置。
选择第一个选项('插入到')。现在block_sequence以指代屏幕开头,这正是我们想要的。
添加指导文字
点击instructions以打开它,并添加合适的指导文字,例如:
操作指南
寻找 {target_color} {target_shape}
如果找到请按右箭头键
如果没有找到请按左箭头键
按任意键开始
花括号中的 '{target_color}' 和 '{target_shape}' 表示这些不是文字,而是指我们在experimental_loop中定义的变量。当实验运行时,这些变量的值将显示在此处,参与者将看到(例如)“寻找黄色圆形”。
给出目标的视觉预览
向参与者展示他们需要找到的实际刺激也是很好的。要做到这一点:
- 在显示器的中心绘制一个填充的圆形(确保它与文本不重叠);
- 将圆形的颜色更改为'{target_color}'。这意味着圆形的颜色取决于变量
target_color
的值;和 - 将show-if表达式更改为
target_shape == 'circle'
。这是一个Python表达式,用于检查变量target_shape
是否为 'circle'。 请注意,尽管在浏览器中运行实验时您不能使用Python的全部功能inline_script
项目,但您可以为这些简单的条件表达式使用Python。
换句话说,我们画了一个圆,它的颜色由 target_color
决定;而且,只有在变量 target_shape
为 'circle' 时,这个圆才会显示。有关变量和show-if语句的更多信息,请参阅:
我们使用相同的技巧来绘制一个正方形:
- 在显示器的中心绘制一个填充的正方形;
- 将正方形的颜色更改为 '{target_color}';和
- 将show-if语句更改为
target_shape == 'square'
instructions 屏幕现在应该如 Figure 4 所示:
步骤4:定义在区块内变化的实验变量
我们的实验在区块内有三个变量在变化:condition
,set_size
和target_present
。如在步骤2中所述,我们需要在block_loop中定义这些变量,以便它们在每次运行trial_sequence时变化。
这三个变量总共有3 × 3 × 2 = 18种不同的组合。我们可以手动输入这些表格,但是,因为我们有完全因子设计,我们还可以使用完全因子设计向导。要执行此操作,请首先打开block_loop,然后单击“完全因子设计”按钮。
在接下来出现的表格中,将变量名称放在第一行,将值放在下面的行上,如Figure 5所示。
现在单击"确定"以生成完整设计。block_loop 的表格现在应该看起来像 Figure 6。
步骤5:创建试验序列并添加初始化脚本
我们希望我们的试验序列如下所示:
- 固定点,我们将使用sketchpad。
- 搜索显示,我们将使用自定义的inline_javascript在JavaScript中创建。
- 响应收集,我们将使用keyboard_response。
- 数据记录,我们将使用logger。
- (我们还想在每次试验后立即得到反馈,但稍后我们会回到这一点。)
因此,trial_sequence 中唯一缺少的是 inline_javascript。
- 在sketchpad之后插入新的inline_javascript,并将其重命名为search_display_script。
- 将sketchpad重命名为fixation_dot,以便明确其功能;并
- 将fixation_dot的持续时间更改为500,以便将固定点显示500毫秒。(应该已经有一个固定点绘制;如果没有,可以在fixation_dot的中心绘制一个。)
我们还需要在实验开始时添加一个初始化脚本。我们仅将其用于定义(let
)一个将容纳我们要绘制的Canvas
对象的变量。在JavaScript中,您必须对变量进行严格一次定义,这就是为什么我们不能在trial_sequence中执行此操作。
- 在experiment序列的顶部插入新的inline_javascript,并将其重命名为init。
概览区现在应如Figure 7所示。
步骤6:生成搜索显示
自上而下和防御式编程
现在事情将变得有趣:我们将开始使用 JavaScript 进行编程。我们将使用两个指导原则:自上而下和防御式编程。
- 自上而下编程 意味着我们从最抽象的逻辑开始,而不去管这个逻辑是如何实现的。一旦最抽象的逻辑到位,我们将会进行到较少的抽象逻辑,依此类推,直到我们到达实现的细节。这种技巧有助于保持代码结构。
- 防御性编程 意味着我们假设我们会犯错误。因此,为了保护我们自己,我们将在代码中构建完整性检查。
注意: 以下解释假设您对 JavaScript 有些了解。如果像Array
,for
循环和函数之类的概念对您来说没有意义,那么最好先浏览一个入门 JavaScript 教程。您可以在此处找到 JavaScript 教程的链接:
代码的逻辑如 Figure 8 所示。数字显示了我们将实现功能的顺序,从抽象级别开始。
使用 let, var 和 const 声明变量
在JavaScript中,在可以使用变量之前,您必须先 '声明' 它。(在 Python 中不需要这样做。) 在我们的例子中,我们将使用一个名为 c
的变量,因此我们需要声明它。为此,请打开 init 脚本的准备选项卡,并使用 let
关键字声明变量 c
:
let c
我们有三种声明变量的不同方式:
- 使用
let
,如我们在这里所做的。在 OpenSesame 中,这使 JavaScript 可以使用该变量,但在用户界面中不能将其作为实验变量。 - 使用
var
。在 OpenSesame 中,这使电子界面中的实验变量也可以使用该变量。(稍后我们将对 correct_response 变量进行此操作。) - 使用
const
。 这与var
类似,但重要区别在于变量不能在后面重新分配。
准备和运行阶段
打开 search_display_script 并切换到准备选项卡。OpenSesame 区分两个执行阶段:
- 在准备阶段,每个项目都可以准备自己; 每个项目的内容可能不同:对于 sketchpad,这意味着绘制画布(但不显示);对于 sampler,这意味着加载声音文件(但不进行播放)。
- 在运行阶段,实际执行每个项目; 同样,这取决于项目的内容:对于 sketchpad,这意味着显示之前准备好的画布;对于 sampler,这意味着播放之前加载的音频文件。
对于 inline_javascript,您需要自己决定将什么放入准备阶段,什么放入运行阶段。在我们的案例中,是相当明确的:我们将准备阶段划分为绘制画布的代码,而运行阶段则用于显示画布(较小部分)。
参见:
实现抽象层次
从最抽象的层次开始:定义一个绘制视觉搜索显示的功能。我们不详细说明如何做到这一点;我们只是假定有一个函数可以实现这一功能,然后我们将在之后详细考虑这个问题——这就是自顶向下编程。
在准备选项卡中输入以下代码:
c = draw_canvas()
在这里发生了什么?我们...
- 调用
draw_canvas()
,它返回一个我们将存储为c
的Canvas
对象;换句话说,c
是对应搜索显示器的Canvas
对象。这就假定函数draw_canvas()
存在,即使我们还没有定义它。
Canvas
对象是一个单独的显示器;在某种程度上,它是 sketchpad 的 JavaScript 对等物。另请参阅:
现在,通过定义 draw_canvas()
(在到目前为止的脚本之上)我们进一步详细说明:
/**
* 绘制搜索画布。
* @return A Canvas
**/
function draw_canvas() {
let c = Canvas()
let xy_list = xy_random(set_size, 500, 500, 75)
if (target_present === 'present') {
let [x, y] = xy_list.pop()
draw_target(c, x, y)
} else if (target_present !== 'absent') {
throw 'target_present 的无效值 ' + target_present
}
for (let [x, y] of xy_list) {
draw_distractor(c, x, y)
}
return c
}
在这里发生了什么?我们 …
- 使用工厂函数
Canvas()
创建一个空画布c
。 - 使用另一个常用函数
xy_random()
生成一个随机的x, y
坐标数组,称为xy_list
。此数组决定了显示刺激的位置。位置从一个 500 × 500 px 的区域中采样,最小间距为 75 px。 - 检查实验变量
target_present
是否具有值'present';如果是,则从xy_list
中pop()
一个x, y
元组,并在此位置绘制目标。这里假设有一个函数draw_target()
,尽管我们还没有定义它。 - 如果
target_present
既不是'present'也不是'absent',我们会throw
一个错误;这是防御性编程,并保护我们免受拼写错误(例如,如果我们错误地输入了'presenr' 而不是 'present')。 - 遍历所有剩余的
x, y
值,并在每个位置绘制一个干扰物。这假设有一个函数draw_distractor()
,尽管我们还没有定义它。 - 返回
c
,现在已经在其上绘制了搜索显示。
有几个常用函数,如Canvas()
和xy_random()
,它们在 inline_javascript 项中始终可用。请参阅:
实验变量是全局变量。这就是为什么您可以引用在 block_loop 中定义的set_size
,即使在脚本中从未明确定义过变量 set_size
。对于target_shape
、target_color
、condition
等也是如此。请参阅:
实现中级层次
现在我们再往下走一步,通过定义draw_target
(在目前为止的剩余脚本之上):
/**
* 绘制目标。
* @param c 一个画布
* @param x 一个x坐标
* @param y 一个y坐标
**/
function draw_target(c, x, y) {
draw_shape(c, x, y, target_color, target_shape)
}
这里发生了什么?我们……
- 调用另一个函数
draw_shape()
,并指定需要绘制的颜色和形状。这假设有一个draw_shape()
函数,尽管我们还没有定义它。
我们也定义draw_distractor
(在目前为止的剩余脚本之上):
/**
* 绘制单个干扰物。
* @param c 一个画布
* @param x 一个x坐标
* @param y 一个y坐标
**/
function draw_distractor(c, x, y) {
if (condition === 'conjunction') {
draw_conjunction_distractor(c, x, y)
} else if (condition === 'feature_shape') {
draw_feature_shape_distractor(c, x, y)
} else if (condition === 'feature_color') {
draw_feature_color_distractor(c, x, y)
} else {
throw 'Invalid condition: ' + condition
}
}
这里发生了什么?我们……
- 根据条件调用另一个函数来绘制更具体的干扰物。
- 检查
condition
是否具有预期的值。如果没有,我们throw
一个错误。这是防御性编程!否则,如果我们在某个地方犯了一个拼写错误,干扰物可能会在没有产生错误消息的情况下不显示。
现在我们定义一个用于绘制连接条件下的干扰物的函数(在目前为止的剩余脚本之上):
/**
* 绘制连接条件下的单个干扰物:可以是任何形状和颜色的对象,但不能与目标相同。
* @param c 一个画布。
* @param x 一个x坐标。
* @param y 一个y坐标。
**/
function draw_conjunction_distractor(c, x, y) {
let conjunctions = [
['yellow', 'circle'],
['blue', 'circle'],
['yellow', 'square'],
['blue', 'square']
]
let [color, shape] = random.pick(conjunctions)
while (color === target_color && shape === target_shape) {
[color, shape] = random.pick(conjunctions)
}
draw_shape(c, x, y, color, shape)
}
这里发生了什么?我们 …
- 定义一个列表
conjunctions
,包含所有可能的颜色和形状组合。 - 从
conjunctions
中随机选择一个颜色和形状组合。 - 检查所选的颜色和形状是否都等于目标的颜色和形状。如果是这样,继续选择新的颜色和形状,直到不再是这种情况。毕竟,干扰物不能与目标完全相同!
- 调用另一个函数
draw_shape()
,并指定将绘制的干扰物的颜色和形状。这 assumes that there is a functiondraw_shape()
,尽管我们尚未定义它。
此外, 我们 …
- 使用
random
库,它对应于random-ext
包。此库包含有用的随机化函数(如random.pick()
)并且是与 OSWeb 一起包含的非标准 JavaScript 库之一。
现在我们定义在形状特征条件下绘制干扰物的函数(在到目前为止的脚本剩余部分的上方):
/**
*在特征形状条件下绘制单个干扰物:一个与目标形状不同但可以是任何颜色的对象。
* @param c 一个画布。
* @param x 一个 x 坐标。
* @param y 一个 y 坐标。
**/
function draw_feature_shape_distractor(c, x, y) {
let colors = ['yellow', 'blue']
let color = random.pick(colors)
let shape
if (target_shape === 'circle') {
shape = 'square'
} else if (target_shape === 'square') {
shape = 'circle'
} else {
throw 'Invalid target_shape: ' + target_shape
}
draw_shape(c, x, y, color, shape)
}
这里发生了什么?我们 ...
- 随机选择一种颜色。
- 如果目标是圆形,选择一个方形;如果目标是方形,选择一个圆形。
- 如果
target_shape
不是 'circle' 也不是 'square',throw
一个错误—— 更多的防御性编程! - 调用另一个函数,
draw_shape()
,并指定将绘制的干扰物的颜色和形状。这 assumes that there is a functiondraw_shape()
,尽管我们尚未定义它。
(define the added part in remarks similar to previous and preserve rest same) 现在我们定义在颜色特征条件下绘制干扰物的函数:
/**
* 在特征颜色条件下绘制单个干扰物:一个与目标颜色不同但可以是任何形状的对象。
* @param c 一个画布。
* @param x 一个 x 坐标。
* @param y 一个 y 坐标。
**/
function draw_feature_color_distractor(c, x, y) {
let shapes = ['circle', 'square']
let shape = random.pick(shapes)
let color
if (target_color === 'yellow') {
color = 'blue'
} else if (target_color === 'blue') {
color = 'yellow'
} else {
throw 'Invalid target_color: ' + target_color
}
draw_shape(c, x, y, color, shape)
}
这里发生了什么?我们 ...
- 随机选择一个形状。
- 如果目标是黄色,选择一个蓝色;如果目标是蓝色,选择一个黄色。
- 如果
target_color
不是 'yellow' 也不是 'blue',throw
一个错误—— 更多的防御性编程! - 调用另一个函数,
draw_shape()
,并指定将绘制的干扰物的颜色和形状。这 assumes that there is a functiondraw_shape()
,尽管我们尚未定义它。
实现详细级别
现在我们通过定义实际将形状绘制到画布的函数,详细了解所有内容(在脚本剩余部分的上方):
/**
* 绘制单个形状。
* @param c 一块画布。
* @param x x坐标。
* @param y y坐标。
* @param color 颜色(黄色或蓝色)
* @param shape 形状(正方形或圆形)
**/
function draw_shape(c, x, y, color, shape) {
if (shape === 'square') {
// 参数作为对象传递!
c.rect({x:x-25, y:y-25, w:50, h:50, color:color, fill:true})
} else if (shape === 'circle') {
// 参数作为对象传递!
c.circle({x:x, y:y, r:25, color:color, fill:true})
} else {
throw '无效的形状:' + shape
}
if (color !== 'yellow' && color !== 'blue') {
throw '无效的颜色:' + color
}
}
这里发生了什么?我们...
- 检查应该绘制哪个形状。对于正方形,我们在画布上添加一个
rect()
元素。对于圆形,我们添加一个circle()
元素。 - 检查形状是否为正方形或圆形,如果不是就
throw
错误。这是防御式编程的另一个示例!我们确保我们没有意外地指定了一个无效的形状。 - 检查颜色是否不是黄色或蓝色,如果不是就
throw
错误。
重要的是,Canvas
函数接受一个指定所有参数名称的单一对象({}
),如下所示:
// 正确:传递一个包含所有参数名称的单一对象
c.rect({x:x-25, y:y-25, w:50, h:50, color:color, fill:true})
// 错误:不要按顺序传递参数
// c.rect(x-25, y-25, 50, 50, color, true)
// 错误:JavaScript不支持命名参数
// c.rect(x=x-25, y=y-25, w=50, h=50, color=color, fill=true)
实现运行阶段
因为我们已经在准备阶段完成了所有繁重的工作,所以运行阶段只是:
c.show()
就是这样!现在你已经画了一个完整的视觉搜索显示。而且,更重要的是,你是以一种容易理解的方式完成这方面的工作,因为自顶向下的编程,和保险起见,因为防御性编程。
步骤7:定义正确的响应
要知道参与者是否正确响应,我们需要知道正确的响应。您可以在 block_loop 中显式定义此响应(如初学者教程所示);但在这里我们将使用一些简单的JavaScript检查目标是否存在,然后相应地定义正确的响应。
为此,我们首先需要在 init 脚本的准备标签下声明变量,就在 let c
下面。这次,我们使用 var
关键字声明 correct_response
,因为这使变量在用户界面中可用(而 let
则不会这样做):
var correct_response
接下来,在 trial_sequence 的开始位置插入一个新的inline_javascript,并将其重命名为 correct_response_script。在准备阶段,输入以下代码:
if (target_present === 'present') {
correct_response = 'right'
} else if (vars.target_present === 'absent') {
correct_response = 'left'
} else {
throw 'target_present 应该是缺席或出席,而不是 ' + target
}
这里发生了什么?我们...
- 根据目标是否存在检查。如果目标存在,正确的响应是'right'(右箭头键);如果目标不存在,则正确的响应是 'left'(左箭头键)。实验变量
correct_response
会被OpenSesame自动使用;因此,我们不需要明确表示这个变量包含正确的响应。 - 根据目标是否存在或缺席检查,如果不是就
throw
错误 — 这是另一个防御编程的例子。
步骤8:给每次试验反馈
每次试验后的反馈可以激励参与者;然而,每次试验的反馈不应干扰实验的流程。给每次试验反馈的好方法是在正确响应后简要显示绿色的凝视点,在错误响应后显示红色的凝视点。
要实现这一点:
- 在 trial_sequence 中插入两个新的 sketchpad,放在 keyboard_response 之后。
- 将一个 sketchpad 重命名为 green_dot,在其上绘制一个绿色的中心固定点,并将其持续时间更改为 500。
- 将另一个 sketchpad 重命名为 red_dot,在其上绘制一个红色的中心固定点,并将其持续时间更改为 500。
当然,在每次试验中只应显示两个点之一。为了实现这一点,我们将在 trial_sequence 中指定运行条件语句:
- 将 green_dot 的运行条件语句更改为 'correct == 1',表示它只应在正确响应后显示。
- 将 red_dot 的运行条件语句更改为 'correct == 0',表示它只应在不正确响应后显示。
如果变量 correct_response
可用,则会自动创建变量 correct
;这就是为什么我们在第七步定义了 correct_response
。有关变量和运行条件语句的更多信息,请参见:
trial_sequence 现在应该看起来像 Figure 9。
完成!
恭喜,实验完成了!您可以通过点击工具栏上显示有灰色播放按钮的绿色圆圈按钮进行测试运行(快捷键:Alt+Ctrl+W
)。
如果实验第一次尝试时不工作:不要担心,冷静地找出错误来源。崩溃是正常开发过程的一部分。但是,如果像我们在本教程中所做的那样有条理地工作,您可以节省大量的时间和头疼事。
参考文献
Mathôt, S., Schreij, D., & Theeuwes, J. (2012). OpenSesame: 一个为社会科学设计的开源图形实验构建器。行为研究方法,44(2), 314-324. doi:10.3758/s13428-011-0168-7
Treisman, A. M., & Gelade, G. (1980). 注意的特征整合理论。认知心理学,12(1), 97–136. doi:10.1016/0010-0285(80)90005-5