PsychoPyで円を描くように刺激を動かす。複数回のキー押し。キーを押したときの刺激の位置を記録。

この記事では、PsychoPyを使って、円を描くように刺激を動かす方法を解説します。
実験の参加者は、刺激が動いていあいだ、何度でもキーを押せることにします。
そして、キーを押したときの刺激の位置を記録します。
刺激は５秒間呈示されると消えるものとします。

今回のプログラムは、Builderだけでは作れない（作りにくい）例です。Codeコンポーネントを使いますが、それでもまだ作りにくく、Coderを使用します。（注： CodeコンポーネントとCoderは別物です）

刺激（今回の例では青い正方形とします）は、注視点を中心とした円周上を動きます。
![stim_circle1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/927a8e8d-0c0f-e4c7-ef17-ab52b2eeedec.jpeg)

この例では、簡単な三角関数を使います。以下の図の意味が分からないときは、三角関数についてまずは復習してください。
半径がrの円を正方形が移動するとき、正方形の座標は (x, y) = (r * cos(θ), r * sin(θ)) となります。

![stim_circle2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/b136d23a-7f3f-80ea-1127-8c775fccb304.jpeg)

以下の図を参考にして、code, polygon, keyboardの３つのコンポーネントを配置してください。
<img width="880" alt="スクリーンショット 2019-11-14 10.58.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/db27ad10-7043-26a0-a6a5-10aa5bee3804.png">

# polygonコンポーネント

変更箇所は次の通りです。
<img width="667" alt="スクリーンショット 2019-11-14 11.02.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/b86fb333-6d1e-9cad-7f5b-416140452dcf.png">

色を青にするには「高度」タブを開きます。手動で、`$[-1, -1, 1]` と打ち込んでもよいですが、数字が書いてある場所を右クリックすると色を選択することができます。
<img width="667" alt="スクリーンショット 2019-11-14 11.02.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/7468a010-0c95-16d1-f163-129c05e4f1a7.png">

#keyboardコンポーネント

変更箇所は次の通りです。

<img width="709" alt="スクリーンショット 2019-11-14 11.06.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/eae26e4d-a2c1-235c-3df6-a37e8290995b.png">

今回の実験では、何回もキー入力ができるので「Routineを終了」のチェックを外し、記録を「全てのキー」とします。

#Codeコンポーネント

「フレーム毎」のタブを開き、次のコードを入力します。

```python
radius = 5 # deg
angle_deg_sec = 45 # deg/sec
current_deg = angle_deg_sec * t
polygon.pos = (radius * cos(deg2rad(current_deg)), radius * sin(deg2rad(current_deg)))
```

円の半径（radius）を視角5度（degree）としています。刺激が動く速度（angle_deg_sec）は45度/秒です。つまり、１秒で45度進みます。

**t**は、PsychoPyが準備している変数で、Routineの開始時からの時間（秒）になります。
>距離（今回の例では移動角度） = 速度 x 時間

という関係を思い出してくださいね。
`polygon.pos =`の行で、polygonの座標を決定しています。
以上の作業をフレーム毎、つまり、画面が書き換わるタイミングごとに実行されるようにします。

ここまでの作業はBuilderで可能です。プログラムを実行してみてください。青い正方形が円上を移動するはずです。

# Coderを使って、キーが押されたときの刺激の位置を記録しよう

まずはBuilderで実行して、刺激が移動しているあいだに、３回ほどスペースキーを押してみてください。
その後、dataフォルダにある csvファイルを開いてください。
<img width="812" alt="スクリーンショット 2019-11-14 11.22.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/59369a36-5836-9504-d8a0-1f1fd22d2e8e.png">

上の図のようにスペースキーが３回押されて、それぞれの反応時間が記録されていることが分かります。
これらのデータと同じように、スペースキーが押されたときの刺激の位置を記録してみましょう。
刺激の位置は(x, y)の座標ですが、説明を簡略化するため、角度（θ）を記録することにします。角度が分かれば、(x, y)の座標も計算可能です。

Coderを開くには、下の図の赤枠をクリックします。
<img width="880" alt="スクリーンショット 2019-11-14 10.58.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/8267c772-c62a-e27d-4873-66d9e69b6b4e.png">

**重要な注意点です。** Coderで変更したあとにBuilderで変更を加えると、Coderの変更が上書きされてしまうため、まずはファイルを別名で保存するようにしましょう。

`key_resp.keys`や`key_resp.rt`のように記録したいので、これらが記述されているところを見つけ出します。なお、ここからの作業は、Coderよりも、[Visual Studio Code](https://code.visualstudio.com/)や[Atom](https://atom.io/)などのテキストエディタを使うほうが便利だと思います。

```python
# update component parameters for each repeat
key_resp.keys = []
key_resp.rt = []
```
この箇所を見つけてください。これはデータを記録するためのリストを作るためのコードです。初期化とも言います。
角度のデータを保存するため、初期化のコードを追加してみましょう。

```python
# update component parameters for each repeat
key_resp.keys = []
key_resp.rt = []
key_resp.angle = []
```

次に、以下のコードを探してください。

```python
key_resp.keys.append(theseKeys.name)  # storing all keys
key_resp.rt.append(theseKeys.rt)
```
これは**キーを押すたびに実行される箇所**で、どのキーが押されたか、そしてその反応時間を保存しています。ここで角度のデータも同じように保存しましょう。
保存したい角度のデータは、`current_deg`になります。どうして`current_deg`を保存するのか分からないときは、上で説明したCodeコンポーネントのところを読み返してみてください。

次のように追記します。注意点として、インデントを上の２行とそろえてください。Pythonではインデントが重要な意味を持ちます。

```python
key_resp.keys.append(theseKeys.name)  # storing all keys
key_resp.rt.append(theseKeys.rt)
key_resp.angle.append(current_deg)
```

最後に、以下のコードを探してください。

```python
thisExp.addData('key_resp.keys',key_resp.keys)
if key_resp.keys != None:  # we had a response
    thisExp.addData('key_resp.rt', key_resp.rt)
```

Routineの終了時に、すべてのキーの情報と、その反応時間をCSVファイルに保存しています。２行目にif文がありますが、これは「キーが一度でも押されていたら」という意味です。一度もキーが押されないこともあり得るのでこのような書き方になっています。

一度でもキーが押されていたら、反応時間と一緒に、角度が保存されるようにしましょう。

```python
thisExp.addData('key_resp.keys',key_resp.keys)
if key_resp.keys != None:  # we had a response
    thisExp.addData('key_resp.rt', key_resp.rt)
    thisExp.addData('key_resp.angle', key_resp.angle)
```

以上で変更は終わりです。

実行するときは、Builderの実行ボタンではなく、Coderの実行ボタンを押します。
角度のデータが保存されていることを確認してください。