うつろぐ

文章

パロディ音ゲーにまつわる二次創作について

CCC Advent Calendar 2018 - Adventarの13日目の記事です。大遅刻です。

大した内容量ではないですが、音楽ゲームをプレイしている人全員に向けた記事です。パロディ音ゲー(シミュレーションソフト)を遊ぶうえで注意すべきことはしっかり書いた上で、自信のない憶測も書いています。

パロディ音ゲー(シミュレーションソフト)って?

音楽ゲーム(主にアーケード)を模したゲーム・ソフトなどです。クローンソフトとも言われます。譜面制作ツールもあって自分で音源を設定して譜面を作って遊べることが多く、譜面制作を目的としてこの類のソフトを使う場合も少なくありません。例として

  • 太鼓さん次郎
  • BMS
  • K-Shoot MANIA
  • Seaurchin

などがあります。

まず、パロディ音ゲーは法律的にはアウト

当然ですが、本家音楽ゲームを許可なく模しているため、楽曲の著作権云々より前にパロディ音ゲーの存在自体が法律的にはアウトです。
なので、同じ構図を取っている他の多くの二次創作と同様に、「二次創作として黙認される」という立場を取っていけばよいと思います。この観点から言うと、本家楽曲に関する創作譜面の制作も基本的には「二次創作」の態度を取って良いと思われます。
但し、ソフト自体に規約が設けられている場合があるので注意しましょう。

ソフトの規約に注意

最初に挙げた4つに関して、規約の有無を見ていきます。

  • 太鼓さん次郎

本家譜面の配布・設置は禁止。それ以外(本家楽曲の創作裏譜面含む)に関する記述はない。
因みに太鼓さん次郎の諸々の情報はソフトの制作者ページではなく「太鼓さん次郎 交流Wiki」で行われている様子。(そもそも制作者ページが見当たらない...)
ガイドライン(詳細) - 太鼓さん次郎交流 Wiki*

そもそもソフトではなくフォーマットなので、共通規約のようなものはありませんが、このフォーマットの由来の関係上、本家楽曲を用いた遊び方は厳禁とされています。種々のBMSイベントもこの考え方に準じています。

  • K-Shoot MANIA

制作者ページに規約あり。本家楽曲・譜面、許可を得ていない他人の楽曲共にNG。
K-Shoot MANIA (ケーシューマニア)

  • Seaurchin

探してみた感じ規約は見つかりませんでした。そもそもこのソフトに限ってはPCを使って実際にプレイすることが出来ないので、「よその楽曲で創作譜面をするツール」という認識なのかも。

尚どんなソフトでも、基本的にはどんな音源でどんな譜面を作ろうが、その譜面や動画を公開したり配布したりしない個人利用ならばOKです。中々そんな限定的な遊び方する人いないだろうけど。

本家楽曲と他人の楽曲?

上で何故「本家楽曲」と「許可を得ていない他人の楽曲」と区別していたかと言うと、パロディ音ゲー全体の傾向として本家楽曲の使用・譜面再現は避けられる傾向が特に強いからです。パロディ音ゲーは二次創作であるので、本家と違う方向性を持たなければいけないという意識があるように思います。これに関しては僕も賛成です。あと、譜面再現に関してはそもそも「創作」ではないので、これに関してはやはり如何なものかと。
本家の音楽ゲームと関係のない他人の楽曲の使用に関しても著作権法を無視しているのは間違いないです。ただまあそれが二次創作であるので、ソフトに規約がない場合に関しては良識やモラルの問題であると思います。その場合、他人の楽曲を使う際良識や優しさがあれば許可を取りに行ったらいいと思う。

規約と意図と界隈

ここまで読んだ人はこう思うかもしれません。「二次創作なんだからソフトの規約を守るのも良識やモラルに依存するし、破っていいんじゃないの?」と。まあぶっちゃけそうなんですが。でも僕としては守るべきだと考える理由があります。以下に関しては僕がソフトの規約を守る理由になります。

迷惑がかかるかどうかって難しい

僕としては「どうしたら本家に迷惑がかからないか」が僕個人で判断できるとはとても思えないので規約に従っているだけの所があります。
K-Shoot MANIAに関しては特に難しくて、曲にエフェクトがかかるので楽曲の改変?とかそのへんに問われる可能性がありそうなんですが、僕そういうの全然わかってないんですよね。僕みたいに何も分からない場合はそのへんを知っていそうな制作者の意図(規約)に従っておいた方が無難です。

規約が界隈の形成を想定している

ソフトに規約がある場合、そのソフトでゲーム・界隈を構成する際に目指している方向性があって、そうしないと本家に迷惑がかかることを想定している可能性があると思っています。(わからないけど)

(一部の人向け)表明と謝罪

この記事は、昔に他のクローンソフトと同じノリでK-Shoot MANIAで創作譜面をしていたら結構な数の人に冷たい目で見られて、その原因を少し前に理解したからその近辺の二次創作について自身の整理の為に纏めてるというだけのものです。規約の存在を知っていて破った訳ではなく、規約の存在を知りませんでした。YouTubeに上げていた本家楽曲の創作譜面は全て非公開にしました。本当にすみませんでした。

以上です。記事内容(特に規約の有無の部分)に誤りがありましたら、教えてくださるとうれしいです。

フッ軽になる為のChrome複数アカウントの使い方

CCC Advent Calendar 2018 - Adventarの8日目の記事です。

PC版のChromeにはアカウントの切り替え機能があり、かなりお手軽にアカウントを作って用途に応じて使い分けられます。僕がやっているフッ軽になる為のChrome複数アカウントの使い方を紹介します。大したことではないので大した記事ではないです。IEの人は取り敢えず正座しててください。

一応:Chromeでアカウント切り替えるやりかた

これは「他に情報纏まってるページ探してくれ~~~」ってしたかったんですが、最近ChromeのUIが変わってる気がしたので、別記事で説明しました。分からなかったら見てね。↓
新UIでのChrome複数アカウントあれこれ - うつろぐ

問題の設定

ブラウザでよくアクセスするサイトって、取り敢えずブックマークに登録することが多いです。
Chromeだとブックマークバーをブラウザの上部に表示させることができます。
f:id:Distorted_Unchi:20181208213023p:plain
でもこれ、すぐブックマークバーで表示できる分を超過してしまいます。表示されてないと、そもそもブックマークしたことを忘れていたりして、フットワークが重くなってしまいます。

...では渡辺謙さん、よろしくお願いします。

ブックマークバーが狭すぎて重要なサイトが一発で開けなーーーーーーーーーーい!!!!!!!!!世界が終わりました...
すぐ開きたいサイト、僕はいっぱいあります。先程の画像のブックマークバーの分の他には、Scrapboxマイナビ、Trello、Slack、Chatwork、Discord、Dropboxなど...
SlackやDiscordはブラウザで開くよりアプリの方が使い勝手が良いって人もいるので無理にブラウザで管理しなくていいですね。ただScrapboxはアプリがないので、そういったサイトはなんとかブラウザで管理したい。ワンクリックで開きたい!

そんな時は専用のChromeアカウントを作ってしまいましょう。

Chrome無限の垢製で解決してみよう

まずは単純に一つのサイトに対して一つのアカウントを作ってみましょう。(「まずは」なのはそうでないのもあるので)ここではScrapbox用を作ります。
アカウント名を入力してアイコンを選んでアカウント作成。
f:id:Distorted_Unchi:20181208214326p:plain
設定を開いて起動時にScrapboxを開くように設定。
f:id:Distorted_Unchi:20181208214852p:plain
おまけにタスクバーにピン留め。
f:id:Distorted_Unchi:20181208215110p:plain
これでワンクリックでScrapboxですぐメモできるようになりました。
これ、まだブラウザを開いていない状態からの比較なら、Scrapbox」がブックマークバーにある状態よりも1クリック分手数が少なくて済みます!
また、「頻繁に必要だけど開くのが億劫なサイト」にこれをやっておくと、ワンクリックで開けるので開く敷居が下がります。僕の場合マイナビとかですね。

「専用のChromeアカウントを作る」と言っても、一つのサイトに一つのアカウントでなくても良いです。僕は「連絡用」というアカウントでSlack、Chatwork、Discordを纏めて見れるようにしています(その代わり開いてないとPCに通知が来ないが...)
f:id:Distorted_Unchi:20181208220216p:plain
纏められそうな共通概念を見つけたら纏めていきましょう。

僕の現在のアカウント数

8個もってます。
f:id:Distorted_Unchi:20181208182608p:plain
「メインのやつ」はブラウジング用です。2つあるのは、「DTMのことで調べものした後にそれを保持したままプログラミングのことで調べものする」みたいなときにウィンドウを分けたかったからです。
「連絡用」はさっき出てきた通りSlack、Chatwork、Discordを纏めてます。
「BGM用」はYoutubeSoundCloudが開きます。
「Good ○○」はそれぞれのサービスを一発で開けるようにするやつ。GoodMyNaviのおかげてマイナビをワンクリックで開いて、ワンクリックで脳をマイナビにできています。
「Unchibox用」はノーコメント。

難点

  • タスクバーが埋まってると厳しい。
  • やっぱChromeなので、アカウント作った分それなりにメモリは食いそう。

以上纏めてみましたが、これ割と原始的なやり方な気がします。その割に皆が同じようにやってる代替手段が見つからないので、「よく使うサイトをワンクリックで開けるように私はこうしてるよ!」とかあったらおしえてください。

新UIでのChrome複数アカウントあれこれ

某記事からの参照用です。

アカウントのつくりかた

Chrome右上の3点メニューの横のユーザーアイコンをクリックしてアカウントメニューを開き、「ユーザーを管理」。

f:id:Distorted_Unchi:20181208183534p:plain
アカウントメニュー
するとユーザー管理ウィンドウが出てくるので、「ユーザーを追加」。
f:id:Distorted_Unchi:20181208182858p:plain
ユーザー管理ウィンドウ
そこからユーザー名を入力してアイコンを選択すれば作成できます。「このユーザーのデスクトップショートカットを作成」はONの方がいいです。後から消せるし。

アカウントの開き方

ユーザーアイコンをクリックして先程のアカウントメニューを開くと、作成したアカウントの名前があるのでそれをクリック。
アカウントの作成時に「このユーザーのデスクトップショートカットを作成」をONにしてるとデスクトップショートカットからも開けます。
f:id:Distorted_Unchi:20181208183445p:plain

アカウントの消し方

ユーザー管理ウィンドウのアカウントのアイコンにカーソルを合わせるとアイコン右上に三点メニューが出てくるので、クリック→「このユーザーを削除」。

アカウント情報の編集のしかた

Chrome右上の三点メニューから「設定」を開き、ユーザーネームをクリックすると、アカウント名をアイコンを編集できます。

f:id:Distorted_Unchi:20181208184839p:plain
設定

僕が何故こんなにJust Shapes & Beatsが好きなのか

CCC Advent Calendar 2018 - Adventarの3日目の記事です。

僕が1ヵ月ほど前からプレイ動画をず~~~~っと追いかけてきて、先日ようやく購入した神ゲー、Just Shapes & Beatsの布教記事となります。

どんなゲーム?→音楽に同期した避けゲー!

簡単に言うと、「音楽に同期した避けゲー」です。Steamゲーですが、最近Switch版も出たとか。
概要に関しては、プレイ動画を1,2個見てもらった方が速いでしょう。雰囲気が伝わりやすい動画を二つ選んできました。
www.youtube.com
2,3分の楽曲に合わせて動く弾や障害物を十字キーで避けていくゲームです。操作が超簡単!主人公に攻撃手段は一切なく、ひたすら避けていきます。薄い色での敵の攻撃予告と音楽のリズムをヒントに、ノリノリで避けていきます。最後まで避け切ればクリア!拍に合わせて画面が揺れたりしてるのがノリノリポイント高いです。
上の動画は所詮「ボス戦」なので、一つ通常ステージも見てみましょうか。僕がこのゲームで一番好きなステージ、「Spectra」です。
www.youtube.com
動画の通り、プレイヤーは無敵時間のある「ダッシュ」を使って障害物を飛び越すことができます。ダッシュは連発はできないようになっているので、あくまで障害物や音楽に合わせて効果的に使わなきゃなんですね
因みにこのステージのどこがそんなに好きかというと、障害物の音楽同期とゲームの攻略の関連性がめちゃくちゃ分かりやすいからなんですよね。拍合わせの六角形の外枠の回転に、後から登場するメロディに合わせて六角形の中身がぐいーんと伸びてくるところが楽曲の構成をしっかり捉えていて最高に気持ちいいです。

ステージ(楽曲)数は全41です。たぶん。楽曲についてはあのMonstercat Releaseのものも多く収録されています!アツい!
プレイ動画でなんとなくどんなゲームか分かったところで、買おう。Steamで2000円ちょっと!
僕がこのゲームの虜になった経緯を書いていこうと思います。

このゲームに出会うまで:最近の僕の三つの感情

確か僕があるプレイ動画に巡り合って、プレイ動画を追いかけるようになったのが一カ月前とかなんですが、このゲームはそれまで音楽やゲームに対して思っていたことをピッタリ満たしたものだったんです。
思っていたことというのは、以下の三つのようなことです

音楽に同期した映像作品いいよね...

音楽同期した映像表現かっけ~~~おれもやりて~~~~~~~~
特に2Dグラフィックスでかっちり音楽同期してるのやべ~~~~~~(自分が3D表現が全然できず2Dに関心があるので)

音ゲーで主人公が操作できないかな...

僕は音ゲーが大好きなんですが、音ゲーをあんまりやったことがない人ってそもそも「音ゲーって楽しいの?」って思うことがあるっぽいです。その原因の一つとして考えられるのが、「主人公が画面に表示されない」だと思うんですよ。主人公がいないのに、何のために俺は降ってくるノーツを叩くの?という感覚に襲われる経験、実は音ゲーマーの僕にもあったりします。
f:id:Distorted_Unchi:20181202223002p:plain
少し前にSEGAから出た「オンゲキ」というアーケードゲームは、レバーで主人公を操作して音楽同期した敵の攻撃を避けつつ、音楽同期したオブジェクトを叩いて敵に攻撃する、って感じでこの辺が分かりやすくなってます。
www.youtube.com
というかオンゲキがきっかけでこの辺を考えるようになったのかも。

弾幕って綺麗よね...

中学生の頃とかに東方シリーズの体験版をプレイしてからずっと思っていて、「弾幕」は視覚表現としての市民権をある程度得たとすら思っています。
www.youtube.com

で、今思うと、この三つを満たしたものって「音楽同期した避けゲー」だったんですね。実はそういうものは僕が知ってる限りでもいくつか例があります。

三つの感情を満たした他のゲーム

イワナ

イワナの中でも楽曲に合わせたものがあります。勿論合わせてない普通のアイワナもあります。
www.youtube.com
僕はアイワナをやったことないので認識が合っているか分からないんですが、殆どが覚えゲー・死にゲーという認識です。何の予告もなくいきなり弾が出てくる印象。

東方弾幕風

そもそも「東方弾幕風」とは、東方シリーズのような弾幕が簡単に作れる非公式ソフトです。
これも音楽同期したものがあります。
www.youtube.com
めっちゃきれい。音楽同期した視覚表現としてはかなり良いですね~。
ゲームとして見ると、東方という弾幕ゲーの性質上、音楽同期しようがしまいが純粋に弾幕を避ける力が試されている気がします。

この二つをゲームとして見ると、

こんな感じのことが言えると思います。きっと僕はこの中間のバランスのものがやりたかったんです。
Just Shapes & Beatsがこれをあまりにピッタリ満たしたゲームのため、テンションが上がってこれから下の文章が勝手に製作者ヅラをしたものになってます。すみません。

Just Shapes & Beatsはこの二つの中間のバランス

そうなんです。このゲームの覚え・音楽同期要素のバランスが絶妙なんです。覚え・音楽同期要素が丁度いいということは、

  • 曲を知らなくてもちゃんとプレイはできる
  • 曲を知ったからこそ上手にプレイできるようになる

ってことなんです。
このゲームがこのような音楽体験を持つ避けゲーを実現するためにしたことは、"ただ"形と曲を融合させる、ということです。"Just" Shapes & Beatsです。「Shapes」とあるように、弾幕ゲーと言うには障害物の比率が高めです。それは密度の高い弾幕が音楽同期しても結局弾幕回避能力を問うだけのゲームになってしまうからで、そういうゲームになってしまわないためのアプローチがあります。

ダッシュを取り入れること

これにより、大振りな障害物をかわすゲームにする上で移動だけではパターンが不足してしまうことが解消され、かつ密集した弾を一生懸命くぐるゲームではないことが示されています。

予告をしっかりすること

これにより死にゲー・初見殺しを回避し、音楽に合わせて動くゲームであることも示されます。
f:id:Distorted_Unchi:20181202223526p:plain
一部初見殺しを逃れられていないステージもありますが...Paper Dollsとかひどい

それとバランス面以外でも良いなあと思った点があります。

障害物は音楽を引き立てるし、音楽は障害物を避けやすくする

まあこれは結局さっきの

  • 曲を知らなくてもちゃんとプレイはできる
  • 曲を知ったからこそ上手にプレイできるようになる

ということの換言なんですが、なんかめっちゃエモい文面になってしまいましたね。音楽と障害物の共生なんですよね。この観点で「どんなゲーム?→音楽に同期した避けゲー!」で紹介した「Spectra」を見てみるとウオオオオとなると思います。
普通にノーツを叩く音ゲーでも「ノーツは音楽を引き立てるし、音楽はノーツを叩きやすくする」とか言えそうな気がします。それと比較すると、このゲームの体験は主人公がいる分より主体性のあるものになっています。

一番言いたい部分が終わったので、ついでに他の部分も褒めたいと思います。

ついでに:ストーリー

ストーリーも"Just" Shapes & Beatsです。これも動画とか貼りたいんですけどあんまりネタバレになっちゃうとアレなので貼りません。
登場人物はこんな感じに形だけで見事にディテールがありません。
f:id:Distorted_Unchi:20181202223820p:plain
f:id:Distorted_Unchi:20181202224418p:plain
更にストーリモードのゲーム内ムービーでは彼らは人間が理解できる言語を一言も発しません。たぶん要らないからです。彼らがコミカルに動いてる様子や感情表現だけでストーリーが表現されます。それくらいストーリーがシンプルということです。赤色のキャラが敵青色のキャラが味方で、どういった経緯で青色の彼らが道を歩んでいるのか、ということだけを、ユーザーのプレイの動機付けのために見事に伝えています。

ついでに:オンラインプレイ

単語だけだと「ただ避けるだけのゲームのオンラインプレイってなんだ?」ってなると思います。こんな感じです。
www.youtube.com
所詮協力プレイです。このゲーム、自機が三回攻撃に当たると死んじゃうんですが、オンラインプレイでは死んだ後に「Help!」と言いながら左に流れていき、これに他のプレイヤーが触れると救出することができます。救出によって結構ゲーム性が変わってきたりします。他のプレイヤーを救出することでゲットできる実績とかあるし。
選曲は3曲の中から多数決で決定するので、自分のステージの好みを他のプレイヤーに見せたりできます。推しステージという概念、オタクっぽい。
他には、危なっかしいプレイや安置を利用したパフォーマンスを他の人に見せたり、楽曲の拍に合わせてみんなで踊りながらプレイしたりするのがたのしい。

最後に

メッチャ長く書いてしまいました。結論としては、このゲームが好きな理由は
覚え・音楽同期要素のバランスが絶妙だからです。
なるべく伝わりやすい流れで書いたつもりですが、伝えたいことが多すぎてたぶん伝わりにくいと思います。まあ僕が精選したプレイ動画だけ見て「おもしろそう!」と思ってくれればうれしいです
Steamの商品ページ貼っときます。買おう。2000円ちょっと!
store.steampowered.com

Windows10でJoy-ConをProcessingから扱ってみた その1(vJoyなし編)

少し前に同級生がJoy-Conの制御を頑張っていたな~( JoyConの加速度センサーを取るための設定変更についての話 - 忘れないうちに )ということをふと思い出して、でも今switchは買えないな~とか思ったけど、そういえばJoy-Conって単体で買えるんですよね。という訳で買ってProcessingから制御してみました。

少し調べるとこちらの記事 【P5 Tips】 ProcessingをNintendo SwitchのJoy-Conで遠隔操作する|タピオカ@ジェネラティブアート|note に書かれている通り、ProcessingでJoy-ConなどのGamepadを簡単に扱えるライブラリがあるらしいです。当該記事ではMacでやってみた、ということなので、今回はWindowsで同じことをやってみて、躓いたところとかを書いていきます。

Joy-Conについて

まあいろんなとこに売ってて、新品はLRセットでだいたい8000円前後で買えます(Switchがだいたい32000円)。
ここで、このJoy-Con単体での販売には充電器がついてこないことに注意してください。別途充電器を買う必要があります。 Joy-Con充電グリップ

今回できるようになること

今回はこちらの記事 【P5 Tips】 ProcessingをNintendo SwitchのJoy-Conで遠隔操作する|タピオカ@ジェネラティブアート|note の流れに沿って、Joy-Con

・ボタン入力
・スティックの方向入力

ができるようになります。逆に、今回はvJoyを使わないので

・ジャイロ入力
・スティックのJoyStickとしての入力(深度入力)

はできません。それではやっていき

Processingで扱う前の準備

Windows10のPCにJoy-ConBluetooth接続します。このへんはこちらの記事 Windows10でSwitchのJoyConを使う方法 | hyperT'sブログ を参考にしてください。
vJoy driverから先は今回は使わないのでやらなくて大丈夫です。

問題:Joy-Conからの入力の名称が文字化けして認識できない

さっそく記事にある通りにライブラリをインポートして、設定ファイルを作成して、サンプルコードを貼り付けてやってみました。

さっそくおかしい。

具体的には、サンプルコードを実行すると

f:id:Distorted_Unchi:20180721175752p:plain

こんな画面が(出てこないはずなんだけど)出てきて、Joy-Conらしき「Wireless Gamepad」を選択すると

f:id:Distorted_Unchi:20180721180205p:plain

入力信号とプログラム上でのオブジェクトとをつなぐ画面が出てきました。Joy-Conのボタンを押すと右にある該当する入力信号の〇が光るので繋いでいくようです。よく見ると入力信号が文字化けしまくっていて、結果的に同じ文字列になってるのがいくつかあって、これ識別できるのか...?とか思いながら全部繋いで右上の「USE」を押すと、

f:id:Distorted_Unchi:20180721180812p:plain

やっと実行されました。スティックはちゃんと取れてるんだけど、やっぱり文字化けした結果同じ文字列になってしまったボタン類は正しく認識できてません。画像では+ボタンしか押してないのに、+ボタンの入力と同じ文字列の入力のボタンが全部反応してます。
結論としては僕はこの方法ではJoy-Conをちゃんと制御することはできませんでした。

原因?

恐らくはWindows10が認識したJoy-Conの入力をProcessing(のライブラリ)が受け取る際に文字化けしているものだと思われます。
また、このJoy-Conはまだ一度もSwitchと接続していない(Switchを持っていないから)ので、それが原因かもしれないです。

別の方法で認識してみた

設定ファイルからJoy-Conを認識する方法がうまくいかなかったので、今回は名称からJoy-Conを認識してみます。

ControlIO control = ControlIO.getInstance(this);
ControlDevice device = control.getDevice("Wireless Gamepad");

これで取り敢えずdeviceからJoy-Conが扱う第一歩ができたので、ここからボタンやスティックの値を取得します。
ボタンやスティックの入力文字列が文字化けしているので、入力文字列指定ではなく、入力のインデックスを特定して取得します。

//Rの場合
ControlButton buttonA = device.getButton(0); //Aは0番目のButton入力
ControlHat hat = device.getHat(16); //スティックは16番目のButton入力

ここで注意すべきは、Hat(スティック)はHatの独立したインデックスを持つのではなく、Buttonと同列のインデックスにいることです。device.getHat(0)とやるとAを取得していることになり「お前はボタンを取得してるぞ」と怒られます。ここが全然わからなかった。
Joy-Con以外のGamepadにはSliderというのが登場することもあるらしいですが、それはSlider独自のインデックスを持っているらしい。ややこしい...
つまりこういうこと。

これであとはpressed()なりgetPos()なりで値が取れるようになりました。

サンプルコード

あの記事(【P5 Tips】 ProcessingをNintendo SwitchのJoy-Conで遠隔操作する|タピオカ@ジェネラティブアート|note)のものを改変させていただきました。

import net.java.games.input.*;
import org.gamecontrolplus.*;
import org.gamecontrolplus.gui.*;

ControlIO control;
ControlDevice device;

void setup() {
  size(500, 360);
  colorMode(HSB, 360, 100, 100);
  rectMode(CENTER);
  textAlign(LEFT, CENTER);

  control = ControlIO.getInstance(this);
  device = control.getDevice("Wireless Gamepad");
  if (device == null) {
    println("No suitable device configured");
    System.exit(-1);
  }
}

void draw() {
  background(200, 60, 30);

  // ボタンの状態を表示
  for (int i=0; i<device.getNumberOfButtons()-1; i++) {
    stroke(#eeeeee);
    if (device.getButton(i).pressed()) fill(30, 60, 90); 
    else noFill();
    rect(30, 30 + i * 20, 15, 15);
    fill(#eeeeee);
    text(i, 50, 30 + i * 20);
  }

  // スティックの状態を表示
  float radius = 75;
  stroke(#eeeeee);
  noFill();
  translate(width/2, height/2);
  beginShape();
  for (int i = 0; i < 8; i++) {
    float angle = (float)i / 8 * TWO_PI;
    vertex(radius * cos(angle), radius * sin(angle));
  }  
  endShape(CLOSE);

  noStroke();
  fill(30, 60, 90);
  int pos = device.getHat(16).getPos();
  float x = 0, y = 0;
  if (pos > 0) {
    // 「横持ち」基準で上下左右が決められているため
    // 右の Joy-Con の場合は HALF_PI を足す
    // 左の Joy-Con の場合は HALF_PI を引く
    float angle = (float)pos / 8 * TWO_PI + HALF_PI;
    //float angle = (float)pos / 8 * TWO_PI - HALF_PI;
    rotate(angle);
    x = radius;
  }
  ellipse(x, y, 20, 20);
}

この方法で左右のJoy-Conを同時に認識するのに少し手間が要るお話

Joy-Conのデバイスの名称がLR双方とも「Wireless Gamepad」であるため、先ほどのデバイスの取得の方法のままだとLRを区別して認識することができません。
設定ファイルを用いてconfigしてあげれば可能なのですが、今回それが文字化けで使えないため、あるいはconfigの手間をユーザーに取らせたくない場合に、
「名称が『Wireless gamepad』であるデバイスを候補として取得しておいて、LRそれぞれ固有のボタンが押された際に区別する」という方法があります。

Joy-ConのButton(ButtonとHat)入力のインデックスの配置は以下のようになっています。

空のインデックスが存在するのがおもしろい。6,7に至ってはどっちも空、何に使われていたのか...
ここで、-/+、各スティック(押す)、SHUTTER/HOMEはLRでインデックスの被りがなく、これらが押されたことを取得すればLRを区別して認識することができます!今回ぼくは-/+でやってみました。

サンプルコード

起動して、LRの-/+を押すと、それぞれのスティック入力が可視化されます。

import net.java.games.input.*;
import org.gamecontrolplus.*;
import org.gamecontrolplus.gui.*;

ControlIO control;
ControlDevice deviceL=null, deviceR=null;
ArrayList<ControlDevice> candidates=new ArrayList<ControlDevice>();

void setup() {
  size(500, 500);

  control=ControlIO.getInstance(this);

  //control.getDevices()で接続されたデバイスの一覧がとれる
  //device.open()はそのデバイスを有効化する命令(getDevice("Wireless Gamepad")などで取得する際には自動的に呼ばれる)
  for (ControlDevice device : control.getDevices()) { 
    if (device.getName().equals("Wireless Gamepad") && device.getNumberOfButtons()==17) {
      device.open();
      candidates.add(device);
    }
  }
}

void draw() {
  background(0);

  for (ControlDevice candidate : candidates) {
    if (deviceL==null && candidate.getButton(8).pressed()) { //-:8
      deviceL=candidate;
    }
    if (deviceR==null && candidate.getButton(9).pressed()) { //+:9
      deviceR=candidate;
    }
  }

  if (deviceL!=null) {
    float angle;
    if (deviceL.getHat(16).getPos()>0) {
      angle=(deviceL.getHat(16).getPos()+6)%8/8.0*TWO_PI;

      stroke(255);
      strokeWeight(1);
      float x=width*0.33+100*cos(angle);
      float y=height*0.5+100*sin(angle);
      line(x, y, width*0.33, height*0.5);
    } else {
      angle=-1;
    }

    fill(255); 
    textSize(16); 
    textAlign(CENTER);
    text(angle, width*0.33, height*0.5);
  }
  if (deviceR!=null) {
    float angle;
    if (deviceR.getHat(16).getPos()>0) {
      angle=(deviceR.getHat(16).getPos()+2)%8/8.0*TWO_PI;

      stroke(255);
      strokeWeight(1);
      float x=width*0.66+100*cos(angle);
      float y=height*0.5+100*sin(angle);
      line(x, y, width*0.66, height*0.5);
    } else {
      angle=-1;
    }

    fill(255); 
    textSize(16); 
    textAlign(CENTER);
    text(angle, width*0.66, height*0.5);
  }
}

今回はここまで。その2があるとすればvJoyを使ってジャイロやJoyStickの取得になるけど、これらを使った制作物を公開するときにユーザーにvJoy driverのインストールを要求することになるのがよくないのでたぶんやらないかな~...

FMS生に向けた「ネットワーク理論」履修上のことについて

学科向けの記事です。
FMSB2で僕が履修したNDの「ネットワーク理論」について。
(この記事の内容は2017年度の「ネットワーク理論」に基づいたものです。来年度は担当教員、評価、講義の進行が異なる場合があります。)

基本情報

ネットワーク理論はNDの選択必修の2年次科目(2016年入学者用カリキュラム時点)で、MSとFMSの生徒は他学科履修科目として履修することができる。他学科履修科目なので他学部履修科目のような窓口の問い合わせは必要なく、履修登録画面から選択することができる。評価は授業中の演習30%、平常点10%(謎、たぶん出席)、期末考査60%。2単位。

科目の内容

ざっくり。
グラフ理論についての基礎(隣接行列によるグラフの表現など)をさらっとやって、そこから様々な有名な問題についてグラフ理論による解析の例を学ぶ。

まずグラフっていうのはこういうやつ。
f:id:Distorted_Unchi:20180131175115p:plain
点を要素、辺を関係として要素と要素の関係性を表現できるやつ。駅と駅を線路で繋ぐネットワークを表してもいいし、辺を矢印にして(有向グラフ)動物の捕食関係やワークフローを表してもいい。

でここからグラフ理論を活用した簡単な例を一部紹介すると、
f:id:Distorted_Unchi:20180131175152p:plainf:id:Distorted_Unchi:20180131175249p:plain
左のグラフは一筆書きできるけど右のグラフは何をどうやっても一筆書きできない。あと左のグラフは「一筆書きでかつ始点が終点と一致する」みたいなことができない。こういう内容が定理で証明できたり、
f:id:Distorted_Unchi:20180131175830p:plain
A~E駅を鉄道で繋ぐ際に線路整備コスト(各辺の数字)が最小になる繋ぎ方を求めるアルゴリズムがわかったりする。
他にも4つの立方体の問題(急性精神病問題)とか6人の会合(参考:ラムゼーの定理と6人の問題 | 高校数学の美しい物語)とか最大流問題とか色々やったけど今回は図が簡単に書けるやつだけ図を出しました。デジタルな図書くの意外としんどい。
6人の会合については一般化したラムゼーの定理には触れなかったので、そこまで踏み入ってくれればもっと面白かったのになあと思う。授業の傾向としては全体的に、色々なグラフ理論に関する問題に浅めに扱う感じ。

講義

(2017年度の「ネットワーク理論」に基づいたもの)
基本的に出席はない。が、抜き打ちで一回だけ出席取ってたり、宿題(演習課題)のプリントを講義の終わりに配ってたりしたし、講義資料のPDF配布がなくプリントなので基本的に出席しないと(特にアウェイなMSやFMSの人は)困ることが多い。講義はプリントの穴埋め形式で進むし進行もゆっくりめなのであんまり理解に困ることはない。説明もけっこう分かりやすい。教科書は全く使わなかったので来年も担当が同じ人なら買わなくていいけど、内容を深めるための参考書としては良質。
科目の内容の関係上、前提となる数学的スキルは殆どない。基本的な行列の演算や、高校レベルの数列、場合の数に関しての理解ができれば十分。
講義中に演習問題を解くけど、それを提出したりはしない。授業外課題に関しては、2回だけ宿題(演習課題)があったが、あまり負担は大きくない。

テスト

(2017年度)
この辺りの分野はいくらでも応用問題が作れるのでテストもそんな感じなのではとビクビクしていたが、意外にも講義で扱った問題そのままの出題が殆どだった。講義プリントを全て真面目に復習していれば確実に解けるものだった。とはいえ扱った内容の量自体が相当なものなので復習にはそれなりに時間がかかるんだけど。

単位

授業外の負担がかなり少なめなので、授業を受けて復習をそれなりにやってテストに臨めば楽な部類に入ると思う。ただ、当然のことかもしれないけど、内容に興味がないと講義とかテスト対策とかに対してモチベが出なさそうな科目だな~と思った。

所見

「この分野に関して興味があるけどグラフ理論真面目に学んだこと全くない」という人にはおすすめの科目。少し独学してる、みたいな人には講義内容は退屈になると思う。講義内容を踏まえて自分で更に発展させたい、みたいなことは比較的やりやすいと思う。また、グラフに関する問題を解決するアルゴリズムがけっこう出てくるので、FMSにありがちな「それをコンピューターにやらせるぞ~」みたいな動機も生まれやすいので、アルゴリズム実装オタクにもおすすめ。

Processingとffmpegで簡易BGA作成 その2(BPMに同期したモーション、fps指定書き出し)

前回 - Processingとffmpegで簡易BGA作成 その1(レンダリングとプレビューを分けるまで) - うつろぐの続きになります。前回の記事をまだ読んでいない方は是非そちらからお願いします。
 

前回は取り敢えずProcessingとffmpegで音声つきの動画を書き出すところと、レンダリングとプレビューを分けるところをやってきました。
これに今回BPMに同期したモーションの作成やプレビュー、fpsを指定した書き出し(レンダリング)を実現すれば、一通りProcessingでBGAを作成する基盤が完成することになります。
 

前回までのコードの構造を改めて見てみる

前回の最終的なコードは以下のようなものでした。

import ddf.minim.*;
//Minimのsignal等を使っていないのでこの一行だけでよかった

boolean render=true;
//フレーム書き出しと動画への変換を行うかどうか。
//falseにするとプレビューのみを行う

int frameSum=1000;

float size=0;

Minim minim;
AudioPlayer player;

void setup() {
  size(600, 600);
  minim=new Minim(this);
  player=minim.loadFile("music.wav");

  if (render) {
    //以前のフレームデータを消去
    launch(sketchPath()+"\\clearFrames");
    
    //フレーム書き出し
    for (int i=0; i<frameSum; i++) {
      display(i);
      if (i%100==0) {
        println("Now I am dumping frame"+i);
      }
      save("frames/"+nf(i, 4)+".png");
    }
    
    //動画に変換
    launch(sketchPath()+"\\render");
  }
  player.play();
}

void draw() {
  display(frameCount-1);

  fill(0);
  noStroke();
  rect(0, 0, 100, 100);
  fill(-1);
  text(frameCount-1, 50, 50);
}

void stop() {
  player.close();
  minim.stop();
  super.stop();
}

//--------------------------------------

void display(int t) {
  background(0);
  size=400+300*sin(radians(t*10));

  fill(255, 0, 0);
  noStroke();
  ellipse(width/2.0, height/2.0, size, size);
}

構造として、このコードではsetup内でフレームの書き出し、draw内でプレビューを行っており、両方で同じ描画を行うためにdisplay()でタイミングを引数にとって描画内容を返しています。今回はこのdisplay()にBPMに沿ったタイミングを渡してあげることで、BPMに同期したモーションを実現します。

更に、これを現実時間に依存したタイミングを用いると、プレビューもBPMに同期したものになります。前回はProcessingの処理速度に依存したタイミング(frameCount)を用いてプレビューを描画していたのでプレビューが速くなったり遅くなったりしてしまいましたが、現実時間に依存したタイミング(millis()など)を用いることによって、常に一定の速さのモーションになるわけです。

BPM同期の機構をつくる

現実時間に依存したタイミングを用いたBPM同期の機構をつくっていきます。

時間管理をしてくれるclassをつくる

Processingではmillis()で「プログラム起動からの経過ミリ秒」が取得できるので、これを用いてストップウォッチのようなclassを作り、時間管理をさせます。

class MGTime {
  int pMillis, runMillis;
  boolean working;

  MGTime() {
    pMillis=millis();
    runMillis=0;
    working=false;
  }

  void timeStep() {
    if (working) {
      runMillis+=millis()-pMillis;
      pMillis=millis();
    }
  }

  void stop() {
    working=false;
  }
  
  void kill(){
    working=false;
    runMillis=0;
  }

  void start() {
    working=true;
    pMillis=millis();
  }

  int time_normal() {
    return runMillis;
  }
}

start()した後にdraw等のループ内でtimeStep()し、time_normal()でストップウォッチの現在のミリ秒を返します。stop()で一時停止、kill()で停止してリセットします。

BPM換算の機能を追加する

time_normal()で返るミリ秒は、1000ミリ秒(1秒)がBPM60の四分音符と同じ長さになります。よってこの時点でBPM60の楽曲に同期したモーションは簡単に作れますね。ここから発展させて、各BPMでの四分音符を1000としたものをtime_synched()という関数で返すことで、任意のBPMに同期したモーションを作成することが容易になります。
ちなみにtime_synched()の内部のtoSynchedで、ミリ秒を指定したBPMでの換算数値を返しています。

(これがMGTimeクラスの最終版になります)

class MGTime {
  int pMillis, runMillis, BPM;
  boolean working;

  MGTime() {
    pMillis=millis();
    runMillis=0;
    BPM=60;
    working=false;
  }

  void setBPM(int _BPM) {
    BPM=_BPM;
  }

  void timeStep() {
    if (working) {
      runMillis+=millis()-pMillis;
      pMillis=millis();
    }
  }

  void stop() {
    working=false;
  }
  
  void kill(){
    working=false;
    runMillis=0;
  }

  void start() {
    working=true;
    pMillis=millis();
  }

  int time_normal() {
    return runMillis;
  }

  float time_synched() {
    return this.toSynched(runMillis);
  }
  
  float toNormal(float t){
    return t/(BPM/60.0);
  }
  
  float toSynched(float t){
    return t*(BPM/60.0);
  }
}

setBPMでBPMを指定し、time_synched()でBPM換算した数値が返ります。

このclassを用いたBPM同期モーションの例

使用例です。

import ddf.minim.*;

Minim minim;
AudioPlayer player;
MGTime mgt;

void setup() {
  size(600, 600);
  minim=new Minim(this);
  player=minim.loadFile("music.wav");

  mgt=new MGTime();
  mgt.setBPM(170);

  mgt.start();
  player.play();
}

void draw() {
  mgt.timeStep();
  display((int)mgt.time_synched());
}

void stop() {
  player.close();
  minim.stop();
  super.stop();
}

//--------------------------------------

void display(int t) {
  background(0);

  int turn=1000;
  float size=400*(pow(turn-(t%turn), 2)/pow(turn, 2));

  noStroke();
  fill(255, 0, 0);
  ellipse(width/2.0, height/2.0, size, size);
}


こんな感じになります。
www.youtube.com

fpsを指定して書き出す

前述のストップウォッチのclass(MGTime)にtoSynchedという関数を用意していました。これがfps指定書き出しの際にも使えます。レンダリングfpsに応じた一定の間隔で書き出すことになります。まずは1000.0/fpsでその間隔をミリ秒で算出し、それをBPM換算すると、そのBPMでの書き出し間隔が算出されます。

float interval=mgt.toSynched(1000.0/fps);

for (int i=0; i<frameSum; i++) {
  display((int)(i*interval));
  save("frames/"+nf(i, 4)+".png");
}

サンプル

先ほどのMGTimeクラス最終版を利用した場合のサンプルです。

import ddf.minim.*;

boolean render=true;
//フレーム書き出しと動画への変換を行うかどうか。
//falseにするとプレビューのみを行う

int frameSum=1000;
int fps=60;

Minim minim;
AudioPlayer player;
MGTime mgt;

void setup() {
  size(600, 600);
  minim=new Minim(this);
  player=minim.loadFile("music.wav");

  mgt=new MGTime();
  mgt.setBPM(170);

  if (render) {
    //以前のフレームデータを消去
    launch(sketchPath()+"\\clearFrames");

    //フレーム書き出し
    float interval=mgt.toSynched(1000.0/fps);

    for (int i=0; i<frameSum; i++) {
      display((int)(i*interval));
      if (i%100==0) {
        println("Now I am dumping frame"+i);
      }
      save("frames/"+nf(i, 4)+".png");
    }

    //動画に変換。このbatは命令数が多いのでlaunch()から呼ぶと動作が不安定なので手動でbatを実行する必要があるかも
    launch(sketchPath()+"\\render");
  }
  mgt.start();
  player.play();
}

void draw() {
  mgt.timeStep();
  display((int)mgt.time_synched());
}

void stop() {
  player.close();
  minim.stop();
  super.stop();
}

//--------------------------------------

void display(int t) {
  background(0);

  int turn=1000;
  float size=400*(pow(turn-(t%turn), 2)/pow(turn, 2));

  noStroke();
  fill(255, 0, 0);
  ellipse(width/2.0, height/2.0, size, size);
}

displayでの描画内容は「このclassを用いたBPM同期モーションの例」のものと同じです。ここまで出来たらあとはProcessingでいかにかっこいい描画が出来るかに懸かっています。がんばり太郎