Processingとゲームコントローラを接続しよう!
Processingアドベントカレンダー2020 4日目です.
今回はGame Control Plusライブラリを紹介します.
このライブラリを用いることで,Xbox・PS4・Joyconなどのゲームコントローラを使って,Processingで作成したゲームをプレイすることができます.
目標
コントローラのボタンを押して値を取得

環境
サンプルコード
解説
Game Control Plusライブラリで取得できる入力の種類は,Button, Slider, Hatの三種類があります.
| 入力 | 説明 |
|---|---|
| Button | Aボタンやスタートボタンなど,押しボタン |
| Slider | ジョイスティックや十字キーのような移動で使うボタン |
| Hat | ジョイパッドやジョイスティックの上部などにある特別な入力 |
入力の説明
Button
Buttonは押しボタンを扱うクラスです.pressed()を使うとそのボタンを押しているかどうか検出してくれます.
//テストコード void draw() { for (int i = 0; i < device.getNumberOfButtons(); i++) { ControlButton button = device.getButton(i); if (button.pressed())println(i);//押したボタンの数字を表示 } }
Slider
Sliderはジョイスティックや十字キーを扱うクラスです.-1.0から1.0の範囲で値を返します.
//テストコード void draw() { ControlSlider sliderX = device.getSlider("x"); ControlSlider sliderY = device.getSlider("y"); print("x: " + (int)sliderX.getValue()); print(", "); println("y: " + (int)sliderY.getValue()); }
Hat
Hatはジョイパッドやジョイスティックの上部などにある特別な入力です.
今回使ったコントローラにはHatに対応する入力はありませんでした.
↓JoyConの場合,スティックボタンの取得に使えるらしいです.
checkControlType()
コントローラについている入力がどの種類なのか調べるのがサンプルのcheckControlType()です.これを使えばPS4のコントローラのような押しボタンや十字キー,スティックが複数ついているデバイスでも,どの種類の入力がどの値に対応しているか調べることができます.
今回は以下のような結果が出ました.(明らかに入力の数が実際のコントローラにあるものより多いのはなぜ……?)

作品
今回のライブラリを用いて自分は「ソーシャルディスタンスでもできる乳首当てゲーム」を開発しました!
ソーシャルディスタンスを保ちながら乳首当てゲームができるようにい「デジタル乳首当てゲーム」を作成しました!
— ZAWA WORKS (@Zawa_works) 2020年11月30日
濃厚接触することなく乳首当てゲームをすることができます! pic.twitter.com/Ss4QnjtLYp
おわりに
Processingで作成したゲームをゲームコントローラで操作したいと昔から思ってました.
このライブラリの存在は早く知りたかったですね……
より詳しい内容は参考記事に書いてあります!ぜひご参照ください!
この記事でたくさんのProcessingユーザの方々に知ってもらえれば幸いです!
参考記事
Processingで機械学習!? (RunwayML + Processing)
はじめに
Processingアドベントカレンダー2020 3日目の記事です.
RunwayMLとは,クリエイターのための機械学習ツールです.すでに複数の学習モデルが用意されており,それらを使うことで機械学習アート(勝手に命名)を創作できます.
このRunwayMLの良いところは,その出力結果をProcessingに送信できるという点です.すでにProcessingのサンプルが用意されており,今すぐ機械学習を用いたプログラム作成ができます.今回はRunwayMLと通信するためのセットアップの方法とサンプルの一部を紹介します.
目標
試した環境
- Mac OS Catalina 10.15.7
- RunwayML 0.15.0
- Processing 3.5.4
セットアップ
1. RunwayMLソフトウェアをダウンロード
RunwayMLはWeb上で体験することが可能です.しかし,Processingとの通信には専用ソフトウェアをダウンロードする必要があります. ダウンロードは以下のリンクから行うことができます. runwayml.com
2. RunwayMLにサインアップ
RunwayMLのソフトウェアを入れたらサインアップします
3. Processingにライブラリをインポートする
今回使うサンプルを使うにはRunwayライブラリとOSCライブラリが必要です.
「ライブラリを追加」からそれらを検索し,インストールしてください.

サンプルを動かしてみよう!
今回はポーズキャプチャするプログラムを使ってみます.
libraries > examples > OSC > PoseNetWebcam > PoseNetWebcam.pdeです
場所がわからない方は以下のリンク先にあるコードをコピペしてください.
1. RunwayMLでポーズキャプチャモデルを探す

2. ポーズキャプチャモデルを起動する
モデルを選択すると以下の画像のような画面が表示されます.
右下にあるRunボタンを押すとポーズキャプチャが実行します.

3. PoseNetWebcam.pdeを実行する
そしてPoseNetWebcam.pdeを実行します.
するとProcessingの実行画面にポーズキャプチャの様子が描画されます.
コードの解説
PoseNetWebcam.pde起動時にProcessingとRunwayMLはこんな感じで通信しています.
runway.query()とrunwayDataEvent(),runwayInfoEvent(),runwayErrorEvent()はRunwayライブラリに標準搭載している関数たちです.
基本的にrunwayDataEvent()で,RunwayMLが送信した基本的なデータ(今回は体パーツの座標)を非同期で取得しています.
この命令は他の学習モデルにも適用することができます.
他のサンプル
他には顔のパーツ(FaceLadmark)を取得することもできます.
また,画像を写実的に表現することもでき,それは以下の記事で細かく解説していました.
おわりに
自分は機械学習に詳しくないので,このような作品を思いついても制作することができませんでした.RuwayMLによってProcessingにもその技術を応用できるようになり,創作するコンテンツの幅が広がるような気がします.
この記事を読んだ方々もぜひ機械学習を用いた創作にチャレンジしてみてください!
Unity+Vuforia: ARで画像と動画を表示する
今回はUnity とVuforiaを使ってマーカー上に画像と動画の表示方法をまとめました!
目標
目次
環境
- Mac Catalina ver10.15.3
- Unity 2019.3.9.f1
- Vuforia Engine 9.0
事前準備
教材
UnityとVuforiaのセットアップ
UnityとVuforiaの準備は以下の記事に書いてあるので,こちらの準備が終わってから進んでください.
画像の表示
1. Assetsの中にimagesフォルダを作りcat.jpegをいれる

2. catのTexture TypeをSprite (2D and UI)にする

3. catをImageTargetの子オブジェクトにする

4. catの角度を調整して,マーカーと動画が平行になるようにする

5. 再生する
動画の表示
1. Assetsの中にvideosフォルダを作りcat.mp4をいれる

2. videosの中にRender TextureとMaterialを作成する

3. testMaterialにtestTextureを貼り付ける

4. ImageTarget の下に Quad を作成する

5. QuadにVideoPlayerを追加する

6. Quadにcat.mp4, testTexture, testMaterialを追加する

7. Quadの角度を調整して,マーカーと動画が平行になるようにする

8. 再生する
おまけ: 動画の縦横比を変える
先ほどの手順だけでは,動画とQuadの縦横比があっていないため,黒い部分ができてしまいます.
ここではQuadの縦横比を動画と同じにして,黒い部分ができない方法を教えます.
1. AssetsにGetVideoAspectRatioEditor.csを追加する

2. VideoPlayerを右クリックしてGet Aspect Ratio for Meshを押します

3. Render ModeをMaterial Overrideにする

4. 再生する
参考資料
Processing: 円と円の交点を求めよう
目次
はじめに
目標
今回は円と円の交点を求めてみます
サンプルコード
動画のコードは以下のようになります
記事の後半では、この中のgetCirclesCrossPoints()という関数について解説していきます
解説
数式で考えてみる
2つの円の方程式を用いて考えます
円の中心座標が
で半径が
の円の方程式
中心座標が
で半径が
の円の方程式
このときをすると
この(3)は円の交点を通る直線の方程式になっています
あとは(3)の直線と(1)または(2)の円との交点を求めれば、円と円の交点を求めることができます
直線と円の交点の求め方は以下の記事を参考にしてください
コードにしてみよう
とおきます。それを円と直線の交点を求める関数に代入すれば円と円の交点が返ってくるようになっています。
//円と円の交点を取得する関数 PVector[] getCirclesCrossPoints(float x1, float y1, float r1, float x2, float y2, float r2) { float a = 2*(x2 - x1); float b = 2*(y2 - y1); float c = sq(x1)-sq(x2)+sq(y1)-sq(y2)+sq(r2)-sq(r1); return getLineCircleCrossPoints(a, b, c, x1, y1, r1); } //直線(ax + by + c = 0)と円(中心座標(circleX, circleY), 半径 r)の交点を取得する関数 PVector[] getLineCircleCrossPoints(float a, float b, float c, float circleX, float circleY, float r) { //中身はこちらの記事を読んでください //http://zawaworks.hatenablog.com/entry/2019/12/04/012717?_ga=2.20645616.952110635.1578237427-1521756013.1576935957 }
Processing: 2番目の画面でクラスを使うときの注意
Processingは通常画面が一つしか作成されませんが,PAppletクラスを継承するクラスを作れば画面を複数作成することができます.

問題
新しくTestクラスを作りました.これは画面の背景を黒くするクラスです.
このクラスをSecondWindowのdraw()に書きましたが,背景が黒くなりません.

解決方法
これは以下のようにSecondWindow内にPApplet型の変数を用意して,applet.background(0)とすれば2番目の画面の背景色を変更することができました.

今回のコードは,すべてこちらにあります. 2番目の画面内でクラスを使うときの注意 · GitHub
解説
PAppletクラスは画面を作成し,setup()やdraw(),background()を管理しているクラスです.
Processingでは起動するとPAppletクラスが呼ばれて画面を作成し,その中身はsize()やbackground()などで変更されます.
SecondWindowクラスはそれを継承して,新しい画面を作成し元のPAppletクラスと同じ機能(setup()やbackground()など)を備えています.
background()やellipse()などの命令を自分の作ったクラス(今回はTestクラス)で呼ぶと,それは1番目のPAppletの命令を呼んでしまうので,2番目のPApplet(SecondWindow)の中身には何も変化が起きません.
これは,クラスの内で呼ばれるbackground()などの命令が,どのPAppletの命令なのかを指定することで解決することができるのです.
ちなみに
クラス内でmouseXやmouseYとしても,1番目の画面のマウス座標を取得してしまうので,これもapplet.mouseXのように指定する必要があります.
class Test { PApplet applet; Test(PApplet _applet) { applet = _applet; } void display() { applet.background(0); applet.fill(255); applet.ellipse(applet.mouseX, applet.mouseY, 20, 20); } }
参考文献
Processing: 2点を通る直線と円の交点を求めよう
目標
今回は以下のようにある2点を通る直線と円の交点を求めるgetCrossPoints()という関数を作ります.
//交点を取得する関数 PVector[] getCrossPoints(float x1, float y1, float x2, float y2, float circleX, float circleY, float r) { //ax + by + c = 0 の定数項 float a = y2-y1; float b = x1-x2; float c = -a*x1-b*y1; //円の中心から直線までの距離 //mag(a, b) = √a^2+b^2 float d = abs((a*circleX+b*circleY+c)/mag(a, b)); //直線の垂線とX軸と平行な線がなす角度θ float theta = atan2(b, a); if (d > r) { return null; } else if (d == r) { PVector[] point = new PVector[1]; //場合わけ if (a*circleX+b*circleY+c > 0)theta += PI; float crossX = r*cos(theta)+circleX; float crossY = r*sin(theta)+circleY; point[0] = new PVector(crossX, crossY); return point; } else { PVector[] crossPoint = new PVector[2]; float[]crossX = new float[2]; float[]crossY = new float[2]; //alphaとbetaの角度を求める float alpha, beta, phi; phi = acos(d/r); alpha = theta - phi; beta = theta + phi; //場合わけ if (a*circleX+b*circleY+c > 0) { alpha += PI; beta += PI; } //交点の座標を求める crossX[0] = r*cos(alpha) + circleX; crossY[0] = r*sin(alpha) + circleY; crossX[1] = r*cos(beta) + circleX; crossY[1] = r*sin(beta) + circleY; for (int i = 0; i < crossPoint.length; i++) crossPoint[i] = new PVector(crossX[i], crossY[i]); return crossPoint; } }
動画のプログラムは,こちらを実行すれば体験できます.
2点を通る直線と円の交点の求め方
ここからは直線と円の交点をどうやって求めているのか解説したいと思います
直線と円の交点があるかを確認
まず「直線と円が交わっているのか?」を確認しないといけません.
確認の方法は至って簡単で,直線と円の中心の距離をdとしたときに,r < dだったら直線が円から離れているので交わっていません.
r = dのときはちょうど円と直線が接しています.そして,r > dだったら直線と円は2つの交点を持ちます.
dの求め方
dは点と直線の距離の公式を使うことで,求めることが可能です.
点(
,
)と直線(
)の距離の公式
コードにすると……
//円の中心から直線までの距離 //mag(a, b) = √a^2+b^2 float d = abs((a*circleX+b*circleY+c)/mag(a, b)); if (d > r) { //交点が0のとき }else if (d == r) { //交点が1つのとき }else{ //交点が2つのとき }
直線と円が接しているとき
まず直線と円が接しているときに交点の座標をどのように求めるのかを解説していきます.
交点の座標の求め方
例えば円の中心が原点だったときのことを考えます.
円と直線の距離をdとし,直線の垂線がX軸と平行な線となす角をθとすると交点の座標は
float x = r*cos(theta); float y = r*sin(theta);
となります.
円の中心が(centerX, centerY)だった場合は平行移動させればいいので
float x = r*cos(theta) + centerX; float y = r*sin(theta) + centerY;
とすれば,交点の座標を求めることができます.
θの求め方
直線と円との距離は,円の半径と同じであるため求めるのが簡単です.一方,θの値はどうやって求めればいいのでしょうか.
円と接する直線の方程式をax + by + c = 0とすると,その垂線の傾きはb/aとなることが知られています.
そして,傾きはtan()を使って表すこともでき,tan(θ) = b/aとなります.
これを満たすθを求める命令がatan2()です.
float theta = atan2(b, a);
これでθの値を求めることができました.
atan2()の罠
先ほど紹介したθの求め方だけだと,正しい交点の座標を求めることはできません.それは.θの値を直線の方程式のaとbを使うことで角度を求めているので,その値に円と直線の位置関係が反映されていないからです.
これは,a*centerX + b*centerY + cが0より大きいか小さいかで場合わけすることで対処することができます(理由は後日記述します).
float theta = atan2(b, a); if (a*circleX+b*circleY+c > 0) theta += PI;
このようにa*circleX+b*circleY+c > 0のときにθに180°足すと,円と直線の位置関係が反映されて,正しい交点の座標を求めることができます.
コードにすると……
これまで説明したことをコードにすると以下のようになります.
float theta = atan2(b, a); //場合わけ if (a*circleX+b*circleY+c > 0)theta += PI; float crossX = r*cos(theta)+circleX; float crossY = r*sin(theta)+circleY;
直線と円の交点が2つのとき
次に直線と円の交点が2つのときに交点の座標をどのように求めるのかを解説していきます.
交点の座標の求め方
直線と円が接しているときと同じ考え方で求めます.円の中心と交点を通る直線とX軸と平行な線のなす角をそれぞれα, βとすると
float crossX1 = r*cos(alpha) + centerX; float crossY1 = r*sin(alpha) + centerY; float crossX2 = r*cos(beta) + centerX; float crossY2 = r*sin(beta) + centerY;
として求めることができます.
αとβの求め方
直線の垂線AがX軸と平行な線となす角をθとし,「円の中心を通る垂線A」が「円の中心と交点を通る直線B, C」となす角をそれぞれγ,δとすると
float alpha = theta - gamma; float beta = theta + delta;
として求めることができます.
γ,δの求め方
「円の中心と交点の直線」と「円の中心から直線の垂線」を引きます.
そしてできた直角三角形を見てみると,それぞれ合同であることが分かります.
つまり,φ = γ = δとなるφの値を求めるだけで済みます.
直角三角形であることを利用すればφを求めることは簡単です.
r, d, φは,cos(φ) = d/rという関係があります.
これはatan()のときと同じようにacos()という命令を使えば,求めることが可能です.
float phi = acos(d/r);
これでphiの値を求めることができました.
コードにすると……
これまで説明したことをコードにすると以下のようになります.
(atan2()の場合わけも忘れないように!)
phi = acos(d/r); alpha = theta - phi; beta = theta + phi; //場合わけ if (a*circleX+b*circleY+c > 0) { alpha += PI; beta += PI; } //交点の座標を求める crossX[0] = r*cos(alpha) + circleX; crossY[0] = r*sin(alpha) + circleY; crossX[1] = r*cos(beta) + circleX; crossY[1] = r*sin(beta) + circleY;
おわりに
今回は,2点を通る直線と円の交点の求め方について解説しました.この記事の知識をプログラム作品に応用してもらえたら幸いです.
Python : pandasでdf['hoge']が使えないときの対処法
はじめに
自分の研究でデータ分析をするためにpandasを使い始めました.pandasはcsv(tsv)ファイルをdf = pd.read_csv('hoge.csv')(またはread_table)で読み込んで,df['hoge'] という形で指定したカラム(列)を配列として抽出できます.しかし,あるときなぜかそれができませんでした.そこで,そのときの対処法をここでまとめました.
今日使うデータの説明
今回使うデータは以下の通りです.
hoge.csv
| id | sex | name |
|---|---|---|
| 0 | M | Yuta |
| 1 | F | Rikka |
| 2 | M | Sho |
ちなみに表の一番上にあるid, sex, nameのタイトルが書かれているカラムのことをヘッダーといいます.
コード
df pd.read_csv('hoge.csv') print(df['id']) print(df['sex']) print(df['name'])
これがうまくいけば以下のような結果が返ってきます.
実行結果
[0, 1, 2] [`M`, `F`, `M`] [`Yuta`, `Rikka`, `Sho`]
read_csvかread_tableかを確認
pandasには 'read_csv' と read_table という二つの命令があります.これらは前者がcsvファイルを読み込むときに使い,後者が read_table ファイルを読み込むときに使います.
| read_csv | read_table |
|---|---|
| csv専用 | tsv専用 |
そのため,read_csv('hoge.tsv') や read_table('hoge.csv') とすると,ヘッダーが読み込めなくなり,df['id']と呼んでも[0, 1, 2] の配列は返ってきません.
ちゃんとread_csv('hoge.csv')かread_table('hoge.tsv')になっているか確認しましょう.
index_col=0 またはheader=0を入れる
たとえばread_csv('hoge.csv')をやってもdf['id']が出ない場合,hoge.csvが「ヘッダーがないcsvファイル」として読み込まれている可能性があります.csvファイルには「ヘッダーあり」の場合とありますと「ヘッダーなし」の場合があります.df['id']でカラムを抽出できるのは,ヘッダーありcsvの場合なので,ヘッダーがないcsvだとできません.このとき,ヘッダーありのcsvとして読み込むためには,以下のように書きます.
df = pd.read_csv('hoge.csv', index_col=0) df = pd.read_csv('hoge.csv', header=0)
index_col = 0は「0行目をindexとして使いますよ」という変数で,header=0は「0行名をヘッダーにするよ」という命令です.これを指定あげることで,「0行目がヘッダーのcsvファイル」として読み込むことができます.