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ファイル」として読み込むことができます.