解説4
ここでは、実験部分(刺激を呈示して、実験参加者の反応を取得する部分)について説明をします。
for i=1:length(order)
length(order)は練習試行と本試行をあわせた19になります。for文の中身を19回繰り返すことになります。
if i==ntrain+1 %before the first test trial
DrawFormattedText(expWin, 'Are you ready for the experiment?\n(Press any key to start experiment)', 'center', 'center');
Screen('Flip', expWin);
KbWait([], 3);
end
ntrainは3ですから、iが4になったとき(練習試行が終わったとき)に、「Are you ready for the experiment?」と表示させます。iが1から3の間は練習試行、iが4から19の間は本試行ということですね。KbWaitを使って実験参加者からのキー押しがないと先に進まないようになっていることにも注意してください。
Screen('DrawTexture', expWin, fixcross,[],[mx-10,my-10,mx+10,my+10]);
Screen('DrawTexture')をご参考ください。fixcrossの元になっている行列FixCrのサイズは20×20でしたね。だから、中心座標(mx, my)から10をプラスマイナスしたところにfixcrossを表示すると画面の中央に凝視点が呈示されるわけです。ですが、DrawTextureの動作としてデフォルトでテクスチャを画面の中心に呈示するので、[mx-10,my-10,mx+10,my+10]を省略しても同じ動作になります。
それから、この段階ではまだ画面上に凝視点は見えていないことに注意してください。画面に見えない画用紙に凝視点を書き込んでいるだけ、とイメージしてもらうとよいでしょう。
tfixation = Screen('Flip', expWin);
上記のScreen('DrawTexture', expWin, fixcross) で描いておいた画用紙を、バンッと画面に表示させます。詳しくはScreen('Flip')をご覧ください。凝視点が呈示された時間をtfixationとして記録しておきます。
ここからは刺激の描画に関する内容になります。下のほうに図を用意していますのでそちらも参考にしてください。
stimsize=rand*100+50;
刺激(ミュラーリヤーの矢羽)を作成するときの基準となる変数です。randは0から1の範囲の小数をランダムに返しますから、stimsizeの最小値は50(rand=0のとき)、stimsizeの最小値は150(rand=1のとき)となります。
hll=[stimsize, stimsize*0.9];
hllは、half line lengthの略です。hllは1行2列の行列(行ベクトル)です。1つ目の要素の90%が2つ目の要素になります。1つ目の要素と2つ目の要素の違いは、10%ですね。
hos=stimsize*.8;
headoffsetです(hosがどこを表すかは下図を参照のこと)。stimsizeと8の間に、アスタリスクとピリオドがあることに注意してください。これはstimsizeを0.8倍するという意味です。0.8という数字そのものに意味はありません。stimsizeは50から150の数値ですから、hosは40から120の数値になります。
voff=stimsize*1.5;
水平方向の2本の矢羽が、垂直方向に並んで呈示されるのですが、その垂直方向の距離です。voffがどこを表すかは下図を参照のこと。1.5という数字そのものに意味はありません。stimsizeは50から150の数値ですから、voffは75から225の数値になります。
l_hll = hll(condtable(order(i),(1)));
condtableは実験条件を表す行列でしたね。具体的に考えて見ましょう。order=[5, 7, 12, 9, ・・・]、i=2だったとします。order(2)は7ですね。つまりcondtable(7, 1)となります。ではその値をcondtableを見て確認しましょう。condtableの7行目の1列目の値です。2ですね。ということで以上の例ではl_hll = hll(2)となり、hllの2番目の要素ということになります。
l_head = condtable(order(i),(3));
l_hllの場合と同様に考えてみましょう。i=2のときorder(2)=7なので、condtable(7, 3) = -1 ですね。l_headはどの条件でもcondtableの3列目の値と言うことですから、プラス1かマイナス1のいずれかになります。
l_hos=hos*l_head;
l_headはプラス1かマイナス1なので、l_hosは、プラスhosかマイナスhosです。下図において、l_hosとhosの長さ(絶対値)は厳密には同じになっていませんが、実際には同じになります。ちなみにu_hosの長さも同じです。
u_hll = hll(condtable(order(i),(2)));
u_head = condtable(order(i),(4));
u_hos=hos*u_head;
l_hll, l_head, l_hosと考え方は同じです。condtableの2列目のデータがu_hllに、condtableの4列目のデータがu_headに使用されていることに注意してください。
Screen('DrawLine', expWin , 0, mx-l_hll, my-voff, mx+l_hll, my-voff, lw); %上の水平線分
if l_hos~=0
Screen('DrawLine', expWin , 0, mx-l_hll, my-voff, mx-l_hll+l_hos, my-voff+hos/2, lw); %線分①
Screen('DrawLine', expWin , 0, mx-l_hll, my-voff, mx-l_hll+l_hos, my-voff-hos/2, lw); %線分②
Screen('DrawLine', expWin , 0, mx+l_hll, my-voff, mx+l_hll-l_hos, my-voff+hos/2, lw); %線分③
Screen('DrawLine', expWin , 0, mx+l_hll, my-voff, mx+l_hll-l_hos, my-voff-hos/2, lw); %線分④
end
Screen('DrawLine', expWin , 0, mx-u_hll, my+voff, mx+u_hll, my+voff, lw); %下の水平線分
if u_hos~=0
Screen('DrawLine', expWin , 0, mx-u_hll, my+voff, mx-u_hll+u_hos, my+voff+hos/2, lw); %線分⑤
Screen('DrawLine', expWin , 0, mx-u_hll, my+voff, mx-u_hll+u_hos, my+voff-hos/2, lw); %線分⑥
Screen('DrawLine', expWin , 0, mx+u_hll, my+voff, mx+u_hll-u_hos, my+voff+hos/2, lw); %線分⑦
Screen('DrawLine', expWin , 0, mx+u_hll, my+voff, mx+u_hll-u_hos, my+voff-hos/2, lw); %線分⑧
end
刺激の描画です。DrawLineの使い方は難しいものではなく、開始座標と終了座標を指定して線分を描きます。lwは線分幅です。コメントにつけている番号は、下図に対応しています。
l_hosまたはu_hosが0になることはないのですが、もし0であれば、if文の中は同じコマンドの繰り返しになるのでif文を使っているのでしょう。
※l は lower、uは upper の略だと思われますが、実際には l から始まる変数が上の刺激に、u から始まる変数が下の刺激に使われています。ただし、座標系は左上を(0, 0)として右に行くほど下に行くほど値は大きくなるのでそういう意味ではlとuは正しいのかも。
telapsed = Screen('DrawingFinished', expWin, [], 1);
コメントを読むと不要なコマンドらしいですが、"Apparently this can improve performance"とも書いてあるので注意が必要かも。
[VBLTimestamp, StimulusOnsetTime, FlipTimestamp]=Screen('Flip', expWin, tfixation + 0.5);
FlipについてはScreen('Flip')を参照のこと。すぐには刺激を画面に呈示させずに、凝視点を呈示したとき(tfixation)から0.5秒後に呈示するようにしています。0.5秒間は凝視点が呈示されているということになります。
tic;
ticはtocとセットで使います。ticを呼び出したときはストップウォッチのスタートを押したときと思ってください。tocを押すとそのストップウォッチを止めて経過時間を返します。
ここでticを呼び出しているのは、Matlabで時間を測るコマンド(tic & toc)と、PTBでの時間測定の誤差を調べるためのようです。実際の実験では後者(PTB)だけで十分なはず。
[resptime, keyCode] = KbWait;
実験参加者の回答(s or d)を待ちます。キーを押すと押した時間がresptimeに、キーの情報がkeyCodeに返ります。KbWaitを参照のこと。
MLrt=toc;
上記のticを参照のこと。Matlabのコマンドを使って反応時間を測っています。
rt=resptime-StimulusOnsetTime;
実験参加者が回答した時間(resptime)から刺激の呈示時間(StimulusOnsetTime)を引いて反応時間を測っています。これがPTBのコマンドを使った反応時間の測定方法です。
cc=KbName(keyCode);
実験参加者が押したキー(sかdか、それとも全く関係ないキー)がccに代入されます。
if isempty(cc) || strcmp(cc,'ESCAPE')
break;
ccが空っぽの状態であれば実験を中断します。また、実験参加者がESCキーを押した場合も実験を中断します。A | B と A || B は、似て非なるものです。前者は論理和と呼ばれ、後者はショートサーキット論理和と呼ばれます。
elseif ~any(strcmp(cc,'s') || strcmp(cc,'d'))
anscorrect = 66;
anyの前にチルダがついているのでanyの結果が反転します。anyは()の中が0であれば0を返します。つまり実験参加者がsまたはdのキー以外を押したときはanscorrectに66を代入します。
※66はsubIDのデフォルト値なので、ここはanscorrect = subID;のほうがよいような気がします。
elseif u_hll==l_hll && strcmp(cc,'s')
anscorrect = 1;
2本の水平線分の長さが等しく、回答がsであったら正解なので、anscorrectに1を代入します。
elseif u_hll~=l_hll && strcmp(cc,'d')
anscorrect = 1;
2本の水平線分の長さが異なっていて、回答がdであったら正解なので、anscorrectに1を代入します。
else
anscorrect = 0;
end
上記以外であれば不正解なのでanscorrectに0を代入します。
results(i,:) = [subID, i-ntrain, order(i), l_hll*2, l_head, u_hll*2, u_head, u_hll/l_hll, anscorrect, rt, MLrt];
実験結果を行列resultsに代入します。行列resultsの各行は1試行に対応して、呈示された順番で記録されます。1列目は実験参加者番号、2列目は、本試行の1試行目を1とするためにntrainを引いています。3列目はcondtableの何行目の実験条件か、4列目は上の水平線分の長さ(上図を参考)、5列目は上の刺激の矢羽の向き、6列目は下の水平線分の長さ、7列目は下の刺激の矢羽の向き、8列目は下の水平が上の水平線分の何倍になっているか、9列目は回答が正解かどうか(1が正解)、10列目はPTBで測定した反応時間、11列目はMatlabのコマンドで測定した反応時間。
if anscorrect ~=1
beep;
end
回答が間違っていたらビープ音を鳴らします。
DrawFormattedText(expWin, 'Press any key to start next trial', 'center', 'center');
Screen('Flip', expWin);
KbWait([], 3);
次の試行を促すメッセージを呈示します。KbWaitがあるので実験参加者が何かのキーを押すまでは次の試行は始まりません。
解説4はここまで。