Version
このPDF、HTMLファイルはSmartDocによって LATEX で生成されています。だいたいのページ数をつかんだり、執筆作業のために仮でレイアウトしたPDFです。HTMLも関係各位に内容を確認していただくために出力しています。つまり最終的な出版書籍とは全く異なるものです。著作権は白井暁彦とその共著者にありますので、無断コピーや配布はしてはいけません。
【この版における凡例】
【作業メモ】(6/12)現在270ページ(1-7章)、8章はMouse、400pでも大丈夫。厳守max350!300-350が自分のPDF+5章は30pぐらい、前付け奥付が20p。pointerだけ
「Wii」本文中のWiiは任天堂の登録商標です。
本書を手に取った賢い読者の貴方、まず貴方に質問をしたいとおもいます。
貴方はこの本にどんな期待をしていますか?
『WiiRemoteプログラミング入門』
貴方が手に入れたい知識はWiiRemoteの使い方?プログラミング?人に聞けない数学や物理?インタラクション技術の開発テクニック?はたまた欲張りにも「そのすべて」でしょうか?まず最初にキッパリと宣言させていただきますが、この本は任天堂Wiiコンソールに関する『ハッキング本』ではありません。興味本位や不正に利益を得る目的で、ゲームを改造したり不正なコピーをする行為は、ゲームそのものの面白さを奪うだけでなく、そのゲームタイトルを世に生み出すために魂を削って制作した人々にも、深い悲しみと経済的ダメージを与えます。貴方がゲームを愛するなら、そんな行為に時間を費やしてはいけません。
ハッキングにもいろんな意味がありますが、仮に不正を働くための「ハッキング本」があるとすれば、この本が目指すところは、その正反対の、WiiRemoteをつかった楽しい「クッキング本」、かもしれません。
この本はWiiRemote(Wiiリモコン、Wiimote様々な呼称がありますが本書ではWiiRemoteに統一)を使って、プログラミングを学びます。ついでに数学や物理の使い方も演習を通して学びます。
目標としては、貴方が大好きであろう、コンピュータを使ったゲーム、その想像力や可能性を最大限に加速するための「最初の武器」を身につけるための「きっかけ」を与えることを目指しています。
読者としては、以下のような方々を対象として想定しております。
そんな読者の皆さんに、WiiRemoteをはじめとする新しいヒューマンインターフェイスを使った、エンタテイメントシステムの世界の「開拓の面白さ」、学校で学ぶ物理や数学の「使いこなし方」、そしてほんのすこし「世界の広さ」を理解してもらえればいいかな、と考えています。インタラクション技術の「広さ」と「深さ」を知ることが、明日の新しいエンタテイメントシステムを作るのです。
すでにコンシューマゲーム機やPC、Flashなどでプロ級のゲームプログラミングを行っているシニアエンジニア以上の読者の貴方は、きっとここまで読んで『なぁんだ、この本?役に立つのかな?』と思ったかもしれません。前述の通り、この本には任天堂の守秘義務にふれるような事は一切書かれていません。Wiiに関する内部仕様に関しては、任天堂から提供されている公式の技術文書をご利用ください。
しかし、任天堂から提供される公式の情報はあくまでゲームを作るための基本情報のはずです。よりよいゲームを開発するための、想像以上のWiiRemoteの使い道や、ちょっとした気づきになるような情報が、本書に少しでも存在すれば幸いです。
筆者(私、白井暁彦)は、1994年ごろから、ヒューマンインターフェイス、リアルタイムコンピュータグラフィックス、物理シミュレーション、触覚インタフェースといったバーチャルリアリティ技術を中心とした基礎技術をエンタテイメント産業の実用の世界に使えるようにするための技術の研究開発を行ってきました。ゲーム用CGの根幹や、大学の研究室、次世代放送用技術の研究開発に身を置いた時期もありますし、テーマパークアトラクションの設計を行ったり、日本やフランスの大学で芸術学部や工学系の学部から大学院生まで幅広くの学生の指導・教育をしてきたりもしました。
中でも、16年以上続いている「国際学生対抗バーチャルリアリティコンテスト(http://ivrc.org/)」のプロデュースや、Laval Virtual ReVolutionというフランスで開催されている国際VR作品公募展のチェアマンの経験を通して、世界中から集まった多くの新規な(新奇な?)デバイスを使ったインタラクティブ作品の裏側とその開発を行った作者に出会う機会を得てきました。
現在は科学コミュニケーターという職業で、最先端の情報科学を世界中にわかりやすく伝える仕事をしています。日本の研究者は特にこの「インタラクティブ技術」のセンスや実装能力が非常に高く、世界から高い評価を受けています。仕事柄、世界のステージに立つその作者やプロジェクトを支援することが多いのですが、驚くことに日本や海外に限らずそのクリエイターは「若い学生」が多いのです。また、新しい表現、すばらしい技術革新を生み出すクリエイターが「必ずしも十分な基本技術を体系的に身につけているわけではない」という状況にもよく遭遇しました。
私が過去にゲーム業界で働いていたときには、仕事柄、数多くのゲームデザインを担当する企画系の方とお話しする機会が多かったのですが、『面白い体験をつくるノウハウ』は、たいてい誰かの「頭の中」に蓄積されており、書籍化は難しいという話を聞くことが多くありました。また最近非常に人気のある、メディアアート・デジタルアート系の大学の先生方も、それぞれアートや映像技術などの専門家なのですが「体系的なインタラクション技術の基礎」は以外と教科書的にまとまっていない、と耳にします。常識にとらわれていては新しいゲームやアートは創れませんが、インタラクティブ技術にかかわるハードとソフトの中間領域に、基礎技術を体系的に伝える教科書の1冊ぐらいは欲しいな、と何度も感じてきました。
筆者はこの本の執筆を通して、現在は「ただの学生さん」かもしれない貴方が、将来ゲーム開発の現場で働く若手のエンジニアや、未来のインタラクティブ技術に革新を与えるアーティストになるかもしれない、と考えながら執筆しています。皆さんが、何に気づき、何を武器にして、新しい世界のフィールドに立っていくのか、そのノウハウを書籍というカタチで共有することを試みています。皆さんも、チャンスがあれば映像やホームページ、論文などの文章にして、共有に挑戦してみてください。
この本に書かれている情報の多くは、上記のような『まだ誰もやったことがない、コンピュータを使ってあたらしい表現をしよう!』としている若者に対して『攻撃力を加える“インタラクション技術”という武器を与えよう!』という視点によって書かれています。こういう「考え方のヒント」はなかなかまとまった書籍にはしづらいものです。インタラクティブシステムの工学的な開発・デザインに関するまとまった書籍や、体系づけられた学問は、(日本では)この10年以上形になっていない状況です。それだけ成長途上であり、経験や経験則に依存する世界でもあるからです。
しかし現実はその状況に甘んじていられません。読者のみなさんがもし、研究室やゲーム開発、インタラクティブ作品開発チームの先輩エンジニアなら、ひとりでできることは限られています。誰も見たことがないプロジェクトをすすめるために、時には「未知数の後輩」に対して、ある時は事細かにソースコードを紐解き、ある時は高校の『数学I』の教科書を持ち出して、熱っぽく語らなければならないときもあるでしょう(しかも開発の真っ最中に!)。本書は、その『すばらしい講義で解説済みの(貴方なら)1秒で理解できるような数式や実装方法』を目の前に、何日もウンウンと唸っている後輩がいるときに、『しょうがないなァ、この本でも読んでやってみろよ』と、机上に付箋付きでおいてあげられるような、そんな書籍になればと思い、書き始めました。
実際に、執筆をすすめてみると、扱わなければならない基礎的な話があまりに多く、なかなかつっこんだ事例やすぐに使えるレシピばかりを紹介するのは大変な作業であることがわかりました。それでも、多くの人に、新しく学べる分野を示すことはできていると思います。もし、本書を読んで『この章のこれを突っ込んでいきたい!』と思う人がいれば、それは本書のねらい通りです。ぜひ、実験や研究をして、論文を書き、本書を参考文献に加えてください。
また本書の一部は、金沢高等専門学校の小坂崇之先生のご協力により、WiiRemoteを使った、初心者向けのステップバイステップのC++/C#のプログラミング演習本としても機能します。『本書を学校の演習に使っています!』といったお話もありましたら、今後のためにフィードバックをいただけると、非常に嬉しく思います。
WiiRemote関係の技術は日進月歩、秒進分歩です。世界中の技術者の努力により、使えなかったものが使えるようになったり、想像もしないすばらしい結果を生み出すこともありますが、公開されているものが都合によって非公開になったり、有料になったりといったこともあります。本書は発行時において可能な限り最新の情報を記載できるよう努力しておりますが、将来にわたり保証されるものではありません。また本書で掲載されているソースコードやプログラム、内容等も将来にわたり正確さが保証されるものではありません。
文中の「Wii」は以後、特に「TM」などを標記しませんが、任天堂株式会社の登録商標です。本書は任天堂株式会社とは一切関係がありません。本書に記載された内容を実行した事による不利益等は全て製品保証の対象外になる可能性があり、すべてはユーザーの責任であることをご理解ください。
この本を書き上げるにあたり、数多く方々にお世話になりました。まず、ゲーム産業に歴史的革命を与えた任天堂の開発者のみなさんに言葉にできない感謝の気持ちを伝えたいと思います。そして編集を担当した大内さん。そして特に執筆やコード提供で協力をいただいた木村秀敬さん、高橋誠史さん、南澤孝太さん、金沢工業専門高等学校の小坂崇之先生、奈良先端科学技術大学院大学の井村誠孝先生、くるくる研究室のみなさん、一年半にわたる遅筆な筆者に最後まであきらめずおつきあいいただき、ありがとうございました。ステキな表紙イラストを描いていただいたタナカユカリさん。そして、世界中のWiiRemote開発者のみなさん、本文中にてお世話になった技術と共に、可能な限りお名前つきでご紹介していきたいと思います。最後に、激烈な昼間の仕事の合間の数少ない休暇、貴重な家族との時間の削減に文句を言いながらも執筆やコーディングを応援してくれた妻・久美子、息子の成彦と隆佳に、愛と感謝を伝えたいと思います。
さあ、読者の皆さん、次は貴方がレヴォリューション(革命)を起こす番です!
本書は大まかに、3つのパートで構成されています。
【パート1】基礎知識・導入編(第1章〜3章)このパートでは、WiiRemoteをつかったプログラミングをはじめるにあたり、知っておくべき知識や先人の開発したプロジェクト、ツール類、そして開発に必要なソフトウェアのセットアップが紹介されています。今までにWiiRemoteで開発を行ったことがある人は読み飛ばしてもかまいません。
【パート2】プログラミング基礎編(第4章)このパートでは、オープンソースで開発されているオープンソースのAPIプロジェクト「WiimoteLib」と無料で利用できるMicrosoft Visual Studio Expressを利用してC++やC#のプログラムを書くことで、WiiRemoteとPCがどのように通信を行い、どのようにしてセンサー類の値が取得できるのかなど、基盤となる技術をステップバイステップで詳細に解説します。
【パート3】応用編(第5章〜9章)このパートでは、4章で扱わなかったDirectXによる3DCG、FlashやProcessingを用いた、より具体的なアプリケーション開発の例を紹介します。DirectXやActionScript3など専門の知識が必要になりますので、ご自身の利用したいケースに近いサンプルを中心に読みほどくことをお勧めします。
関連した雑談は「コラム」にまとめてあります。
※本書の中で扱ったサンプルプログラム等は、正誤表と共にオーム社のHP「書籍連動/ダウンロードサービス」にて入手できる予定です。訪問してみてください。
ここでは、WiiRemoteを使ったプログラミングを学ぶ前に、まずWiiRemoteの基礎知識をまとめておきます。
「Wii」(ウイイ)は2006年末に発売された、任天堂の家庭用ゲーム機です。英語の「We(わたしたち)」と特徴的なコントローラー「Wiiリモコン」を表す「ii」をかけて「Wii」と名付けられたそうです。
このWiiが発売される以前の開発コードは「Revolution(レヴォリューション:革命)」と呼ばれていました。2005年の米国「E3」(Electronic Entertainment Expo;エレクトロニック・エンターテインメント・エキスポ、日本での東京ゲームショーにあたるゲーム産業における世界的な見本市)でこのコードネームとロゴが発表されました。
「革命」という名にふさわしく、Revolutionはいままでの家庭用ゲーム機とは大きく異なるコンセプトで設計されました。詳しくは「社長が訊く Wiiプロジェクト(任天堂公式HP)」に、開発チームと社長の対談という形式で詳しく公開されていますが、簡単にストーリーをまとめると、任天堂・岩田社長は2002年頃「このままゲームが複雑になっていったら、ゲーム業界は縮小する」と考え、その結論として新しいハードウェア設計のほとんど全てにおいて見直しを行った点が大きな「革命」になりました。対象とするユーザ層を従来の青少年層から比べて幅広く設定し、ゲームの遊び方、それを取り囲む環境、性能の設定や消費電力など、事細かに今までの家庭用ゲーム機の進化の流れを見直す方向に設計されています。
Wii本体には省電力・無線常時接続ネットワーク機能や縦置きデザイン、性能などさまざまな特徴がありますが、特にその中でも最も大きな役割を持っている「“革命”の主人公」とも言うべき存在が「Wiiリモコン」(本書では以後"WiiRemote"と標記)でしょう。無線化された片手で持つ、モーションセンサを主軸においたコントローラー。このように「指先ではなく、全身の動作」に注目したヒューマンインターフェース(機械と人間をつなぐ装置)を使った遊び体験は、ダンスゲームを除けばヴァーチャルリアリティ技術やアミューズメントテーマパークなど、ごく一部の大型エンタテイメントシステムに利用されているだけで、まだまだ高価で家庭用ゲーム機になじむとは思われてはいませんでした。レーシングゲームや釣りゲームのような一部のゲームジャンルにおいて、別売のコントローラーを必要とする例もありましたが、やはり「プラットフォームが提供する標準搭載コントローラー」という存在はインパクトがありました。かつてこの種のゲーム用特殊デバイスは多くの研究者・開発者が取り組んできましたが、汎用的な利用方法とその価格に問題があり、なかなか実現しませんでした。しかし発売日に1000万台以上が見込める新ハードに採用されるとなると、一台のコントローラー価格は5000円以下で販売されます(=一般的にハードの製造コストは売価の半分程度、かつコントローラーはゲームソフトよりも同価格かそれ以下に設定されるべき)。「夢のインタラクションプラットフォームが一気に家庭にやってくる!」、「でもどうやって?」世界中のヒューマンインタフェースやエンタテイメント技術の研究者・開発者はこのニュースに色めき立ちました。
その後、Wiiが発売されてから2年以上が経過していますが、WiiRemoteの存在感は全く失われていません。当初はストラップやジャケットを着用せずにプレイで興奮する人が多く、酷い例ではテレビを破壊したり、テニスのやり過ぎでヒジを痛める「急性Wii痛」などネガティブな話題でも賑わせましたが、バーチャルコンソールなどのクラシックコントローラーを利用する場合を除いて、WiiRemoteに対する否定的なユーザーの意見はほとんどありません。またWiiRemote単体の販売価格も徐々に下がり、入手しやすくなっています。ゲームの歴史における革命児はいまや市民権を得ている状態といえるでしょう。
さて、この節ではWiiRemoteのハードウェア的な仕組みを解説します。ブラックボックス化しがちな製品技術を理解するコツとして、まず皆さんが「発売前を想定して、自分でデバイスを開発するつもり」になってみるとよいでしょう。
前述の通り、WiiRemoteは革命的な操作体験を提供しつつも「5000円を切る販売価格で」という要求仕様があったようです。しかしWiiRemoteはゲームのためのコントローラーですから、従来のボタンアクションやその操作の反応速度も維持しつつ、無線化を実現する必要があります。またバイブレーターもあったほうがいいですし、電池もできれば長持ちして欲しいでしょう。これらは非常に難しい要求です。
コードネーム「Revolution」時代、「コントローラーにはモーションセンサが載るらしい」という噂はありました。ゲームボーイ「コロコロカービィ」以来、任天堂のゲームタイトルに採用されていた、テキサスインスツルメンツ(TI)社製の加速度センサのようなものが利用されることは想像されていましたし、すでにNECトーキン社からも、加速度センサ、角速度センサ、地磁気センサを組み合わせたエンタテイメント用途向けセンサー部品が発売されていましたが、10万円近くする高価な物で「技術的には不可能ではないだろう」と予想はされていましたが「どのような仕様で」、「どうやったら想定される価格内に収まる製品になるのか?」は全くの謎でした。またこれらのセンサーだけでは位置決めに使うには精度が十分ではありませんし、どんなゲーム・インタラクションになるのかも想像がつきませんでした。任天堂内部の開発者もいろいろな苦労と検討を重ねたようです。最終的にWiiRemoteには、赤外線光源をTVの上に置く「センサーバー」が付け加えられ、赤外線CMOSセンサーという構成になりました。また当初の設計では公表されていなかったスピーカーなども加えられ、最終的に、WiiRemoteは以下のようなスペックになりました。
サイズ | 縦148mm、横 36.2mm、厚さ30.8mm(突起部分除く) |
通信機能 | Bluetoothによる無線接続、最大接続台数:4台 |
プレイ可能距離 | テレビから5m |
ポインター | 画面を指し示すポインティング機能 |
モーションセンサー | 傾きや動きの変化を検出(3軸) |
ボタン | デジタル11入力(1,2,A,B,-,+,Home,十字) |
振動機能 | バイブレーター1個 |
スピーカー | モノラルスピーカー1個 |
プレイヤーインジケータ | 青色LED 4個 |
拡張ユニット接続可能 | ヌンチャク、クラシックコントローラーなど |
以下は独自に調査した、より詳細なスペックと解説です(将来の仕様変更により変更される可能性があるかもしれません)。
どうでしょう?高速な無線通信が行えるBluetoothコントローラだけでも魅力的なのに、これだけの能力を持ったモジュールを含む製品が5000円以下で、しかも耐久性の高い量産品として販売されていること自体が驚きです。
こんなすばらしいデバイスを開発・販売している任天堂に敬意を払い、応援するためにも(=この本で解説しているPCでの利用を流行させるためにも)、WiiRemote単体での購入をぜひぜひお勧めいたします。もちろんWii本体も。余談ですが筆者のフランス時代の研究室では、Wii発売当初は本体が手に入らず、研究開発のためにWiiRemoteだけ1ダースほど購入した時期もありました。
現在、世界中の有志により、WiiRemoteをPCで利用できるようにする取り組みがされています。そして、それらのツールやオープンソースのAPIを利用して、世界中の学生を中心にさまざまなWiiRemoteを使った革新的なプロジェクトが開発されています。
ここではまずWiiRemoteを使ったプログラミングで、どんな楽しいことが実現できるか、最新の学生プロジェクトを中心に紹介していきたいと思います。
まずは北海道から、巨大なロボットをWiiRemoteで操作するプロジェクト「未来大IKABO Project」を紹介します。
制作者のひとり、はこだて未来大の味岡真広さんによると『設計図や仕様書のようなものは、学生がゴリゴリ作ったものなのでありません』と、きっぱり。もともとは「ロボットフェス・インはこだて」市民の会という組織が中心となって作った「観光用の巨大イカロボット」で、はこだて未来大学の3年生が中心となって、ソフトウェアの部分を開発したそうです。ロボットの詳細はIKABO公式サイト(http://ikarobo.com/)に記載されていますが、身長220cm、重量約200kg、エアシリンダーによるアクチュエーターで、足1本につき3つの関節、さらに1つの関節に3つのエアシリンダーを搭載しており、足1本につき512通りの動作の実現しています。足は2対ありますので、合わせて約25万通りの動作、さらに目や頭も動きますので、WiiRemoteを使うことで、イカロボット独特の多様なポーズの設定ができるようになっています。
このような複雑なロボットの操作であっても「どんな人でも簡単に操作できるように」と、過去にタッチパネルを用いた操作ツールも開発したようです。その後、WiiRemoteをつかった独特の操作として、両手にWiiRemoteを持ち、操作者が腕を動かす動きに対応して、IKABOの足が動作させる方法に辿り着きました。これにより操作者の動きに合わせた自由な動作、複雑な動きやユニークな動きを実現することができるようになったそうです。
開発プラットフォームはVisual C++ MFCアプリケーションで、APIは「WiiYourself! - native C++ Wiimote library v0.96b」を使用しています。WiiRemoteから3軸加速度+ボタンの情報を取得し、3軸加速度から3軸の傾き情報にプログラム上で変換し、イカロボットの腕の動作を決めています。その情報を有線シリアル通信(USB)もしくはネットワーク(DirectPlay)でイカロボット実機へ動作指示を通信し、イカロボット内にあるマイコンボートに送信しています。
実際の操作は、イカロボットが目の前にいるなら目で、遠いところにいるなら、操作ソフトに組み込んだリアルタイム動画配信によってイカロボットが動いているのを見て行うそうです。WiiRemoteで実物のロボットを動かすことで、ユーザの動きをイカロボットが真似てくれる、という点が楽しいそうです。
実際に地元のお祭りでも盛り上がっているようで、YouTube上で大観衆の中、クネクネ動くIKABOのアツい動画を見ることができます(http://jp.youtube.com/watch?v=4P_alu527SY)。
続いて、奈良先端科学技術大学院大学(NAIST)情報科学研究科の学生チーム『サムい人たち』(千原研・横矢研・加藤研)が、第16回国際学生対抗VRコンテスト(IVRC2008、http://ivrc.net/)で製作した「Glaçon」(グラソン)を紹介します。「Glaçon」はフランス語で「氷・ツララ」を意味しますが、WiiRemoteを使って『自由に天井から生える氷柱をのばすことができる』という作品です。
チーム代表の吉竹大輔さんによると「温暖化により失われゆく地球の神秘さや魅力を、メディアアート的なアプローチにより表現することを目指しました。ブースの天井をWiiRemoteを使ったライト型デバイスで照らすことで、天井からツララが伸び、水滴が床へ滴るなどのインタラクションが生まれます。複数人が協調してツララと関わり合う過程において、自然現象や環境問題を、そして自分たちが自然と関わり合う中で何ができるのかを考えるきっかけとなることを願い、この作品は制作しました」とのことです。
開発プラットフォームは、ViualStudio 2005(C++)、Bluesoleil 1.6.1で、オープンソースなどのAPIは使用せず、Windows Driver Kit(WDK)をつかって研究室のスタッフとともに開発したそうです。
インタラクション技術としては、ユーザーが天井を指すことにより、指した天井の位置を計算する点でなかなか難しいことを実現しています。仕組みとしては、床と垂直な平面(壁)のセンサ面に赤外線LEDを4点、正方形の角となるように配置し、その4点の座標をWiiRemoteで取得、それらの位置関係(正方形の変形の度合い)からセンサ面に対するWiiRemoteの入射角を画像処理ライブラリ「OpenCV」を使って求めているそうです。この角度と4点の座標から天井のどの位置をWiiリモコンが指しているかを推定し、天井のつららを制御するモータを回転させたり、床の光の波紋を発生させたりしています。
残念ながらコンテストでは東京予選で敗退してしまいましたが、作品開発の様子はYouTubeで見ることができます(http://jp.youtube.com/watch?v=waVNvmwKWaM)。
こちらもIVRC2008で発表された作品、ニオイの吹き矢で遊ぶゲーム「La flèche de l'odeur(ラ・フレッシュ・デ・ロドー)」。タイトルを日本語訳すると『ニオイの矢』。フランス語で『ニオイ・ダーツ』とも訳せます。金沢工業高等専門学校・小坂研究室による『飲食物を飲食しながら口臭を変化させ、口臭を用いてモンスターを倒す』ゲーム作品で、高専学生が匂いセンサーとWiiRemoteを組み合わせて開発したものです。
チームリーダーの金沢工業高等専門学校、国際コミュニケーション情報工学科岩本拓也さんにインタビューしたところ「人に不快感や嫌悪感をえる口臭に着目し、口臭を入力としたデバイス“吹き矢型デバイス”及びそれを応用したコンテンツ『La flèche de l'odeur』を提案しました。吹き矢型デバイスはプレイヤーが吹き込む息を計測することによって息の速さ、そして臭いセンサを用いて口臭の要素を計測し、WiiRemoteが吹き矢型デバイスの向きを検出しています。ゲームの中では、スクリーンに現れるモンスターを、吹き矢型デバイスを吹いて撃退していきます。モンスターには弱点となるニオイがあって、プレイヤーはプレイ中に実際にポテトチップやチーズなどの食べ物を飲食しながら口臭を変化させて遊びます。二人同時にプレイして、モンスターの弱点となる種類の口臭の吹き矢を上手に選べば、モンスターを上手に撃退することができます」といったコメントをいただきました。
ゲームとしての完成度は非常に高く、美麗なグラフィックスの最後に現れるラスボスは「(水を口でゆすいで)"清い息"で倒す」とインタラクションデザインも秀逸です。グラフィックスはDirectXで開発し、観客をリアルタイムで動画合成したり、扇風機を制御したりと、WiiRemoteの活用だけでなく演出面の技術的も高度なことを実現しています。
開発環境は、Windows Vistaに、Microsoft .Net Framework 2.0、Visual C#、Microsoft XNA、Microsoft DirectX August 2007という組み合わせで、WiiRemoteとの接続APIは「WiimoteLib」を使用しています。
コンテストでは見事最終選考に残り、総合3位にあたる「各務原市長賞」を受賞しました。なおこのWiiRemoteと臭いセンサーを使った吹き矢型のデバイスは特許申請中で、メディアアート作品の登竜門であるNHK-BS「デジタルスタジアム」で紹介されるなど高く評価されています。学生VRコンテストのスポンサーである岐阜県各務原市の名産「各務原キムチ」のニオイに注目して脚光を浴びせるなど、新たな展開も期待できそうです。作品の開発や体験の様子も動画で公開されています(http://jp.youtube.com/user/KosakaChannel)。なお、この作品の制作を監修した金沢工業高等専門学校の小坂崇之先生には、プログラミング入門編で協力を頂いております。
こちらも学生VRコンテストIVRC2008より、東京大学大学院の学生によるWiiFit「バランスWiiボード」を使った“文学作品”『人間椅子』を紹介します。この作品は情報理工学系研究科の家室証さんらによる、江戸川乱歩の小説『人間椅子』に着想を得たシステムです。『人間椅子』という短編小説のプロット『ある椅子職人が自分の作製した椅子の中に隠れ、上に座ってきた様々な人の感触を全身で楽しんだ』という物語、つまり、椅子の中に人間が隠れ、上に座ってきた人の感触を楽しむという体験を提供する恐ろしくも甘美な発想による作品です。システムは潜伏椅子と安座椅子の2つの椅子で構成されており、潜伏椅子に座った体験者は、まるで安座椅子に座っているもう1人の体験者が自分の太ももの上に座っているかのような感覚を得ることができるという設計です。
この怪しさ満点の作品のどこにバランスWiiボードが使われているかというと、安座椅子における座面への荷重の取得のために、2台のバランスWiiボードが用いられています。これによって得られる荷重情報を基にして、潜伏椅子に実装されたモータとベルトを用いた機構に、重さが提示されます。また同時に、太もも上におかれたパッド内のヒータの熱で太ももを温めることで、まるで本当に人が乗っているような温かさと重さが再現されます。
作品の最大の特徴は、小説『人間椅子』の体験を再現しようとしたことにあると言えるでしょう。このような「人に上に座られる」という体験から要素を抽出し、さらにバランスWiiボードという、安定して座っている人間の状態を取得できるデバイスを使い、2台の椅子によってシステムを構築することによって、この作品は他では味わえない「空間的・時間的に離れた人に座られる」という特異な体験を提供しています。
バランスWiiボードを用いた事により、荷重の取得を高速かつ安定に行うことが可能になっており、潜伏椅子に座った体験者は、安座椅子に座った体験者の動きをリアルタイムに感じることができる。バランスWiiボード自体は4本の脚に加わる荷重を独立に取得可能なため、体験者の両足の尻側、膝側という計4つの荷重を取得するには最低限1台のボードがあればよいのですが、様々な体験者の体型や座り方に対して安定に値を取得するため、このシステムでは2台のボードを用いて、1台につき片足の尻側と足側の2つの荷重値の取得を行っています。
このようにして得られた計4つの荷重値を基に、潜伏椅子に配置された4つのモータへの出力電流が決定され、モータを用いてベルトを巻き取り、太もも上に置かれたパッドを太ももに押しつけるというシンプルな構造によって、体験者の太ももに対して重さの提示が行われています。
バランスWiiボードを用いた荷重取得には、同じ東京大学の先輩、南澤さんが公開している「WiiBoard to PC ver.2.0」が使用されています。このサンプルプログラムによって、BluetoothでPCと接続されたバランスボードから、荷重値を取得することができます(http://minamizawa.jp/wii/)。
さて作品『人間椅子』はコンテストでは最終選考まで勝ち残ることができました。作品の様子はIVRCの公式サイトで見ることができます(http://ivrc.net/2008/)。
日本の学生の活動だけではありません。世界中の学生がWiiRemoteを使って新しいインタラクティブ技術を生み出しています。フランス西部のラヴァル(Laval)にあるENSAM(国立工芸大)Presence&Innovation研究所の学生さん、アレクシィ・ゼルーグ(Alexis Zerroug)は、『SoundQuest』というWiiRemoteの赤外線センサーを安価なモーションキャプチャとして使うことで『映像を全く使わないゲーム』を開発しました。『視覚を使わない』というコンセプトのテーマパークのアトラクション開発のためのプロトタイプで、フランスで毎年開催されているヨーロッパ最大のヴァーチャルリアリティのイベント「Laval Virtual ReVolution 2008」で発表されました。Wiiリモコンを天井に吊り、ユーザーは別のWiiRemoteが内蔵された無線ヘッドフォンを装着します。ヘッドフォンの上には赤外線マーカーが付いており、天井のWiiRemoteでユーザーの頭を検出できるモーションキャプチャとして利用しています。3次元音響空間の中にいるヴァーチャルキャラクターを探し出したり、手元のヌンチャクコントローラーを使ってインタラクションするというもの。
モーションキャプチャシステムは人間の動きを高速にとらえることができますが、高額な装置で、準備や装着に時間がかかるので、一般的には映像制作会社などプロ用途でしか使われていません。このプロジェクトが秀逸なのは、数百万円するモーションキャプチャを安価なWiiRemote複数台で作っている点です。天井から吊したWiiRemoteによって、頭につけた三角に配置した赤外線LEDのマーカーによって、ユーザーの頭の向きを検出しています。つまり、頭にヘッドホンを装着するだけで位置や方向が検出できるので、いろいろな応用ができそうです。
開発はVirtoolsという産業用ヴァーチャルリアリティプロトタイプ開発ツールで行っています。VirtoolsはちょうどFlashのようなコンテンツオーサリング環境なのですが、付属のSDKとC++をつかって独自のプラグインを開発し、機能を拡張できます。WiiRemoteと通信するプラグインを開発して、赤外線LED3点から向きを算出するプラグインを開発しています。
ちなみにこのシステムを開発したアレクシィ・ゼルーグ氏は筆者のフランス時代の教え子でもありますが、現在、東大に留学中です。開発の様子はYouTubeで公開されています(http://jp.youtube.com/watch?v=TMK7ULUG7S4)。
さて、ここまで世界中で取り組まれているWiiRemoteをつかった学生プロジェクトを紹介してきました。どのプロジェクトも、非常にエキサイティングです。また紹介しきれなかった面白い物もたくさんあります。初心者の読者にとっては、専門用語など難しい点もあったかもしれませんが、上で紹介した方々には後に続くパートで解説やサンプル作成に協力していただいておりますので、本書を読み進めていくことで、いずれ自分自身のアイディアを実現することもできるかもしれません。
さあ次は、皆さんの番です!
ここでは、WiiRemoteをPCで利用するための仕組みを説明します。ハードウェアやソフトウェア、その中間にあるミドルウェアなどの知識がある方は、読み飛ばしていただいてもかまいません。
まずは主に使用するハードウェアを解説します。
もしお使いのPCが標準でBluetoothを装備しており、かつ後に紹介するWiiRemoteとの接続実験に成功するのであれば、新たに何かを購入する必要はありません。PCにBluetoothホストアダプタが装備されていない場合には、PCパーツ店、電器店、通販等で購入してみてください。だいたい2,000円代ぐらいからUSBタイプのアダプタが入手可能です。WiiRemoteと通信を行うだけの目的であれば、最新・高級品である必要はありません。ただし、各種Bluetoothスタックによって接続に癖があるので注意が必要です(次節を参考にしてください)。
もしWiiRemoteを複数使用するのであれば、ゲームショップなどで追加購入可能です。必要であればセンサーバーも任天堂ホームページで購入できますし、サードパーティから様々なセンサーバー互換品が発売されています。
さて、次はソフトウェア構成です。「WiiRemoteをPCで利用する」といっても、C++やC#などを使ってディープに開発したり、既にコンパイルされているツールを使ったり、Flashなどの外部のアプリケーションと連動させたりと、いろいろな方法があります。
下の層から「WindowsPC」、「Bluetoothスタック」、「API」と「Win32」、そして「アプリケーション/ツール」となっています。下に行くほどよりハードウェアに近く、上に行くほどよりアプリケーションに近いソフトウェアになります。ソフトウェア用語で、OSより上層、アプリケーションより下層のソフトウェアを「ミドルウェア」と呼ぶことがあります。WiiRemoteをPCで使うプログラムでは、このミドルウェアが非常に大きな役割を受け持ちます。
WindowsPCプラットフォームにおいて、ミドルウェア部分は、突き詰めればWin32の関数、主にDDK(Driver Development Kit)やWDK(Windows Driver Kit、DDKにテストツールが統合された)を利用してコーディングされています。
本書では、上記の各種APIを利用した様々なアプリケーションの書き方を紹介します。大きく分けると、C#.NETやC++を使った様々なアプリケーション、WiiFlashというBluetooth-ネットワーク間のプロクシを行うソフトウェアを使ったFlash上での利用、そしてWiiFlashの通信をProcessingで利用する例の4種類です。
もちろん他にもJavaやVisual Basic、Pythonを使ってアプリケーション/ツールを開発することも可能ですが、本書では主として扱いません。しかしWiiRemoteを使う上での基本は他の言語・環境でも変わりません。ゲームやアート作品など、アイディアを実現する上で「Windowsじゃなきゃダメ」、「C#/C++でなければダメ」ということはないのです!皆さんが得意としているOpenGLやSDL、DirectX、Win32やMFC、コマンドラインプログラムやC++/CLI、VB、C#、Java、Processing、各種スクリプト言語、そしてFlashやMaxMSPといった様々なツールやコンテンツクリエイション環境で利用することができることが報告されています。
以上のように「WiiRemoteをPCで利用する」とひとことで言っても、幅広く、全てを網羅するにはハードウェアの知識が必要になります。
特にグラフィックス中心としてきたゲームプログラマーにとっては、APIより下層のことを考えなければならない状況は少々苦しいかも知れません(ちょうど上の図もDirectXより下のGPU(グラフィックプロセッサ)を直接コールするようなイメージがあるでしょうか)。しかし、WiiRemote登場当初に比べて、より安定して高機能なAPIが数多く登場していますし、なにより任天堂がコンシューマープラットフォームとして開発製造しているWiiRemoteは、非常に安定したハードウェアです。
そういう意味でもWiiRemoteはデバイス・ハードウェア寄りのプログラミングを学ぶにはうってつけの環境とも言えるでしょう。玄人のグラフィックスプログラマーさんにとっても、学ぶこと、活用できることは沢山あるはずです。
「WiiRemoteをPCで利用するのは違法では?」ときく人がいます。いきなり「違法」なんて決めつけられるとドキっとしてしまうのですが、端的に筆者の個人的見解を述べると「No」です。将来的には違法となる可能性もあるかもしれませんが、日本を含めた国によっては違法性を問うのは難しいと考えます。まずWiiRemoteの使用にあたり、使用者は何の契約もしていません。インストール時などにEULA(エンドユーザーライセンス承諾書)を読み、合意しているわけでもありません。
基本的には「ユーザーのリスクにおいて、サポート外の行為」であるといえます。それを行うことで発生する不具合や故障の修理代や損害賠償をユーザーが請求しなければ、自由です(本書も同様、何の保証もありません!)。もし仮に、発売側にそれ以上の権利が存在するのであれば、パッケージを開けることで成立する「シュリンクラップ契約」などで購入前に明示されるべきでしょう。
しかし業務的に「Wiiプラットフォームにおいてゲームを開発している開発者」は別です。このような開発者は、それぞれの所属する企業と任天堂やゲームソフトを実際に販売するパブリッシャ間において、守秘義務契約が結ばれています。ボランティアや自分の技術的興味で活動している世界中のハッカーとは本質的に立場が異なります。
本書の読者の多くに該当するであろう、ホビープログラマにおいて、気をつけるべき事は「実質的な加害者にならないこと」ではないでしょうか。「WiiRemoteを車の運転に使ってみた」なんてウケ狙いで実験するのは自己責任ですが、もしその車が事故を起こして、人の命を奪ったとしたら……?そんなことにならないように、気をつけてくださいね!
WiiRemoteを使ったPC上のプロジェクトの開発や実験をする上で、最も良いスタートを得る方法は『実績のあるBluetooth製品を選ぶこと』です。コンシューマーゲーム機周辺機器とはいえ、ここから先は何の保証もない世界です。先人の知恵を共有し、不要な労力を避けるためにも、まずは安定して動作する環境を準備しましょう。
ここでは前節で紹介した内容をより具体的に、WiiRemoteをWindowsPCで利用するためのBluetooth接続について解説します。Bluetooth接続に関わる問題は、PC上でWiiRemoteでいろいろなアプリケーションを開発する上で、常に頭を悩ませるブラックボックスとなることが多いので『自分の環境では問題なく使えているよ!』という人もこのステップで、知識として知っておくことをお勧めします。
世界中のハッカーによるレポートを読んでいると、「IVT BlueSoleil(ブルーソレイユ)」Bluetoothスタック&ドライバが最もよく使われているようです。IVT社のホームページによると、1999年からBluetoothソフトウェア製品の開発をリードし続けており、BlueSoleilは2008年4月の情報では2500万ライセンスが販売されているそうです。実際、BlueSoleilはBluetoothホストアダプタを製品にしている周辺機器メーカーにOEMとして採用されていて、多くの製品において、購入したUSB Bluetoothアダプタにドライバとして同梱されています(一部機能限定版の場合もある)。もちろんBlueSoleilのホームページにおいてオンライン購入することができます。
2009年に公開されているメジャーバージョンは「BlueSoleil 6」シリーズで、価格は19.95ユーロです。WiiRemoteとの接続以外にも、携帯電話との接続やワイヤレスヘッドセットなどにも利用できますので、手持ちのBluetooth製品が不満で、かつ運良くBlueSoleilがサポートしていれば、買っても損はないでしょう(特にWindowsXPからVistaに乗り換えた場合など)。BlueSoleilのホームページから「Download」を選ぶと製品版と同じソフトウェアをダウンロードできますので、動作が確認できたらライセンスを購入すると良いでしょう。最近ではLinux版やCE版も発売されているようです。
DELLやLenovoなどのノートPCに装備されている内蔵Bluetoothスタックとして、よく使われているものは、OEM供給されている「TOSHIBA製スタック」です。Bluesoleilとは若干異なった挙動をするため注意が必要ですが、操作もシンプルで扱いやすく、WiiRemoteとの接続は可能なものが多いようです。筆者が発見した東芝製スタックの問題は「4つ以上のWiiRemoteと同時接続できない」という点です(ソフト的な問題なので将来的には解決するかもしれませんね)。
Windows XP Service Pack 2以降やWindwos Vistaには、コントロールパネルに「Bluetoothデバイス」というアイコンがあります。こちらはMicrosoft製のBluetoothスタックで、対応しているBluetooth製品と対応ドライバがインストールされていると動作します(存在しない場合はコラム「Bluetoothコントロールパネルがないときは」を参照)。過去にこのMicrosoft製スタックはWiiRemoteとは相性が悪いと言われていました。Windows Vista環境においてはペアリングに失敗する、接続できても値の取得に失敗する、といった多くの不具合が報告されていましたが、最近になって製品付属のドライバやWindowsUpdateなどを経由して多くの問題が解決されてきているようです。特にペアリングにコツがあり『接続が完全に終了するまで』1ボタンと2ボタンを『押しっぱなしにすること』でうまく接続できます。この現象は、MicrosoftのBluetoothスタックが、サービスを列挙しPIN(≒パスワード)を求めている間に、WiiRemoteの同期モードが終了してしまうということが原因のようです。この間の悪い時間切れ現象に対して、WiiRemoteの「1,2ボタンを押し続ける」ことで同期モードを継続し、上手に接続することができます。
WindowsXP Service Pack2以降もしくはWindows Vistaをお使いの方で、コントロールパネルに「Bluetooth」のアイコンがない場合について、Microsoftのナレッジベースにいくつかの対処方法が公開されています。
可能性としては「Bluetoothサポートサービス」が開始されていないか、そのサービスがローカル管理者アカウントを使用するように、構成されていないということです。
Windowsキー+Rを押して「ファイル名を指定して実行」ダイアログを使い、「services.msc」と入力し管理コンソール(MMC)スナップインを開きます。[Bluetoothサポートサービス]が停止している場合、ダブルクリックしサービスを開始させます。Bluetoothを常に使うのであれば「スタートアップの種類」を「自動」にすると良いでしょう。さらに「ログオン」タブをクリックし「ローカルシステムアカウント」が選択されていることを確認してください。最後に、コンピュータを再起動して動作を確認してみてください。
Broadcom社はBluetooth業界では大手です。WiiRemote本体の中に使われているBluetoothコントローラーチップはもとより、周辺機器として販売されているBluetoothアダプタや、最近のThinkPadの内蔵品などさまざまな製品にOEMとして採用されています。Microsoft製のスタックと統合されたドライバして組み込まれていることが多く、ユーザーは気がつかないこともあるかもしれません。
実はかつて、Broadcomのスタックは「Widcomm」という製品名で展開されており、WiiRemoteとは相性が良くないといわれていましたが、最近では問題なく接続できる製品が多く登場しています。
以下は筆者が利用しているWindowsXP搭載ThinkPadにおけるコントロールパネルの例です。「コントロールパネル」には「Bluetooth設定」というアイコンがあり、「診断」タブを見ると「Broadcom Corporation」、「ファームウェアリビジョンVersion 2.1.211.299」と表示されています。
このコントロールパネル統合型のBluetooth管理ソフトウェアは、タスクトレイなどに常駐したBluetoothアイコンから接続するタイプの他社製スタックとは異なり、「マイコンピュータ」の「マイBluetooth」から接続する、エクスプローラー統合型になっているという特徴があります。
その他、上記で紹介のないスタックについては、試してみたが現在のところ成功していない、WiiRemoteとのペアリングが成功しない、製品寿命が終了している、日本で使用できないなど、様々で本書では取り扱いませんでした。もし手持ちのBluetooth製品で成功しているものがあれば、WiiLi.orgなどで共有した方が良いでしょう。なお輸入した無線デバイスを日本国内で使用することは電波法に違反する可能性があります。技術基準適合証明ラベルが必要です。
また本書ではメインで扱いませんが、Windows以外の環境ではMacOSとLinuxでも比較的簡単にWiiRemoteが利用できます。
MacOSではBluetoothはOSの標準機能で利用でき、接続ツール、アプリケーションなど様々なソフトウェアが登場してきています。「OSX Wiimote Enabler」という、近くにあるWiiRemoteを見つけてペアリングしてくれるソフトウェアなどは便利そうです。
Linux環境ではBluetoothとの接続に特別なソフトウェアは必要ありません。特に最近急速にユーザ数が増えているディストリビューション「Ubuntu」ではUbuntu7.10(Gutsy)以降、標準的なソフトウェアで利用できるようになってきています。
もともとはWiiでLinuxを動かそう!というプロジェクトのポータルですが、かなり初期に立ち上がったこともあり、WiiRemoteに関する情報もたくさん扱われています。こちらのページには動作確認が取れたBluetoothデバイスのリストがあります。http://www.wiili.org/index.php/Compatible_Bluetooth_Devicesなお過去に作動しなかったBluetoothデバイスでも、ソフトウェアアップデートにより、動作する可能性があるとされています。
実は任天堂も公式にWiiRemoteを一般のユーザーが利用するための仕様を公開しています。Wii本体のWebブラウザ機能である「インターネットチャンネル」におけるWiiRemoteです。これに関しての技術仕様は任天堂のホームページ「インターネットチャンネルの拡張機能について知りたい」(http://www.nintendo.co.jp/wii/q_and_a/093.html)に掲載されています。
技術仕様にはJavaScriptによるボタン情報やセンサーバーをつかったカーソルの位置や傾きの取得方法が記載されています。また「利用上の注意」として「当社は、この拡張機能に関して、一定の商品性を有していること、特定の目的への適合性を有していること、第三者の知的財産権(特許権、著作権、商標等)を侵害していないこと等を含め、一切の法律上の保証を行いません。この拡張機能を使用したことによって被るいかなる損害に対しても、当社は責任を負いません。当社は、この拡張機能を使用する方に対して、いかなる権利をも付与するものではありません。当社は、この拡張機能に関するサポートは一切行っておりません」と記載されています。
法的な見解上のグレーはグレーのまま、としておくほうが良いこともあります。「公式に保証しません、責任を負いません」と言い切った方が、現代のネット社会の文化に合っていて潔いという見方もあるでしょう。
いずれにせよ現在のこの設計のおかげでWiiRemoteは開かれたプラットフォームとして利用することができるわけですし、WiiRemoteを旧来のゲームコントローラー以外の使い方をすることで、結果としてWiiRemote単体の売り上げには協力できているのかもしれません。
今後もWiiRemoteファンと任天堂、開発者の間に、間接的ではあるけれどWin-Win-Winの関係が保てるといいですね。
WiiRemoteをつかったプログラミングの根幹にある"PCとの接続"は、Bluetoothスタックを経由したHIDクラスの利用であるため、ブラックボックス的要素が多くなってしまいます。初期のWiiRemoteプログラミング環境は不可解な動作やトラブルといったことに悩まされる状況が非常に多くありました。公開されているフリーウェアなどもアプリケーション作者が自分の環境で利用しているハードしか試しておらず、特定の環境でしか動かない…といったこともありました。最近ではWindows XP SP2以降のサポート向上により、かなり状況は改善されていますが、それでも「確実に接続実験をしたい」という相談をよく受けます。
筆者が個人的に愛用しているのはプリンストンテクノロジー社の「PTM-UBT3S」です。とても小さなUSBコネクタサイズのBluetoothホストアダプタで、電車の中でプログラムを書く事が多い筆者は(左右の乗客にUSBがぶつからず)とても重宝しています。同梱されているスタックは東芝製で、Windows Vistaでの動作確認もとれています。
現在はPTM-UBT3Sは販売終了とのことで、現在ではその後継として、さらに100メートルの最大通信距離、Bluetooth Ver2.1+EDR対応の「PTM-UBT5」が発売されています(http://www.princeton.co.jp/product/network/ptmubt5.html)。★動作確認予定。
本当はBluetooth製品それぞれに動作確認情報を出せれば良いのですが、調べてみると製品のパッケージには同梱されているスタックの種類までは記載されていません。調査しても、製品のバージョンやリビジョン、インストールされる側のOSによって全く異なる、というケースもあります。おそらく同梱するソフトウェアのライセンス料が製品価格の大きな部分を占めるからでしょう。
幸いなことにBluetooth製品は日々、低価格化が進んでいます。購入に失敗したら買い直してもそれほど痛い価格ではなくなってきました。
ソフトウェアのアップデートで使えることもありますから、まずは「案ずるより、買ってみるが易し」でしょうか。
ここでは前節で紹介したいくつかのBluetoothスタックを使って、実際にWiiRemoteをPCに接続する方法をステップバイステップで解説していきます。すでにお使いのBluetooth環境で問題なく接続できている読者は読み飛ばして、次節のツールをつかった実験に進んでいただいてもかまいませんが、初めて実験するときや、新しく買ったBluetoothアダプタを利用する場合は、ぜひ関連する種類の解説を一読することをお勧めします。ドライバ付属のウィザードだけではどうしても接続できない「ちょっとしたコツ」があるときがあります。
現在、多くのBluetoothホストアダプタはUSBインタフェースの形状をしています。インストールは製品付属のマニュアルを良く読んで行ってください。一般的なUSBメモリなどと異なり、Bluetoothホストアダプタは初めて挿入する「前に」、付属ソフトウェアのインストールを必要とする場合がほとんどです。
何も考えずに、BluetoothハードウェアをUSBポートに挿入してしまうと、自動でドライバーを設定されてしまったりして、ソフトウェアのセットアップで詰まることになります。まずは、製品に同梱されているマニュアルを一読しましょう。多くの場合はまず、ソフトウェアCD-ROMのインストーラーを使って、ソフトウェアをセットアップすることになるはずです。
またBluetooth機器のドライバーはサービスとしてインストールされるものが多いので、インストール後は必ず再起動しましょう。またインストール時のオプションで、Bluetoothヘッドホンなどの音声関係のサービスや、ファイル転送関係のサービスを選択できる場合があります。もしWiiRemoteだけで利用するのであれば、これらのサービスは全く必要が無く、使用しませんので、追加インストールしたり、サービスを自動起動する設定にする必要はありません(メモリや処理の節約になります)。
無事にインストールが終わり、再起動したら、タスクバーやデスクトップ、マイコンピュータ内にあるBluetoothアダプタのアイコンから、Bluetoothスタックの様々なサービスを利用できるようになります。
ここでは手軽に入手できるUSB外付けタイプのBluetoothアダプタ「PTM-UBT3S」(プリンストンテクノロジー)を使って、ステップバイステップでWiiRemoteとPCのペアリングを解説します。
筆者が購入したPTM-UBT3Sに同梱されていたスタックは東芝製でした。タスクバーの時計の近くにあるBluetoothのアイコンをダブルクリックすると「Bluetooth設定」というウインドウが起動します。メニューの「ヘルプ→バージョン情報」を選ぶことで「Bluetooth Stack for Windows by Toshiba Version v5.10.15」といったバージョン情報を確認することができます。
この東芝製スタックはDELLの一部の機種にも採用されており、WiiRemote登場当初から安定した接続が行えていることで有名でした。早速、WiiRemoteと接続してみましょう。
全てのBluetooth機器は「ペアリング」という接続認証をホスト側と連携して行う必要があります。これは様々な機器が混在する近距離無線通信において、適切な周辺機器が、適切なユーザに確認された上で、接続されることを保証するためにあります。携帯電話などの機器は、勝手に接続されると電話帳の閲覧や、発信などを扱えてしまうため、接続にPIN(パスコード)が必要になることが多いのですが、WiiRemoteの場合には、害の無いただの入力インタフェースなので、PINは設定されていません。基本はホスト側とタイミングを合わせて同期ボタンを押すだけでペアリングできます。
「Bluetoothの設定」のメニューから「新しい接続」をクリックすると「新しい接続の追加ウィザード」が起動します。「エクスプレスモード(おすすめ)」のまま次にすすめましょう。
「Bluetooth機器を探しています」と表示されたら、すかさずWiiRemoteの「1」ボタンと「2」ボタンを同時に押しましょう。WiiRemoteの電池フタを開けたところにある、赤い「Syncボタン」でもかまいません。プレイヤーインジケータ(WiiRemote下部にある4個の青色LED)が点滅し、外部からの接続要求を数秒間の間、受け入れることを意味する「接続認証待ち」の状態になります。
次に「使用するBluetooth機器を選択してください」というダイアログが表示され、図のようにデバイス名の一覧に「Nintendo RVL-CNT-01」が現れます。これが表示されたらクリックしてWiiRemoteを選択し、「次へ」をクリックしてください。
「Bluetooth機器に接続しています」というダイアログが表示されたら、WiiRemoteをみて、青色LEDが点滅していることを確認します。もし点滅していなかったら、再度「1」ボタンと「2ボタン」を同時に押しっぱなしにして、接続認証待ちの状態を保ってください。
PINコードは必要ないので、自動的に接続完了の状態になるはずです。これで無事接続できました。途中何度か「1」「2」ボタンを押しっぱなしにするところがありましたが、WiiRemoteの裏側にある電池フタ内部にある「Sync」ボタンでも同じ操作ができます(まったく同じ機能です)。
さて、WiiRemoteには節電機能があり、しばらくの間、通信やボタン操作がないと、自動的に接続を終了し、電源断の状態になります。この切断状態では、Bluetooth管理ソフトウェアでの表示は下のような表示になります。
この状態は「WiiRemoteは登録されているが切断されている」という状態です。ダブルクリックすると、
「HIDデバイスの接続の準備をしてからOKボタンを押してください」と表示されます。ここで再度WiiRemoteの「1」,「2」ボタンを同時押しして、接続認証待ちの状態(4つの青色LEDが点滅)にしてから、OKボタンを押せば再接続できます。
なお複数のWiiRemoteが混在する可能性がある場合、不要な接続設定は右クリックでメニューを表示し「削除」しておくと良いでしょう。うまく接続できないときも、一旦設定を削除して、最初のステップからやり直すと良いでしょう。
また右クリックのメニューから「詳細」を表示することができます。ここにはWiiRemoteの個体番号「デバイスアドレス」などが表示されています(ネットワークカードのMACアドレスに相当する固有のID)。複数のWiiRemoteが混在する環境だと、個々のWiiRemoteを見分けるにはこのデバイスアドレスがたよりです。下4桁などをシールなどにして貼っておくと混乱しなくて良いでしょう。
次に、代表的なノートPC「ThinkPad」標準搭載Bluetoothアダプタでの接続例を紹介します。ここでは筆者が使用しているWindowsXP搭載ThinkPad X61を例にしています。
「コントロールパネル」には「Bluetooth設定」というアイコンがあり、「診断」タブを見ると「Broadcom Corporation」、「ファームウェアリビジョンVersion 2.1.211.299」とありました。
もしこの段階で正しく表示されていない場合、Bluetoothアダプタが故障しているか、無線機能がハードウェアスイッチでOFFになっている、BIOSで有効にされていない、もしくは、デバイスが存在しないといった可能性があります。特にBIOSやソフトウェアスイッチでの無効化は見落としがちなので、確認してみると良いでしょう(無線の状態はFn+F5キーで確認することができます)。
次に「クライアントアプリケーション」のタブをクリックしてみましょう。これから利用する「HID(Human Interface Device)」のセキュリティ保護が「不要」になっていることを確認してください。
無事に作動しているようでしたら、実際に接続してみましょう。「マイコンピュータ」に「マイBluetooth」というアイコンがあるはずですので、ダブルクリックして開いてください。最初は何も表示されませんので、WiiRemoteの「1」と「2」のボタンを押して、検索可能状態にした状態で「範囲内のデバイスの検索」を実行してみてください。
近くにWiiRemoteや携帯電話などペアリング可能なデバイスが存在するとアイコンが表示されます。このアイコンにマウスポインタを近づけたり、プロパティを見るとデバイスアドレスを見ることができます。
さて、ここでつい、このアイコンをダブルクリックして「デバイスの接続」を実行してしまいがちなのですが、この方法では永遠にWiiRemoteとのペアリングを実現することはできません。その流れでいくと、ペアリングのためのPINコードを必須として要求されるのですが、WiiRemoteには「空白のパスワード」が設定されているため、空白のパスワードを受け付けないこのスタックでは認証ができないのです。これはおそらくBroadcomのスタックにおけるバグか仕様ミスなのですが、ちょっとしたコツで接続する方法があります。
まず、一度、検索結果の個々のデバイスアイコンではない空白部分をクリックしてください。すると左側のタスクの一覧に「Bluetoothデバイスの追加」というタスクが現れます。これをクリックすると「Bluetoothセットアップウィザード」が起動します。
検索を開始し、WiiRemoteが現れたらそのアイコンをクリックして「次へ」のボタンを押します。ここからWiiRemoteの「1」と「2」のボタンを押しっぱなしにして指をはなさないでください。
PINを要求する画面が表示されますが、WiiRemoteのボタンを片手で押したまま「スキップ」をクリックします。そのまま3秒ほど待ってみてください。
すると、エラー画面は表示されず、無事、WiiRemoteのHIDサービスを選択する画面が表示されます。チェックボックスをONにして「完了」してください。
この流れで無事Broadcom社のスタックも利用できるようになります。PINが要求される画面で「1」+「2」ボタンを押しっぱなしにしておくのがポイントです。再接続の場合も同じ手順で接続できますが、一度ペアリングに成功していれば、マイコンピュータ内のマイBluetoothアイコンから、接続したいWiiRemoteのアイコンをクリックして「選択したBluetoothデバイスの追加」として、「1」と「2」ボタンを押しっぱなしにしておけば、短いステップで再接続できます。
なお、この後「マイBluetooth」ウィンドウには何も表示されませんが、「範囲内のデバイスの表示」というBluetoothタスクをクリックしてみてください。「マイBluetooth\Bluetooth Neighborhood全体」という場所にはリンク状態を示す「→←」が付加されたWiiRemoteのアイコンが表示されているはずです。切断する場合はデバイスのアイコンをクリックして「無効化」を選んでください。再接続は、デバイスのアイコンをクリックしてBluetoothタスクから「選択したBluetoothデバイスの追加」を選ぶと、ウィザードが再度起動します。上記の手順と同じく、「1」,「2」ボタンを押しっぱなしにして、「スキップ」を選んだ後、そのままボタンを押し続けてHIDサービスが表示されるまで3秒ほど待つのを忘れないようにしてください。
以上で、ThinkPadに搭載されているBroadcom社のエクスプローラー統合型のスタックにおける接続方法の解説を終わります。長年の実績と堅牢さで人気のThinkPadですが、筆者の使用している環境はいささか古いのかもしれません。システムのデバイスマネージャーからBluetoothデバイス内部のドライバのプロパティを見ると、メーカーは確かにBroadcomですが、日付が「2006/12/19」、バージョンが「5.1.0.2900」となっております。
Broadcom社はBluetooth業界では大手で、WiiRemote本体にもそのチップが搭載されています。またBroadcomが供給するドライバソフトウェアは周辺機器として販売されているBluetoothアダプタや、多くのノートPCに採用され、ドライバのバージョンも日々進化しており、WiiRemote登場当初に比べて、下層のドライバには何の不満もないのですが、ときおり翻訳上の表現やGUIの動作で意味不明なところもあります(そもそもWiiRemoteを接続するテストをドライバ開発者が標準的に行っているとも思えませんが...)。今のところHIDサービスが列挙されるまで『1+2を押しっぱなしにしないとタイムアウトする』というテクニックは共通のようです。筆者が体験した面白いBloadcomスタックの経験としては、デバイスの列挙時に文字化けを起こし、ボタンも表示されず、何をして良いのかわからない、という製品もありました。こんな時は「Alt+C」や「Alt+S」、「Alt+N」など「スキップ」や「次へ」にあたるショートカットを試してみるとよいでしょう。
続いて、最近話題のネットブックの代表ともいえるASUS社製「EeePC 901」でのペアリングを紹介します。内蔵されているBluetoothアダプタはThinkPadの例と同じくBroadcom社ですが、バージョンが異なり、よりWindowsのエクスプローラーに統合されています。接続はできるのですが、ちょっとしたコツがありますので、初期設定から解説します。
まずは、初めてBluetoothで接続する場合、コントロールパネルの「システム」から「デバイスマネージャ」を起動し、どのようなBluetoothデバイスがインストールされているのか確認しておくと良いでしょう。
さて、このPCで初めてBluetoothを使用する場合、初期設定が必要になります。コントロールパネルの「Bluetooth設定」をダブルクリックすると、初期設定のためのウィザードが起動します。
「WIDCOMM Bluetooth Software 5.1.0.5500」と小さく表示されていますが、まずはこのPCのBluetoothホストとしての名前とコンピュータの種類を設定します。ここでは「Eee」という名前の「ラップトップ」としました。続いてサービスの設定に入ります。
ここではWiiRemote都の接続に必要なサービスはありませんので、全てのチェックを外した状態で「次へ」進みます。サービスは後でもコントロールパネル「Bluetoothの設定」から「ローカルサービス」で設定することができますので心配は要りません。
さあ、初期設定は終わっています。ウィザードはそのまま続いてデバイスとの接続を行うことができます。WiiRemoteを準備して「次へ」進みます。WiiRemoteの「1」ボタン「2」ボタンを同時押しして、接続待機モード(青色LED4つが点滅)の状態にします(1+2ボタンからは手を離さないほうが良いでしょう)。タイミングが合わないときはウィザードの「再検索」を押してみましょう。
マウスとキーボードのアイコンと共に「Nintendo RVL-CNT-01」が発見されたら、すばやくクリックして「次へ」を押します。このとき、つい「1」+「2」ボタンから手を離してしまうことが多いので気をつけてください。
ここから少し素早く手を動かす必要があります。ダイアログが表示されペアリングに入ります。「今すぐ組み合わせ」と表示されていますが、実はPINを持たないWiiRemoteとはこの流れでは接続できません。ここでは「スキップ」を選択します。するとしばらく何も表示されないので、「1」+「2」ボタンを押しっぱなしにしていてください。
無事にWiiRemoteのサービス列挙に成功すると、図のように「Nintendo RVL-CNT-01:Bluetooth対応マウス、キーボード、その他のインタフェースデバイスを使用します。」と表示されます。「1」+「2」ボタンを押してるので大変かもしれませんが、チェックして「次へ」を押しましょう。
さて、これで無事接続完了です。WiiRemoteのボタンから手を離しても大丈夫です。「マイBluetooth\Bluetooth Neighbourhood全体」というウィンドウにWiiRemoteを表す「Nintendo RVL-CNT-01」が表示されているはずです。
ダブルクリックすると、ステータス表示できます。ステータスには信号強度やアクティビティが表示されます。WiiRemoteのボタンを押すと受信データが増える様子を見る事ができます。なお他のスタックのようにMACアドレスを見ることはできないようです。
右クリックで「無効化」を選ぶと切断状態になります。再度接続するときは「マイコンピュータ」の「マイBluetooth」から「Bluetoothデバイスの追加」というウィザード形式のものを選んでください。「Bluetoothデバイスの検索」や「Bluetooth Neighborhood全体」を選んでも良さそうなものなのですが、ペアリングの「スキップ」にバグがあるようで、先に進むことができません。
再接続であっても「デバイスの追加」ウィザードを選ぶ、という点にだけ気をつけてください。接続の流れ自体は再接続でも全く変わりません。「1」+「2」ボタンを押しっぱなしにするのを忘れずに。接続されている場合は「Bluetooth Neighborhood全体」に表示されます。
さて、無事にBluetooth接続の流れが理解できましたでしょうか?最後に紹介したThinkPadとEeePCの例はBroadcom社のスタックにおけるバグのようなものがあり、ペアリングに「ボタン押しっぱなし」や「再接続時もウィザード」などのコツが必要でしたが、一度覚えてしまえば難なく利用できます。特にネットブックではVisual Studioを使った開発などはあまり現実的ではありませんが、標準でBluetoothアダプタを内蔵しているので、展示やプロジェクトなどでは便利に使えることもあるでしょう(何より安価です!)。
ここで紹介したBluetooth接続がうまくいかなかった方は、できればこのステップで使えそうなものを調達しておくことをお勧めします。無事にBluetooth接続に成功した人は、そのまま次の節の実験に進んでください。
BlueSoleilは、WiiRemote登場当初は欧米で最も動作実績のあるスタックでした。専用の接続アプリケーションが特徴的で、特に難なく接続できるので解説はいたしません。
またWindows Vistaにおいて、一時期、WiiRemoteが接続できず、あきらめていた人もいるかもしれませんが、SP1以降から動作することが報告されていますので、あきらめずに試してみるとよいでしょう。
ちなみにMacOSやUbuntuなどのLinuxではOSの標準の機能だけで問題なく接続できます。
WiinRemoteは2006年12月2日、Wii本体が発売されたその日に公開され、世界でもっとも有名になったWiiRemoteをPCで利用できるフリーウェアです。tokkyoさんによって開発され「おなかすいた族」(http://onakasuita.org/wii/)にて公開されています。WiiRemoteを使ってWindowsのカーソルを動かしたり、加速度センサや赤外線センサの状態を観察することができます。
2007年1月13日に公開された「WiinRemote_v2007.1.13.zip」が現在のところ最終版で、Borland Delphi 6によるソースコードも公開されています。最も早く公開され、ソースコードが貢献しているだけでなく、ツールとしても様々な機能が安定して利用できるので、現在でも多くのユーザに利用されています。
WiinRemoteは「おなかすいた族」(http://onakasuita.org/wii/)にて無料で配布されています。「--ダウンロード」の最も新しいバージョン「WiinRemote_v2007.1.13.zip」をクリックしてダウンロードします。ダウンロードしたファイルを解凍します。場所はどこでも良く、インストールは不要です。あとは「WiinRemote.exe」を実行するだけです。
前節で無事にWiiRemoteとBluetooth接続接続できていれば、スクリーンショットのように加速度の値を示すグラフが表示されます。WiiRemoteをブンブンと振って、動きが変化することを確認しましょう。
WiinRemoteには様々な機能があります。WiiRemoteの基本的な入力情報を確認するだけでなく、マウスの代わりとしてある程度の操作ができるようになっています。まず、いろいろ試す前に「Ctrl+S」(コントロールキーを押しながら「S」)で、マウス入力機能のOn/Offを切り換えられるのを覚えておくと慌てないで済みます。
左側のWiiRemoteの写真が、現在の押されているボタンを表示しています。ボタンが押されると薄ピンクで表示されます。
「Motion Sensor」と表示されているエリアが加速度センサーのリアルタイムの値です。Blue,Red,GreenがそれぞれWiiRemoteのX,Y,Z軸に割り当てられています。次のセクションではこれを使ってちょっとした物理の実験を行います。「Motion Sensor」の右側に表示されている黒い線が、推定されたWiiRemoteの「姿勢」を表しています。
「IR Sensor」では、赤外線センサーが取得した座標値を表示しています。いま手元に純正のセンサーバーがなくても、WiiRemoteを使って周りを探してみてください。何か反応する物があるかもしれません。身近な物では例えば、太陽や白熱電球などが赤外線を発しています。
ヌンチャクコントローラーが接続されている場合「Nunchuk」のエリアに加速度センサーとアナログスティックの値が表示されます。
メニューの「Options」で「Preferences」もしくはCtrl+Pで、設定画面が表示されます。
左から、「General」の「Cursor」で、カーソルモード時のマウスポインタを何で操作するかを選べます。デフォルトは加速度センサーによる傾きです。センサーバーを使わなくてもよいのですが、これはあまり操作しやすいものではありません。赤外線で操作する「IR Sensor」もしくはヌンチャクのアナログスティックで操作する「Nunchuk Analog Stick」をぜひ試してみてください。その他のチェックボックスの機能は以下の通りです。
項目 | 意味 |
---|---|
Enable Cursor at Startup | カーソルモードを起動時から使用 |
Minimize at Startup | 起動時に最小化 |
Draw Graph | グラフを描画 |
Rumble on Edge | マウスカーソルが画面端に来たときにバイブレータを振動 |
「Motion Sensor」項目は、加速度センサーでマウスを操作するときに必要になるパラメーターです。
項目 | 意味 |
---|---|
Motion sensor threshold | 加速度センサーの微少な値をどこまで無視するか |
Invert Horizontal | 水平方向を逆転 |
Invert Vertical | 垂直方向を反転 |
Cursor Speed | カーソル移動速度の最速と最小(左にすると遅い) |
When button pressed | Bボタンが押されているときカーソルを速い/遅いを選択 |
Re-Calibrate on Cursor On/Off | カーソルモードの切り替え時にキャリブレーションを実行 |
「Button Assign」では、WiiRemoteの各ボタンに機能を割り当てられます。シンプルですがなかなか強力な機能で、例えばButtonを「HOME」、Assignを「Keys」にして、「Keys」にある「Press Keys Here」をクリックして「Alt」キーを押してから「F4」キーを押してみましょう。その下に「Alt+F4」と表示されたら「Set」を押します(最後に「Apply」キーを押して設定保存)。これでHOMEキーを押したら、そのウィンドウを閉じる機能ができました。同じような手順で「+」「−」キーに音量を割り当てたり、「1」「2」キーに好きなアプリケーションを割り当てることができます(「Execute」で実行ファイルを選んでから「Set」するのを忘れずに)。
「IR Sensor」では「赤外線が見えないときは加速度センサーを使う」というチェックボックスと、LEDが視界の外に出た場合の安定性を調整するための「見失った時の調整枠を表示(垂直方向)」があります。
「Nunchuk」ではアナログスティックをカーソルモードで使用した場合の速度と、上下左右方向の逆転用チェックボックスがあります。
さて、さきほど紹介した「Motion Sensor」では、加速度のリアルタイム値を3色{X,Y,Z}={青,赤,緑}で表示しています。WiiRemoteのX,Y,Z軸とはそれぞれ、ボタンの付いている面を机の上に置いたとして、側面のボタンが付いていない方向がX、赤外線センサーが付いている方向がY、ボタン面の上下方向がZとなります。
そして、よくみると、3つの色の線はいつも同じレベルにはありません。ボタン面を上にしている時は緑が他に比べて少し低く、右側を下にするかたちで横に寝かせると青が低く、そして赤外線センサー部分を下にして立たせると、赤が低くなります。
これはいったい何でしょう?そうです!これが地球上の全ての物質に働く『万有引力』です。普段は直接我々の目に見えない重力加速度が、各軸の下向きに働く加速度として観察できているといえます。当たり前のことなのですが、ちょっとした感動が味わえませんか?これを利用することで、WiiRemoteの傾きも検出できます。加速度センサーグラフの右側ではそのようにして姿勢を推定しているようです。ただし、気をつけなければなりません、時には人間がWiiRemoteを強く振るときには重力よりも大きな値を入力することになります。そんなとき、この姿勢推定はどんな様子になるか、観察してみてください。
さて、シンプルでパワフルな「WiinRemote」ですが、実用的に使うために、少し設定してみましょう。ここではプレゼンテーションソフト「PowerPoint」での利用を想定してみます。
まずは「HOME」ボタンでパワーポイントを起動できるようにしましょう。Ctrl+Pで設定画面を表示させて「Button Assign」の「Button」を選んで「Home」に変更、そしてすぐ下の「Assign」を「Execute」に変更します。さらに下のExecuteに「C:\Program Files\Microsoft Office\OFFICE11\POWERPNT.EXE」をファイルブラウザなどを使って設定し、最後に「Set」を押します。「Apply」を押して、いったん設定画面を閉じて、HOMEボタンを押したらパワーポイントが表示されるのを確認しましょう。表示されたら、好きなプレゼンテーションファイルを開いておくとよいでしょう。
同じ要領で、以下のように割り当ててみてください。
Button | Assign | 機能 |
---|---|---|
Up | Up | スライド戻し(変更なし) |
Down | Down | スライド送り(変更なし) |
Right | Keys [PgDn] | ページ送り |
Left | Keys [PgUp] | ページ巻き戻し |
A | Mouse->Left Click | マウスクリック(変更なし) |
B | Cursor On/Off&Speed | カーソルモード&速度切替(変更なし) |
Plus | Mixer->Volume Up | 再生音量アップ |
Minux | Mixer->Volume Down | ビデオ等の再生音量ダウン |
1 | Keys [Esc] | いざというときのための終了 |
2 | Keys [F5] | プレゼンテーション再生 |
どうでしょう?いつものPowerPointが、よりカッコよく操作できるようになりました。この応用で、特にプログラムを書かなくても、様々なアプリケーション用にWiiRemoteが使えるようにカスタマイズすることができます。
ここではフリーウェア「WiinRemote」を使って、WiiRemoteの接続後の動作確認と、PowerPointをWiiRemoteで操作するための設定を解説しました。WiinRemoteはシンプルですが、スタートが速かったことで多くの人々に支えられ、様々なスタックで安定動作することが特徴です。インストールも不要で、サイズも小さいので、いざというときの動作確認のために持ち歩いておくと便利です。
このようにWiinRemoteは様々なアプリケーションに合わせて、GUIだけでボタンの割り当てなどを設定することができ便利ですが、その固有の設定を保存してあとで読み込んだり、切り換えたりすることには向いていません。またジョイスティックの代わりの信号を出すことも残念ながらできません。そのような用途には次節で解説する「GlovePIE」が適しているでしょう。
「キャリブレーション」とは、デバイス系制御ではよく出てくる用語です。センサーや測定器など、ある信号の入力と測定の対象と、出力される値との関係を、再現性のある基準に合わせて決定付ける作業です。デバイス制御系のプログラミングでは、デバイスの「生の値」(raw data)を、使用できるデータに変換するための操作ともいえます。
この変換は用途や特性によりさまざまな変換が実施されます。初期値や原点をセットすればいいものから、較正曲線といった二次曲線を利用する方法、ある値域だけを利用するバンドパスフィルタを組み合わせた方法など、その調整方法についてはさまざまです。
なお昔は「較正」とも標記されていましたが、近年では計量法で「校正」と表記されており、正式には「校正」の概念には「調整」が含まれないそうです。よって最近では「キャリブレーション」のままカタカナ翻訳される事が多いようです。
ユーザーインターフェースなどでキャリブレーションする、といった場合はたいてい原点のリセットなどを行って、そこを基準にする、という行動であることが多いです。例えばタブレットPCなどのタッチパネルを初めて使うとき画面に表示された「+」をクリックしてください、というメッセージが数回出ることがありますが、内部では4点の表示上の位置と、タッチパネルデバイスから送られてくる信号を適合させるための「キャリブレーション」を行っています。
WiiRemoteは計測機器ではなく、ゲーム用のインターフェイスですから、測量や重力の再現性はそこそこもとめられますが、測定器ほどではありません。再現性の不備や、経年変化などで誤りがあったとしても、同じような体験が再現できればいいわけです。
そのため、WiiRemoteには、加速度センサーの重力に対する補正値が保存されているようです。また赤外線センサーの強度は距離、すなわち使用する部屋の空間に依存しますので、Wii本体側で4段階に設定することができるようです。その他、必要になるキャリブレーションの仕組みはアプリケーション開発者側で考えて実装する必要があります。もちろんキャリブレーションの必要のない、極力少ない手順で安定して利用できる仕組みが実現できればすばらしいです。
GlovePIEは最も使用されている、WiiRemoteをサポートするコントローラエミュレーターです。Carl Kennerによって開発されています。GlovePIEとは「Glove Programmable Input Emulator」の意味で、もともとヴァーチャルリアリティのためのデータグローブ製品「5DT」を使って、さまざまなゲームをプレイするためにジョイスティックやマウスをエミュレーションするために開発されていたようです。その成長の過程でWiiRemoteをサポートし、有名になりました。特徴として、すべてのエミュレーションを専用のスクリプトで記述します。例えば「Aボタンをショット、Bボタンをボムに」といったゲームそれぞれの割り当てを、自分で書くことができるのです。
例えば、「DOOM」などの一人称シューティングゲームでよく使われる[W][A][S][D]キーがそれぞれ前後左右の移動キーに割り当てられている場合、GlovePIEスクリプトでは、データグローブの位置を使って
W = glove.z > -50 cm S = glove.z < -70 cm A = glove.x < -10 cm D = glove.x > 10 cm
と表現することができます。他にもジョイスティックやマウス、キーボードも複数のキー入力を連続したマクロとして扱うことなどもでき、SAPI(音声出力API)や、電子音楽で使われるMIDIやOpenSound Control(OSC)などの出力もサポートしており、非常に高機能なツールといえます。
GlovePIEのインストールは非常に簡単です。公式ホームページに行き、最新版をダウンロードし、アーカイブを展開するだけです。
http://carl.kenner.googlepages.com/glovepie_download
最新版はバージョン0.30なのですが、深刻なバグ(赤外線センサー使用時に加速度センサーキャリブレーションに不具合)を理由に公開が中止されています。ジェスチャー認識などたくさんの新機能が盛り込まれているようなのですが、「公開されるまでの間0.29をお使いください」と書かれたまま、ずいぶんと長い時間が経ってしまっています。Ver.0.29の公開は2007年1月4日と少々古いのですが特に問題はないので、こちらをダウンロードしましょう。
ダウンロードのリンクも帯域の制限などで4箇所ありますので、確実にダウンロードできているかどうか確認しながら「GlovePIE029.zip」をダウンロードしてください。
ダウンロードしたZIPファイルを展開すると、たくさんのファイルが現れます。実行ファイルは「GlovePIE.exe」ですが、まずはライセンス関係を確認するために「readme.txt」を開きましょう。
ライセンスに関しては、以下のように記述されています。
This software is copyright (c) Carl Kenner, except for scripts by other \ authors. By using this software you agree to obey the following license conditions: * You can't make money using this software as part of a baseball \ simulation. This is for contractual reasons. But you can make as much \ money as you like using it for anything else. * You may not use this software directly or indirectly for any military \ purpose. This includes, but is not limited to, training, research and \ development, controlling military hardware, directing military \ personel, or troop entertainment. You may not use this software \ anywhere on a military base or vessel. This applies to all versions of \ PIE. * You may not export this software to Israel, or use it in Israel \ (including the occupied territories), until Israel has ended its \ occupation of the West Bank, Gaza Strip, Lebanon, Syria, and anywhere \ else it may occupy. If you try to run it in Israel it will give you an \ error. * Missionaries may not use this software. It may not be used for any \ missionary purpose. Or any other genocidal purpose. * You may not use this software to cheat at online or multiplayer games. \ What constitutes cheating depends on the game and the server. Just \ using a different input device shouldn't be considered cheating, but \ complex scripted actions to make things easier may be considered \ cheating. Don't get GlovePIE banned, or you will hurt everyone who \ wants to play with a VR glove. But feel free to cheat at single player!
意訳すると、以下のようになります。「このソフトウェアのコピーライトは(c)Carl Kennerです。他の著者による著作(スクリプト)を除きます。このソフトウェアを使用することにより、以下のライセンス条項に従うことに同意します。あなたは、このソフトウェアを野球シミュレーションの一部のようにして、お金を儲けることができませんが、これは契約上の理由です。他の何かに使うことで、好きなだけお金を儲ける機能を有します。直接または間接的に、このソフトウェアを軍用目的に使用してはいけません。これはトレーニング、研究開発、軍用ハードウェアの制御、部隊指示、部隊の娯楽などを含み、限定されません。このソフトウェアを軍用基地もしくは軍用艦内で使用してはいけません。これはPIEのすべてのバージョンに該当します。このソフトウェアを、イスラエルがウエストバンク(ヨルダン川西岸)のその占領、ガザ地区、レバノン、シリアと他の占領区の占有を終えるまで、イスラエルに輸出もしくはイスラエル(占有された領土を含む)で使用してはいけません。もしあなたがこのソフトウェアをイスラエルで実行しようとすれば、あなたにエラーを与えます。宣教師は、このソフトウェアを使用してはいけません。いかなる伝道目的、もしくは他のいかなる大量虐殺目的にも使われてはいけません。オンラインまたはマルチプレーヤーゲームでいかさまをするために、このソフトウェアを使用はしてはいけません。不正行為を意味するところは、ゲームとサーバーに依ります。普段と異なる入力デバイスを使うことは不正行為とされるべきではありませんが、物事をより簡単にするための、複雑なスクリプト化されたアクションは不正行為になるでしょう。GlovePIEを禁止されないようにしてください。もし、VRグローブでみんなを傷つける遊びをしたいなら、シングルプレーヤーで不正行為を遠慮なくやってください!」
本書を読んでいらっしゃる方で、このライセンス条文が問題になる人はまずいないでしょう。もしこのソフトを使ってお金持ちになったなんていい話があったら、ホームページの「Donate」から、作者のCarl Kenner氏に募金をするとよいかもしれません。特に、新しい機能についてのアイディアなども募金と共に募集しています。
まず、3.1章の流れに沿って、WiiRemoteをBluetooth接続してください。無事接続が終わったら「GlovePIE.exe」を起動します。
この後、サンプルスクリプトを読み込んで、実際にWiiRemoteを使って操作を行う実験を行います。ここで注意です。スクリプトが想定外の動作をすることで、正常な操作ができなくなる可能性があります。そうなったときに慌てないために[Shift+P+I+E]もしくは[Alt+R]→[S]で「スクリプトの停止」ができることを覚えておくとよいでしょう([Shift+PIE]は動作しないこともあります)。
まずは最も簡単なWiiRemoteを使うサンプルスクリプトを試してみましょう。「File」メニューから「Open」を選んで、GlovePIEと同じ階層にある「WiimoteScripts」フォルダ内にある「Wiibrator.PIE」をロードします。なおこのディレクトリには大量の面白そうなファイル名が並んでいます。PIEスクリプトというテキストファイルで、様々なゲームのエミュレーターや、Windows上でWiiRemoteを使うためのサンプルが用意されています。メモ帳などのテキストエディタで開いたり編集することもできますが、この際なので「.PIE」という拡張子をGlovePIEに関連づけてしまうのもよいでしょう。
スクリプトがロードされると、以下のように表示されているはずです。
//Wiibrator //By deceased // Does not control mouse so feel free to surf ^^ // D pad up turns it on // D pad down turns it off if wiimote.Up wiimote.Rumble = 1 endif if wiimote.Down wiimote.Rumble = 0 endif
さて、このスクリプトは十時キーを使ってバイブレーターをOn/Offするだけのスクリプトですので安心して実行できます。[F9]もしくは、メニューの[Run!]から[Run!]を選んで実行してみましょう。
緑の三角矢印が「Run」から「Stop」に変わり、スクリプト表示エリアがグレーになったら実行中です。接続したWiiRemoteの十時ボタンの上(POWERボタン側)を押してみましょう。バイブレーターが作動します。あわてず十時ボタンの下(Aボタン側)を押すと、止まります。何度でも繰り返し遊んでみてください。ひととおり遊んだら、GlovePIEウィンドウ内の「Stop」を押してスクリプトを止めましょう。
さて、スクリプトを解説してみます。「//」で始まる行はコメントなので、実質6行のスクリプトです。まず、PIEスクリプトは非常に動的でユーザーフレンドリーなスクリプトで、ほとんどのケースで初期化コードが不要です。一番上から実行され、一番下までいくと、また上から実行されます。この場合も宣言や初期化などしなくても
if wiimote.Up
とすることで、WiiRemoteが初期化され、現在のボタンの状態を「wiimote.Up」で取得することができます。ここが「True/1」になったときに「wiimote.Rumble = 1」となりバイブレーターを駆動し、「endif」でif文を抜けます。下を押した場合つまり「if wiimote.Down」の場合も同様で、バイブレーターの振動を意味する「wiimote.Rumble」を「0」にすることで振動を止めます。
例えば、ここで新しくシンプルなスクリプトを作成してみましょう。「File」メニューから「New」を選びます。「Clear text box without saving changes?(保存しなくてもいいですか?)」という質問が出たら「Yes」で問題ありません。新しいスクリプトとして以下をテキストボックスに書きます。
wiimote.Rumble = wiimote.A
たった一行のスクリプトですが、Aボタンを押している間だけ、バイブレーターが鳴ります。GlovePIEはスクリプト記述についても支援機能があり、「wiimote.」といったように予約されたクラス名とピリオドまで書くと、自動的にその先のプロパティ名などをリストしてくれます。これでマニュアルがなくても簡単にスクリプトが書けますし、またシンタックスエラー(文字の書き損じによる間違い)も大きく減らすことができます。
書き終わったら、まずはシンタックスを確認しましょう。メニューの「Run!」から「Check for errors」を選びます。何か間違いがあると、該当する行がピンク色に変わります。間違いがなければ何も起きませんので、[F9]で実行しましょう。無事に、Aボタンを押している間だけバイブレーターが鳴れば成功です。「Aボタン連打!」などしてみて、バイブレーターの鳴り具合を確認してみるとよいでしょう。
もし挙動がおかしい、反応がない、といったときは、接続を確認するために、WiinRemoteなど確実に動くツールを起動してみるとよいでしょう。Bluetoothが時間切れで自動切断されている場合もあります。単純なスクリプトなので、挙動がおかしいときは、単にGlovePIEやPCを再起動してみるのも解決策になるときもあります。
ここでバイブレーターに関する注意を実験を通して確認します。バイブレーターを起動した状態でスクリプトを止めると何が起きるでしょう?上記のスクリプトでは、Aボタンを押しっぱなしにした状態で、GlovePIEをスクリプトを停止させます。するとスクリプトは終了しているのに、バイブレーターは鳴り続けます。大変です。暴走状態です。この状態でしばらく放っておくとあっという間に電池が切れてしまいます。しかし、スクリプトを再度起動しても、Aボタンを連打しても、バイブレーターは止まらないかもしれません(これはバグではないかと見ています)。
こういうときは、慌てず騒がず、GlovePIEを終了します。スクリプトの保存が必要なら保存をしておきます。そして、再度、GlovePIEを起動し「Wiibrator.PIE」などのバイブレーターを使用するスクリプトを読み込んで、Runすればバイブレーターの暴走は止まります。
このセクションでは、GlovePIEを使って、高機能な赤外線マウスを作成していきます。実践的な開発を通して、WiiRemote開発のコツやGlovePIEの強力なスクリプティング機能とGUIによる支援機能をステップバイステップで学ぶことができます。
まずはこれから作成する赤外線マウスについて、簡単に仕様を決めておきましょう。基本動作としては、WiiRemoteをセンサバーや赤外線光源に向けて、マウスのようにして使うタイプのものにします。せっかく作るのですからファイル操作やPowerPointのプレゼンテーションに実際に使える高機能なものを想定します。
WiiRemote側操作 | 割り当てる操作 |
---|---|
赤外線 | マウスポインタの移動 |
十字キー | カーソルキー |
Aボタン | マウス左ボタン |
Aボタン・ダブルクリック | Enterキー |
Bボタン | マウス右ボタン |
Bボタン・ダブルクリック | Deleteキー |
A+Bボタン同時押し | デスクトップを表示 |
+ボタン | アプリケーション切り替え[Alt+Tab] |
−ボタン | アプリケーション終了[Alt+F4] |
Homeボタン | スクリプト終了 |
1ボタン | GlovePIE最小化/最大化 |
2ボタン長押し | PowerPointを起動 |
2ボタン・シングルクリック | Escキー(プレゼンテーション終了) |
2ボタン・ダブルクリック | プレゼンテーション開始[F5] |
WiiRemoteならではの、沢山あるデジタルボタンを活用して、長押し、ダブルクリック、A+Bボタン同時押しなどのコンビネーションを使った使いやすい操作を盛り込んでいます。もちろんこのセクションの体験を通して、ご自身のアイディアで新しい機能を盛り込んでいくことが可能です。
まずは手始めに「Homeボタンで終了」を実装してみましょう。新しいスクリプトとして以下を記述して実行してみてください。GlovePIEスクリプトは大文字・小文字は無視されます。
if Wiimote.Home then ExitScript end if
記述したらWiiRemoteを接続後にRunして、Homeボタンで終了できるかどうか試してみてください。「ExitScript」とは実行中のGlovePIEスクリプトを終了させるコマンドです。詳しくは、GlovePIEとおなじディレクトリにあるマニュアル(Documentation.rtf)に記載されています。基本的なコマンドはこのセクションで解説していきますので、いまは「そういう便利な物があるんだ」という理解でよいでしょう(本セクションの最後にまとめて紹介します)。
さて、次は十字キーにカーソルキーを割り当てましょう。先ほどのようにif文をつかうと明確に条件やその後の処理を記述できますが、今回はボタンイベントに対するキーアクションを大量に設定しなければなりませんので、もっと簡単な記述方式で書き直してみます。
//WiiRemote IR mouse //Key Binds ExitScript = Wiimote.Home Key.Up = Wiimote.Up Key.Down = Wiimote.Down Key.Left = Wiimote.Left Key.Right = Wiimote.Right
このように「=」でつなぐことで、キー入力に割り当てるアクションが1行で表現できます。また「//」で始まる行はコメント行です(日本語は表示上化けてしまいます)。
次は「+ボタン」でアプリケーションを切り換え、「−ボタンでアプリケーション終了」できるようにします。「Alt+Tab」のように複数のキーを使う場合は以下のような表記をします。
Key.Alt+Tab = Wiimote.Plus Key.Alt+F4 = Wiimote.Minus
簡単ですね!もちろん「Key.Alt+Key.Tab」と表記してもかまいません。
そして、これは「WiiRemoteのAボタンとBボタンを同時に押したときに、デスクトップを表示する」というアクションです。
Key.Windows+D = Wiimote.A and Wiimote.B
次は、WiiRemoteのA,Bボタンのダブルクリックを使ってEnterやDeleteを入力できるようにします。
Key.Enter = DoubleClicked(Wiimote.A) Key.Delete = DoubleClicked(Wiimote.B)
「DoubleClicked()」という関数を使うことで、ダブルクリックを判定できます。ここで実際に動作を試すために、メモ帳などを実行してからスクリプトをRunしてみて、+ボタンでアプリケーションを切り換え、十字ボタンやダブルクリックを試してみるとよいでしょう。最後にHomeボタンでスクリプトを停止します。
次は、「2ボタン」にいろいろな機能を割り当ててみます。長押し、シングルクリック、ダブルクリックで、それぞれPowerPointの起動、Escキー、F5キーに割り当てます。
Execute("C:\Program Files\Microsoft Office\OFFICE11\POWERPNT.EXE") = HeldDown(Wiimote.Two, 1s) Key.Escape = SingleClicked(Wiimote.Two) Key.F5 = DoubleClicked(Wiimote.Two)
HeldDown(対象,秒数)とすることで、長押しを検出できます。単位は秒です。「0.5」などでもよいでしょう。Execute("実行ファイル名")で任意のアプリケーションを起動できます。シングルクリック、ダブルクリックも同様に関数を使って検出することができますので今までと同じように割り当てます。
さて、実際に「2ボタン」を長押しし、PowerPointが立ち上がったら、今度はダブルクリックでプレゼンテーションを起動、さらにシングルクリックでプレゼンテーション終了、「−ボタン」でPowerPoint終了、と試してみてください。Escキーは他のアプリケーションなどでもよく使いますのでここに割り当てておくのは便利そうです。
PowerPointがインストールされていない場合は他のアプリケーションで試してみましょう。例えば「Execute("mspaint.exe")」とすることで「ペイント」が起動します。PrintScreenキーと張り付け(Ctrl+V)でも面白いでしょう。
Execute("mspaint") = HeldDown(Wiimote.Two, 1s) Key.PrintScreen = SingleClicked(Wiimote.Two) Key.Ctrl+V = DoubleClicked(Wiimote.Two)
「−ボタン」を押して終了すると「保存しますか?」ときかれますが、十字キーの右、そして「Aボタン」のダブルクリックで「いいえ」を選択することができます。もう簡単な操作ならなんでもWiiRemoteでできそうですね!
最後に、左右のマウスボタンをそれぞれA,Bボタンに、そして「1ボタン」にはGlovePIEの表示切り替え機能を割り当てます。
UnMinimizePie = HeldDown(wiimote.One, 1s) MinimizePie = wiimote.One Mouse.LeftButton = Wiimote.A Mouse.RightButton = Wiimote.B
ここまでの作業でスクリプトは以下のようになっているはずです。
//WiiRemote IR mouse (part for buttons) //Key Binds ExitScript = Wiimote.Home Key.Up = Wiimote.Up Key.Down = Wiimote.Down Key.Left = Wiimote.Left Key.Right = Wiimote.Right //combination Key.Alt+Tab = Wiimote.Plus Key.Alt+F4 = Wiimote.Minus Key.Windows+D = Wiimote.A and Wiimote.B //Double Clicks Key.Enter = DoubleClicked(Wiimote.A) Key.Delete = DoubleClicked(Wiimote.B) //Multifunctions on Two-Button Execute("C:\Program Files\Microsoft Office\OFFICE11\POWERPNT.EXE") = \ HeldDown(Wiimote.Two, 1s) Key.Escape = SingleClicked(Wiimote.Two) Key.F5 = DoubleClicked(Wiimote.Two) //Hide and Show by One-Button UnMinimizePie = HeldDown(wiimote.One, 1s) MinimizePie = wiimote.One //Mouse Buttons are linking to A and B Mouse.LeftButton = Wiimote.A Mouse.RightButton = Wiimote.B
いかがでしょうか?GlovePIEスクリプトの短い記述だけで、かなり高機能なツールがつくれることが実感できたでしょうか?
さて、ボタンアクションを一通り使いこなせるようになって、だんだん楽しくなってきたところでしょう!しかしまだこの状態では、マウスポインタは相変わらず動きません。センサーバーの赤外線にWiiRemoteを向けて、自在にマウスポインタが操作できたらどんなに楽しいでしょうか。
はやる気持ちを抑えて、まずは「デバッグ機能」を学びましょう。
先ほどのスクリプトの一番下に以下の記述を足してみましょう。
Debug = mouse.x
実行すると、GlovePIEの「Runボタン」の右側に、なにやら数字が表示されるようになったはずです。これは現在のマウスのX(横方向)の値です。ここでマウスを動かしてみましょう。値は左から右に行くにつれ、ゼロから1への小数をとるはずです(これを値域[0,1]と表現します)。「Debug=」とすることで、デバッグ用に内部の値を表示する機能です。
続いて、以下のDebugPrint()関数も試してみましょう。
Debug = mouse.x DebugPrint("X="+mouse.x+" Y="+mouse.y)
これは別のウィンドウが開き、時系列でより多くのデータを読むことができます。用途に合わせて使い分けるとよいでしょう。
さて、それではついにセンサーバーを使って、このデバッグウィンドウに赤外線センサーの値を表示してみましょう。先ほどのテストコードを以下のように書き換えます。
Debug = "mx="+mouse.x+" my="+mouse.y if Wiimote.dot1vis then DebugPrint("X="+Wiimote.dot1x+" Y="+Wiimote.dot1y) end if
「dot1vis」が「最初の1点目が見えるかどうか」、「dot1x」と「dot1y」はその座標を表しています。
早速実験してみましょう。最低でも1点の赤外線光源が見えればよいので、近くにセンサーバーがない場合は、太陽光や電球光源などを探してから実行しましょう。光源として使えるかどうか不安なときは、あらかじめWiinRemoteを使って確認すると便利です。
赤外線光源に向けたとき、デバッグウィンドウに値が書き足されていく様子が見えれば成功です!値が書き足されない場合は、WiinRemoteをつかって赤外線が正しく認識できているか試してみましょう。ウィンドウが見つからないときは、GlovePIEのウィンドウの裏側に隠れていることがあるので調節してみましょう。
さて、原理的にはこのdot1x,dot1yをマウスのx,yに割り当てれば完成、ということなのですが実際にはそう単純ではありません。
Mouse.x = Wiimote.dot1x Mouse.y = Wiimote.dot1y
実はこのままでは画面上のマウスポインタとWiiRemoteの赤外線センサーが取得する値とのスケールがあっていません。さらに動作方向の極性(正負)があっていません。操作のイメージとしては、WiiRemoteを上に向けたらマウスポインタが上に、左に向けたらマウスポインタが左に行って欲しい感じがします(多くのWii用ゲームがそうであるように)。そのため、スケーリングをして、さらにある軸の正負を反転させる必要があります。
WiinRemoteや、デバッグ出力で観察できるように、WiiRemoteの赤外線センサーは水平方向Xは[0,1023]の値域、垂直方向Yは[0,767]の値域をとります。WiiRemoteをセンサーバーに向かって上から下に向けるとき、WiinRemoteからみえる赤外線の点像は上部から下部、生値Xはゼロからはじまって767に向かって増加していきます。また左から右に向けたとき、WiinRemoteからみえる赤外線の点像は右側から左側へ、生値Xは1023から始まってゼロに向かって減少していきます。
対して左辺側のマウスポインタの座標は左上を(0,0)、右下を(1,1)とする[0,1]の値域となっています。これら異なる値域をスマートにつなぐ関数が「MapRange(x, a,b , c,d )」です。xという値域[a,b]をとるもとの入力値を、値域[c,d]に変換することができます。よって、以下のように関数を記述することで、正しい操作感が得られるように変換されます。
Mouse.x = MapRange(Wiimote.dot1x, 1023, 0 , 0,1) Mouse.y = MapRange(Wiimote.dot1y, 0, 767 , 0,1)
さて、これで完成!と思いきや、実行してみると…どうでしょう?何か違和感はありませんか?人によっては感じないかもしれませんが、実際にこの赤外線マウスを使って、フォルダを開き、ファイルを移動して…といった操作を試してみるとよいでしょう。するとポインタが、高速にブルブルふるえて非常に操作しづらいはずです。この「ふるえ」にはいろんな原因があります。一つは赤外線センサーのノイズ(特に“ドリフトノイズ”と呼ばれることもありますが、センサーの内部がわかるわけではないので単に“ノイズ”としておきましょう)、それから人間の手そのものが持っている微少な震えです。マウスの場合は机の上の摩擦で気にならないのですが、WiiRemoteの場合は空中で、しかもかなり高速高精度の計測を行うので、このような人間の手の震えの存在も理解しながら実装していく必要があります。信号処理の知識はこのようなケースで非常に役に立つでしょう。この場合は急速な動きである高周波成分を除去する「ローパスフィルタ」が効果的ではないでしょうか。
難しい話はさておき、ローパスフィルタは平均をとることで実現できます。GlovePIEスクリプトにはこういった便利な関数が数多く実装されています。
先ほどのMouseへの代入の式において、Smooth関数を取り入れてみましょう。ExtraFramesは3にしておきます。これで過去3フレームの値を使って平均をとります。デバッグ出力も、以下のようにすっきり整理します。
if Wiimote.dot1vis then Mouse.x = MapRange(Smooth(Wiimote.dot1x, 3), 1023, 0, 0,1) Mouse.y = MapRange(Smooth(Wiimote.dot1y, 3), 0, 767 , 0,1) Debug = "mx="+Mouse.x+" my="+Mouse.y+" IRX="+Wiimote.dot1x+" \ IRY="+Wiimote.dot1y end if
どうでしょうか?ぐっと安定感のあるマウスポインタになったのではないでしょうか?信号処理の理論では、適切なExtraFramesを算出する方法もありますが、今回の場合は経験的に入れても全く問題ないでしょう。3〜10程度で試してみるとよいでしょう(値が大きくなるとスムースにはなりますが、その分処理が重たくなります)。
さて、これで高機能赤外線マウスは一旦完成です。ここまでの全てのスクリプトを掲載します。
//WiiRemote IR mouse (basic) //Key Binds ExitScript = Wiimote.Home Key.Up = Wiimote.Up Key.Down = Wiimote.Down Key.Left = Wiimote.Left Key.Right = Wiimote.Right //combination Key.Alt+Tab = Wiimote.Plus Key.Alt+F4 = Wiimote.Minus Key.Windows+D = Wiimote.A and Wiimote.B //Double Clicks Key.Enter = DoubleClicked(Wiimote.A) Key.Delete = DoubleClicked(Wiimote.B) //Multifunctions on Two-Button Execute("C:\Program Files\Microsoft Office\OFFICE11\POWERPNT.EXE") = \ HeldDown(Wiimote.Two, 1s) Key.Escape = SingleClicked(Wiimote.Two) Key.F5 = DoubleClicked(Wiimote.Two) //Hide and Show by One-Button UnMinimizePie = HeldDown(wiimote.One, 1s) MinimizePie = wiimote.One //Mouse Buttons are linking to A and B Mouse.LeftButton = Wiimote.A Mouse.RightButton = Wiimote.B //Assign Infrared as mouse input if Wiimote.dot1vis then Mouse.x = MapRange(Smooth(Wiimote.dot1x, 3), 1023, 0, 0,1) Mouse.y = MapRange(Smooth(Wiimote.dot1y, 3), 0, 767 , 0,1) Debug = "mx="+Mouse.x+" my="+Mouse.y+" IRX="+Wiimote.dot1x+" \ IRY="+Wiimote.dot1y end if
さて、実際に使い込んでボタンアサインや細かなパラメーターなどを変更してみるとよいでしょう。なおこのスクリプトは赤外線が見えないときはマウス制御を奪いませんので、マウスと協調作業することもできます。実行したままタスクバーにしまっておけば(最小化ボタンの左にタスクバー格納ボタンがあります)、何かと便利でカッコイイです。
A+Bボタン同時押しでデスクトップを表示し、+ボタンでアプリケーションを切り換え、Bボタンで右クリック、十字キーで細かな作業をし、Aボタンのダブルクリックで決定、2ボタンでEsc…といった感じで慣れると快感になってきます。スクリプトはHomeボタンを押せばいつでも終了できます。
さて、さきほど最後に赤外線の座標系をマウスの座標系に合わせるために、新しいMapRangeという関数を使いました。GlovePIEは非常に高機能なスクリプト環境を装備しているので、そもそも「mouse」といったキーワードや便利な関数、コマンドなどを、付属のマニュアルだけで探してくるのは(しかも「未完成」と明記されています...)、なかなか骨の折れる作業です。
GlovePIEの新機能として「GlovePIE GUI」が実装されています。グラフィカルな環境で、入出力の関係を結びつけると自動的にスクリプトが作成され、接続関係なども管理してくれるようです。マニュアルをよく読んでいくと「WiiRemote関係の機能はGUIを使わない方が良い」と書かれているのですが、筆者が試した感覚では、WiiRemote→キーボード・マウス等に使うのであれば十分使えますし、最終的にテキストで表現されるスクリプトが全てなので、GlovePIEの機能を素早く調べる上ではかなり役立つ環境ですので紹介しておきます。
練習のために、先ほど最後に作成したWiiRemote→マウスの変換式をGUIで作成してみましょう。まずは先ほど作成した「高機能赤外線マウス」を保存して、一旦GlovePIEを起動し直してください。新しいGlovePIEが起動したら、メニューの「Edit」の下あたりにある「GUI」というタブをクリックしてください。ボタンが2つありますが「Detect Output Emulate」を押すと、使用可能な出力デバイスがリストされますので、「Output Device」から「Mouse」を選んでください。下にたくさんの操作可能なプロパティが現れますのでここから「x」を選び「Edit Manualy」ボタンを押します。
「Input Device」というプルダウンがありますので、ここから「Wiimote」を選びます。「Number」は複数のWiiRemoteを接続した場合ですので空白のままで、その隣の「Part of device, or a numeric value, or expression:」に「dot1x」と書いてみてください(入力支援機能がすでに働いているはずです)。プルダウンから選択するのもよいでしょう。
さらに「More」というボタンを押して、詳細を表示します。「Function:」というプルダウンで「Smooth」を選ぶと「Frames」が現れますので「3」を設定し、最後に「Convert from source range」から[min]と[max]をそれぞれ、デフォルトで入っている[0,1023]から[1023,0]に書き換えて、最後に「Apply」という大きなボタンを押して数秒待ちます。
GUIタブから「Untitled」と表示されている通常のスクリプトタブに戻ります。すると、
Mouse.x = MapRange(Smooth(Wiimote.dot1x, 3), 0,1023, 0,1)
というように、先ほどと全く同じスクリプトが自動生成されていることが確認できます。もちろん実行もできますが、同じ手順を再度「GUI」タブに戻って「mouse」→「y」→「Edit Manualy」→「Wiimote」…というように選んで完成させてみてください。GUIで作成したスクリプトは必ず文末に現れるようです(GlovePIE自体が手続き型言語ではないので、順番が問題になることはあまりない)。
今回は新規スクリプトで実験しましたが、既存のスクリプトでも十分に利用できます。新機能を追加するとき、新しくキーボードに割り当てたいが名称がわからないとき、適切な値域を扱いたいが基本的な値を調べるのが面倒なとき...などに非常に役に立ちます。WiiRemoteへの出力(例えばLEDやバイブレーター、スピーカーなど)もまだサポートされていませんが、MIDIなどはかなり整備されているようなので、今後、アップデートされるようであれば機能強化が期待されるところです。
ここから先は、余力のある人だけでかまいません。GlovePIEのもつ様々な機能を利用して、より多くの機能を実装してみましょう。
さて、高機能赤外線マウスを自力で完成させられたあなたは、もう他のPIEスクリプトを読むのが難しくなくなっているはずです。「WiimoteScripts」フォルダの中にあるさまざまな例を読んでみることをお勧めします。多くはPCゲームのキーボード・マウスのエミュレーションが多いのですが、より簡単に操作できるよう様々なスクリプティングテクニックが読み取れます。他にもこのディレクトリには、WiiRemoteの基本機能に対する解説的なスクリプト、例えばバッテリー残量を表示する「WiiBattery.PIE」や、Midiと組み合わせた簡易ドラムセット「WiiDrums2.PIE」、ヌンチャクまで使った本格ギター「Wiitar」、加速度から距離を求めようとする「TestVelocityPos.PIE」など参考になるものがあります(ならないものも沢山あります!)。
バイブレーターは最初に試したとおり「Wiimote.Rumble」で制御できますので、マウスポインタが現在のウィンドウの境界にぶつかったら、振動するようなスクリプトを追加してみます。
var.hit = false; var.hit = mouse.CursorPosX<Window.Left or \ mouse.CursorPosX>Window.Left+Window.Width var.hit = var.hit or mouse.CursorPosY<Window.Top or \ mouse.CursorPosY>Window.Top+Window.Height Wiimote.Rumble =var.hit;
変数var.hitは現在のマウスポインタの位置と、現在のウィンドウ境界を比べて、ポインタが外にいる場合trueになります。これをWiimote.Rubmleにwつなげています。
これを応用すれば弱視の方や何らかの理由でマウスが使えないかた(例えば産まれたばかりの赤ん坊を抱っこしていて...)でもエクスプローラーのようなGUIが触りやすくなるかもしれません。
学会などでプレゼンテーションをしているときに、ついつい時間オーバーしてしまったりしませんか?ですが本番になると逆に時間が気になりすぎて、堂々と話ができなかったり…。このスクリプトは、手元に持っているであろうWiiRemoteのLEDを使って、残り時間を表現します。
Wiimote.Led1 = HeldDown(true, 5s); Wiimote.Led2 = HeldDown(true, 10s); Wiimote.Led3 = HeldDown(true, 20s); Wiimote.Led4 = HeldDown(true, 1 minutes);
この例ではWiiRemoteの青色LEDが左から順に、5秒、10秒、20秒、1分…というように点灯して最後は4つ全点灯します。Windowsのプログレスバーのようなイメージですね。あらかじめプレゼンテーションの構成と共に、適切なラップタイムを設定しておくというのもよいでしょう。
最後にスピーカーを使ってみます。GlovePIEで使えるWiiRemoteのスピーカー機能は非常に低レベルな機能しか提供されていません。周波数とボリュームを設定し、それが適切な長さだけ再生されるように自分で管理する必要があります。WAVファイルを再生できるような機能もそのうち出てくるのかもしれませんが…。このスクリプトはその構造を理解するためだけの目的で書かれています。1ボタンを押すたび様々な音(ノイズ?)が鳴ります。
if Wiimote.One then Wiimote.Volume = 1.00 Wiimote.SampleRate = 3640 Hz while ( true ) if var.f<=0 then var.f=360 end if // wait 10ms debugprint("f="+var.f + "Cos:"+cos(var.f)+" \ Freq:"+abs(500*cos(var.f)+1000)); Wiimote.Frequency =abs(500*cos(var.f)+1000) // Wiimote.Frequency = 261.62 var.f=var.f-10; end while else wait 1000ms Wiimote.Frequency = 0Hz end if
コメントアウトされていますが、いくつかの周波数(たとえば261.62Hz)などでキレイに聞こえる音が存在します。インターネットを探すと音階と周波数の関係についてのデータがありますので、その周波数を使えば音階を表現することも可能でしょう(やはり音質はそこそこですが...)。
PIEスクリプトを作り込んでいくと、今度はGlovePIEから読み込んで実行…という流れが面倒になってくるはずです。アート作品の展示などに使う場合はぜひとも自動起動させたいところです。GlovePIEはコマンドラインからの起動もサポートしています。
起動時のオプションで、以下のように解説されています。
GlovePIE.exeに続いて以下の3種類の方法でファイル名を指定します。空白などが入る場合はクォーテーション「"」で囲むのを忘れずに。拡張子「.PIE」は含んでも含まなくてもよいようです。
例えば以下のようなバッチファイルを書いておけば、すぐに作成した赤外線マウスが使えます。
"C:\GlovePIE029\GlovePIE.exe" -"C:\GlovePIE029\IRmouse"
もちろんIRmouse.PIEは指定した場所に保存しておいてください。
GlovePIEの可能性はGlovePIEスクリプトの使いこなしにあるといっても過言ではありません。本書はWiiRemoteによるプログラミングのための本ですが、このセクションで学んだとおり、GlovePIEによるスクリプティングだけでもかなりのことができます。また、GlovePIEはMIDIだけでなくジョイスティックやデータグローブ、OSCやSpeechAPIといった他のインタラクション技術でも使われているような基本的なユーザーインタフェース周辺機器のエミュレーターやコンバーターとしてのポテンシャルもとても高いソフトウェアです。また、かなり様々な言語の仕様を取り込んでいるので、プログラミング言語マニアとしても勉強になります。
これでこの章はおしまいです。Bluetooth接続やWiinRemoteやGlovePIEなど、基本となるツールの使い方はしっかりと理解できたでしょうか?この先のプログラミング編に入る前に「わかったつもり」を脱しておいてください。またここでの知識は、実際に新しいハードウェアを購入したり、新しい作品を作ったり、展示を行う上での改造を行ったりといったときにこの章で学んだ内容は必ず役に立つものです。
この章ではWiimoteLibというAPIを利用して、WiiRemoteのプログラミングの基礎を解説していきます。プログラミング環境として、無料で利用できる「Visual C++ 2008 Express Edition」もしくは「Visual C# 2008 Express Edition」を使います。つまりプログラミング言語として、C++とC#を同時に並列して扱います。
また、本章のC#.NETによるプログラミングサンプルは金沢工業専門高等学校の小坂崇之先生(http://www.kosaka-lab.com/)の協力によるものです。プログラミング初心者でもわかりやすく理解できるように、できるだけ多くのサンプルを丁寧に順を追って解説することで、WiiRemoteプログラミングを体験することができるようになっています。
このセクションでは、無料で利用できる「Visual C++ 2008 Express Edition」と「Visual C# 2008 Express Edition」をセットアップします。既にこれらの製品の上位バージョン(Standard Editionなど)をインストールされている方や、C++、C#のいずれかしか必要でない方は、必要なところだけ参照してください。
Visual C# 2008 Express Editionのセットアップについては、マイクロソフトの公式サイトに丁寧に解説されております。
http://www.microsoft.com/japan/msdn/vstudio/express/beginners/2008/vcsharp.aspx
Express EditionのWebインストールをダウンロードし、時計を表示するアプリケーションを実際に作ってみることができます。C#はしばらく使う予定はないけれども、インストールしてみよう、という方は是非試してみてください。
ここではMicrosoft Visual C++ 2008 Express Editionのセットアップ、サンプル開発について解説します。既にVisual C++や.NETといった開発環境をお使いの方は、読み飛ばしていただいても問題ありません。
まずマイクロソフトのホームページからMicrosoft Visual C++ 2008 Express Editionをダウンロードします。
http://www.microsoft.com/japan/msdn/vstudio/express/
「Webインストール(ダウンロード)」をクリックすると、Webインストール版セットアップファイル「vcsetup.exe」をダウンロードすることができます。このホームページにはVisual Studio製品を使う上での役に立つ情報がたくさんあります。とりあえずVC2008を使ってみたい方は「はじめての方のためのインストール方法」を読んでみるとすると良いでしょう。
必要なハードディスク容量の確認、起動中のアプリケーションの終了などを行ってからインストールウィザード「vcsetup.exe」を起動します。
「セットアップの品質向上プログラム」はチェックしてもしなくても、どちらでもかまいません。「次へ」をクリックして進みます。
ライセンス条項をよく読んで「同意する」を選んでください。また「Visual StudioでオンラインのRSSコンテンツを受信して表示できるようにする」も特に問題がなければチェックしましょう。イベントやサービスパックなどの更新情報がVC2008起動直後に表示されるスタートページに自動的に表示されるようになります(ここでのRSS受信の設定は後でもオプション→環境→スタートアップで変更できます)。「次へ」をクリックして進みます。
「インストールオプション」を選択します。ここで表示されている3つのオプションは、どれもインストールしなくても問題ありません。「MSDN Expressライブラリ」はF1キーで呼び出せるドキュメントで、オンライン版の方が充実しているのですが、筆者は電車の中でコーディングをすることが多いのでインストールしています(オンライン版と統合して利用できます)。「SQL Server 2005」、「Silverlight」は使う予定がなければインストールしなくて良いでしょう。「次へ」をクリックして進みます。
「コピー先フォルダ」とダウンロードパッケージのリストです。「インストールするフォルダ」は本書ではデフォルトのままとして解説します。ダウンロードリストの中に「Windows SDK」が入っているのが助かります(以前のExpress Editionでは別途インストールする必要がありました)。「インストール」をクリックするとダウンロードとインストールが実行されます。
以上はWindowsXPシリーズにおけるVC2008セットアップの流れです(Windows Vistaの場合は「管理者権限で実行」をする必要がある点などいくつか細かい点が異なりますがほぼ同じ流れです)。興味がある方はVC2008のウィザードやオンラインチュートリアルなどを使って簡単なプログラミングを試してみると良いでしょう。
もし、既にVisual C++やVisual Studioの過去のバージョン(.NET2003/2005など)をお使いで、かつ2008年時点の最新版であるVisual C++ 2008 Express(以下VC2008)を試してみたいの読者は、ここでちょっと回り道をして試してみることをおすすめします。VC2008はVersion Selectorという機能があり、異なるバージョンのVisual Studio製品の混在を可能にします。各バージョンにおいてソリューションファイルは拡張子「.sln」と変わりませんが、自動的にこの拡張子「.sln」に関連づけられたアプリケーションである「Version Selector」がファイル内部のバージョン記述を自動的に読み取り、ファイルがダブルクリックされたときは適切なバージョンのVCを起動します。この機能のおかげで複数のバージョンの開発環境を安全してインストールできるようになります。
WiimoteLibはBrian Peek(http://www.brianpeek.com/)による.NET環境で利用できるAPIオープンソースプロジェクトです。Microsoftが支援するオープンソースプロジェクト支援サイト「CodePlex」(http://www.codeplex.com/)で公開されています。WiimoteLibを用いることで、.NET環境で簡単にWiiRemoteを利用するアプリケーションを開発することができます。Version1.6.0.0からはWiiRemoteだけでなくWii Fitバランスボードにも対応しています。
WiimoteLibは.NETで開発されたAPIであり、言語に依存しませんが、C#とVisual Basicがメインのターゲットのようです。C++でのサンプルは配布されておらず、またネット上の情報もあまり存在しないのですが、本章を読み進めていくことで、多言語間で問題なく利用できることがわかるでしょう。
C++言語にはさまざまな言語仕様が存在しますが、本章では特に.NETプログラミングとして扱いやすい「C++/CLI」を扱います。CLIとは「Common Language Infrastructure(共通言語基盤)」の略で、C++に慣れ親しんだプログラマーにも、違和感の少ない形で先進的な.NETプログラミング環境を提供しています。本章ではその特徴を利用し、同じ基本的なプログラミングをC++とC#という2つの言語で解説するというわけです。
WiimoteLibのライセンスは「Microsoft Permissive License (Ms-PL)」です。Ms-PLは、最も制限の緩いマイクロソフトのソースコードライセンスで、ソースコードを商用または非商用の目的で表示、変更、再頒布できます。また希望する場合は、変更したソースコードに対してライセンス料を課金することもできます。以下に条文の日本語参考訳を引用しておきます(http://www.microsoft.com/japan/resources/sharedsource/licensingbasics/permissivelicense.mspx)。
Microsoft Permissive License (Ms-PL) 公開日: 2007年7月9日
本ライセンスは、付属するソフトウェアの使用に適用されます。本ソフトウェアを使用する場合は、本ライセンスに同意したものとみなします。本ライセンスに同意しない場合、本ソフトウェアを使用することはできません。
1.定義
本ライセンスでは、"複製する"、"複製"、"二次的著作物"、および"頒布"という用語は、米国の著作権法の下で使われる場合と同じ意味を有します。"コントリビューション"とは、オリジナルのソフトウェア、またはソフトウェアに対する追加もしくは変更を意味します。"コントリビューター"とは、本ライセンスの下で自らのコントリビューションを頒布する者を意味します。"対象特許"とは、コントリビューションが直接抵触する、コントリビューターの有する特許権の請求範囲を意味します。
2.権利の付与
(A)著作権に関する許諾-第3条「条件および制限」を含む本ライセンスの条件に従って、各コントリビューターは使用者に対し、コントリビューションを複製し、コントリビューションの二次的著作物を作成し、コントリビューションまたは作成した二次的著作物を頒布する、非独占的、世界全域、無償の著作権ライセンスを付与します。(B)特許権に関する許諾-第3条「条件および制限」を含む本ライセンスの条件に従って、各コントリビューターは使用者に対し、本ソフトウェアのコントリビューションまたは本ソフトウェアのコントリビューションの二次的著作物を作成し、作成させ、使用し、販売し、販売を提案し、輸入し、および/またはその他の方法で処分する、対象特許に基づく非独占的、世界全域、無償の特許権ライセンスを付与します。
3.条件および制限
(A)商標の除外-本ライセンスでは、コントリビューターの名前、ロゴ、または商標を使用する権限は与えられません。
(B)使用者が、本ソフトウェアによる侵害を主張する特許に関し、コントリビューターに対して特許侵害を主張する場合、当該コントリビューターによる本ソフトウェアについての使用者に対する特許ライセンスは自動的に終了します。
(C)本ソフトウェアの全部または一部を頒布する場合、本ソフトウェアに付属するすべての著作権、特許権、商標、および出所の表示を保持する必要があります。
(D)本ソフトウェアの全部または一部をソースコードの形式で頒布する場合は、頒布物に本ライセンスの完全な写しを含めた上で、本ライセンスの条件の下でのみ頒布することができます。本ソフトウェアの全部または一部をコンパイル済みまたはオブジェクトコード形式で頒布する場合は、本ライセンスに抵触しない条件のライセンスの下でのみ頒布することができます。
(E)本ソフトウェアは現状有姿にてライセンスされます。本ソフトウェアの使用に伴う危険は、すべて使用者が負うものとします。コントリビューターからの明示的な保証または条件は一切ありません。使用地の法律に基づき、本ライセンスでは変更できないその他の消費者の権利が存在する場合があります。使用地の法律に基づいて許可される範囲で、コントリビューターは、商品性、特定目的に対する適合性、非侵害について、黙示的な保証を否定します。
(本サイトはhttp://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspxの参考訳です)
ライセンスを確認したら、WiimoteLibをダウンロードします。原稿出版時点での最新版は2009年1月19日に公開されたWiimoteLib v1.7です。
http://www.codeplex.com/WiimoteLib
ホームページ上部にある「Download」のリンクをクリックすると、ダウンロードページを閲覧できます。配布用のパッケージとソースコードが配布されていますが、ソースコードではないほう「WiimoteLib V1.7 (787K)」をダウンロードしてください。
クリックすると、ライセンス条文が英語で表示されますので(確認したら)「I Agree」クリックし、ダウンロードを開始します。
ダウンロードしたZIPファイルを展開したら、まずは動作確認をします。お使いのBluetoothスタック管理ソフトウェアを起動して、WiiRemoteを1台接続します。接続できたら、展開したフォルダの中にある「WiimoteTest.exe」をダブルクリックして実行してみてください。
正しく実行できると、数秒間の初期化の後、図のような実行画面が表示されるはずです。WiiRemoteを振ったり、ボタンを押すことで、リアルタイムで値が取得できていることが確認できます。
フォームの右上「×」をクリックすると終了します。もし手元に複数のWiiRemoteやセンサーバー、ヌンチャク、ギターコントローラー、WiiFitバランスボードなどありましたら、ぜひ接続して動作を試してみてください。
これでWiimoteLibを使えることが確認できました。エラーが発生した場合、特に「Error reading data from Wiimote...is it connected?」と表示された場合は、Bluetoothでの接続に問題があります。Bluetoothスタックが正しくWiiRemoteを接続しているかどうか、確認してみてください。
さて、展開したファイルのうち「WiimoteLib.dll」が一番重要なファイルです。「WiimoteTest.ext」もこのDLLが同じディレクトリに存在しなければ正しく動作しません。なおWiimoteLibを開発で利用するにあたり、特にインストーラーなどはありませんので、展開したフォルダごと「マイドキュメント\Visual Studio 2008\Projects\WiimotLib_1.7」に移動しておくとこのあとの作業が楽になるでしょう(WiimoteLib.dllだけでもいいのですが、ヘルプや複数のバージョンのDLLが混在すると後々厄介です)。なお「docs」フォルダにある「WiimoteLib.chm」がヘルプファイルです。ドキュメントもしっかりと整備されています。
これでセットアップは終わりです!次の節からは実際に、まずはVisual C#を用いてWiiRemoteを制御していきます。
この節では、Visual C# 2008 Express(以後「C#」と標記)を使って、WiimoteLibでのプログラミングを体験していきます。
Visual C# 2008 Express Editionを起動します。
[ファイル(F)]→[新しいプロジェクト(P)]→[Windowsフォームアプリケーション]を選択します。
「プロジェクト名」に「WiimoteLib01」という名前を付けて[OK]ボタンをクリックします。
数秒待つと新しいプロジェクトが作成されます。興味があればここで[F5]キーを押して、実行してみると良いでしょう。
先ほど作成した空のプロジェクトにWiimoteLibを組み込んでいきましょう。右側に表示されている、ソリューションエクスプローラの[参照設定]を右クリック、[参照の追加(R)...]を選択します。
参照の追加から[参照]を選択し、WiimoteLib.dllを選択します。マイドキュメントの「Visual Studio 2008\Projects」においた「WiimoteLib_1.7\WiimoteLib.dll」を選択し[OK]ボタンをクリックします。
これでソリューションエクスプローラの参照設定にWiimoteLibが追加されたはずです。
それでは最小限のプログラムの実行結果を表示するためのフォームを作成しましょう。ソリューションエクスプローラの「Form.cs」を右クリックして「コードの表示」で表示されるC#のコードに、最も重要な最初の1行「using WiimoteLib;」を追加します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; | using WiimoteLib; //ここを1行追加します namespace WiimoteLib01 { //←指定したプロジェクト名 public partial class Form1 : Form { public Form1() { InitializeComponent(); } } }
以上がC#環境でWiimoteLibを用いるための最初の一歩の操作です。まだWiiRemoteらしいことは何もできませんが、これでWiimoteLibのクラスが利用できるようになりました。次のステップで、実際に動作を確認してみましょう。
[F5]キー、または、[デバッグ(D)]→[デバッグ開始(F5)]を押してプログラムを実行させてみましょう。
プログラムにエラーがなければ下図のように表示されるはずです。
このプログラムは単にフォームを生成するプログラムです。「×」ボタンを押してフォームを閉じてプログラムを終了させましょう。以後、このプログラムをベースにWiiRemoteを制御するプログラムを追加していきます。
次はVisual C++ 2008 Express(以後「C++」と標記)を使って、WiimoteLibでのプログラミングを体験していきます。一度流れを覚えたら、以後は共通です。言語もC++かC#、どちらでもかまいません。ご自身が使いやすい言語を選んでみてください。
Visual C++ 2008 Express Editionを起動します。
[ファイル(F)]→[新しいプロジェクト(P)]から、[CLR]の[Windowsフォームアプリケーション]を選択します。
「プロジェクト名」に「WiimoteLib01cpp」という名前を付けて[OK]ボタンをクリックします。
新しいプロジェクトが作成されました。興味があればここで[F5]キーを押して、実行してみると良いでしょう。何もないフォームが表示され、「×」ボタンを押すと終了します。
先ほど作成したプロジェクトにWiimoteLibを組み込んでいきましょう。ソリューションエクスプローラでプロジェクト(ここでは「WiimoteLib01cpp」)を右クリックして[参照]を選択します。
プロジェクトのプロパティページから[新しい参照の追加(N)...]をクリックし、[参照]タブをクリックし、ファイル選択ダイアログで、マイドキュメントの「Visual Studio 2008\Projects」においた「WiimoteLib_1.7\WiimoteLib.dll」を選択し[OK]ボタンをクリックします。
次に、WiimoteLibの初期化コードを書きます。ソリューションエクスプローラの「Form1.h」を右クリックして「コードの表示」を選ぶと、Form1.hのコードが表示されます。このコードの先頭12行目に以下のように、必要な1行を書き加えてください。
#pragma once namespace WiimoteLib01cpp { //←指定したプロジェクト名 using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; //これを1行追加します <以下略>
以上がC++/CLIでWiimoteLibを用いるための必要最低限のプログラムです。[F5]キーを押してプログラムを実行させてみましょう。
プログラムにエラーがなければC#と同様、何もないフォームが表示されるはずです。いまのところ、このプログラムは単にフォームを生成するだけのプログラムですが、WiimoteLibのクラスがusing namespace宣言によって問題なく組み込まれていることがわかります。以後、このプログラムをベースにWiiRemoteを制御するプログラムを追加していきます。
ここからは、さらにWiimoteLibのAPIを用いてプログラミングを行っていきます。解説はC++とC#を並列して解説していきますが、.NETフレームワークのおかげでGUIの設計などはまったく同じ操作で開発を進めることができます。
まず、PC画面上に表示されるFormボタンによって、WiiRemoteの振動機能(バイブレーター)の動作をあやつるするプログラムを作ります。
前節のとおり、WiimoteLibを組み込んだプロジェクトのメインのコード(Form1.csもしくはForm1.h)に以下の3行を追加します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; | using WiimoteLib; //WiimoteLibを宣言 namespace WiimoteLib01 { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスの作成 public Form1() { InitializeComponent(); | wm.Connect(); //Wiimoteに接続 } } } <以下略>
#pragma once namespace WiimoteLib01cpp { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; //WiimoteLibを宣言 <中略> public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteクラスの入れ物 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteクラスの作成; InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // | wm->Connect(); //Wiimoteに接続 } <以下略>
C#もC++も多少の記号や予約語は違えど、ほとんど同じであることがお分かりいただけたでしょうか?C++ではwmという入れ物をForm1クラスのPublicメンバーとして用意しています。
まず、フォームにボタンを貼り付けてください。C#では、ツールボックスからドラッグしてForm1の好きな位置に配置します。(C++でも同様です)表示されていない場合、[表示]から[デザイナ]としてForm1のデザインを表示し、再度[表示]から[ツールボックス]を選ぶことで右側にツールボックスウィンドウが現れます(C++は左側)。「コモンコントロール」に「Button」がありますので、フォームの上にドラッグしてください。
次に、貼り付けた「button1」のプロパティシートの「Text」を「button1」から「ON」に変更します。これでフォーム上のボタンに書かれているテキストが「ON」に変わるはずです。
Form1上に配置したボタン「ON」をダブルクリックすると、ボタンクリック時のイベントを指定するコードが自動的に表示されますので下記のように記述します。
private void button1_Click(object sender, EventArgs e) { wm.SetRumble(true); //バイブレーションON }
#pragma endregion private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { wm->SetRumble(true); //バイブレーションON } }; }
気がはやる方はここで[F5]キーを押したくなるかもしれませんが、試すのは次のステップまで進んでからにしましょう!このままでは、Bluetooth接続されていませんし、バイブレーターを駆動してもまだ止める方法を実装していませんので、ブルブル鳴りっぱなしの困った状態になってしまいます。
バイブレーターを停止させる「OFF」ボタンを作成します。先ほどと同様に、ツールボックスからボタンを配置します。
先ほどと同じく、貼り付けた「button2」のプロパティのTextを「button2」から「OFF」に変更します。
最後に、貼り付けた「button2」をダブルクリックし、下記のようなコードを記述します。
private void button2_Click(object sender, EventArgs e) { wm.SetRumble(false); //バイブレーションOFF }
private: System::Void button2_Click(System::Object^ sender, \ System::EventArgs^ e) { wm->SetRumble(false); //バイブレーションOFF }
以上で終了です。これだけのプログラムでWiiRemoteのバイブレーション機能のON/OFF制御が可能になります。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; | using WiimoteLib; //WiimoteLibの読み込み namespace WiimoteLib01 { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteの宣言 public Form1() { InitializeComponent(); | wm.Connect(); //Wiimoteの接続 } private void button1_Click(object sender, EventArgs e) { | wm.SetRumble(true); //バイブレーションON } private void button2_Click(object sender, EventArgs e) { | wm.SetRumble(false); //バイブレーションOFF } } }
#pragma once <略> | using namespace WiimoteLib; //WiimoteLibを宣言 <略> public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteクラスの入れ物 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteクラスの作成; InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // | wm->Connect(); //Wiimoteに接続 } <略> #pragma endregion private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->SetRumble(true); //バイブレーションON } private: System::Void button2_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->SetRumble(false); //バイブレーションOFF } }; }
まず、お使いのBluetoothスタックからWiiRemoteを接続します。
続いて、Visual C#/C++から「F5」キーを押してプログラムを起動します。エラーがなければ下のようなフォームが起動するはずです。
もし下のようなエラーが発生する場合はWiiRemoteが正しく接続されているか確認してください。
無事に起動できた場合、「ON」ボタンをクリックすると、バイブレーションがONになりWiiRemoteが振動します。あわてず騒がず「OFF」ボタンをクリックして、WiiRemoteの振動をとめましょう。
どうでしょうか?非常に簡単にWiiRemoteのバイブレーション制御プログラムが作れました。このようにWiimoteLibとC++/CLIやC#.NETの組み合わせで、簡単にアプリケーションを開発していくことができます。
WiimoteLibにはバイブレーターの制御以外にもWiiRemoteが制御するための関数がたくさん揃っています。
簡単にプログラムの流れを解説しますと、まず「Connect()関数」でWiiRemoteとの接続を行います。このときWiiRemoteが正しく接続、認識されていなかった場合、例外(Exception)が発生します。今回は例外処理を行っていませんので、実際のアプリケーションでは必要に応じて例外処理を追加してください。ボタンを押したときに、「SetRumble()関数」でWiiRemoteのバイブレーションを制御します。()の引数に「true」を入れるとバイブレーションが振動し、「false」を入れるとバイブレーションが停止します。PWM制御(パルス幅変調:Pulse With Modulation)を用いることによってバイブレーションに強弱を付けることができます。簡単に説明すると高速にONとOFFを繰り返すことによってバイブレーションに強弱をつける制御方法です。ここではあえて説明しませんが、余力のある人はチャレンジしてみてください。
以下、このプログラムで使ったWiimoteLibのAPI関数です。
C# | C++ | 解説 |
---|---|---|
using WiimoteLib; | using namespace WiimoteLib; | ネームスペース宣言 |
public Wiimote wm; | public: Wiimote^ wm; | クラスの宣言 |
wm = new Wiimote(); | Wiimote^ wm = gcnew Wiimote(); | クラス新規作成 |
wm.Connect(); | wm->Connect(); | WiiRemoteに接続 |
wm.SetRumble(true); | wm->SetRumble(true); | バイブレーター作動 |
wm.SetRumble(false); | wm->SetRumble(false); | バイブレーター停止 |
いかがでしょう、.NET環境において、C#とC++では何ら代わりのないことがよくわかります。GUIによるフォーム作成も、マウスドラッグとプロパティの設定、ダブルクリックによる該当コードの自動生成がありますので非常に快適にコーディングできます。次のステップでは同じ要領で、LEDの点灯を制御していきます。
次にWiiRemoteのLED制御についてプログラミングを行っていきます。ここでは、Formボタンをクリックする毎にWiiRemoteの「プレイヤーインジケーター」と呼ばれる青色LEDをカウントアップさせていきます。
準備にあたっての基本的なプログラミングの流れは前節のバイブレーターの制御の場合と同じです。新しいプロジェクトを作成し、プロジェクトのクラスの参照設定にWiimoteLibを追加し、以下の初期化コードを書き足してください。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; |using WiimoteLib; //WiimoteLibの使用を宣言 namespace WindowsFormsApplication1 { //←プロジェクト名によって異なる public partial class Form1 : Form { |Wiimote wm = new Wiimote(); //Wiimoteの宣言 |int count=0; //カウントの宣言 public Form1() { InitializeComponent(); | wm.Connect(); //WiiRemoteへ接続 } } }
#pragma once namespace WLCLED { //←プロジェクト名によって異なる using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; //WiimoteLibの使用を宣言 <略> public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmの宣言 | public: int count; //LEDカウント用の変数countの宣言 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteインスタンスの作成 InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // | wm->Connect(); //WiiRemoteへ接続 } protected: <略>
確認のためにここでWiiRemoteのBluetooth接続を行い、[F5]キーで実行を試してみることをお勧めします。コンパイルエラーやWiimoteLib.dllの追加わすれなど、ただの空白のフォームが表示されるだけの状態ですが、確認は大事です。今後も、この初期化コード作成までの流れ何度も行いますので、カラダで覚えてしまいましょう。
なおC++では、int型の変数countや、Wiimoteオブジェクトを格納するwmの宣言を、Form1のインスタンスとは別に行う必要があります。そうしなければ他のメソッドから扱うことができません。今後、細かいところでC#と違いが出てきますので注意してください(興味のある人は、わざと間違えてみるのも勉強になっていいかもしれませんが)。
次はC#/C++で共通の作業です。先ほどのバイブレーターの例と同様にフォーム「Form1」にボタンを貼り付けてください。ボタンを押すたび表示が変わる仕組みも取り入れますので、ボタンは少し大き目、横長にしておくと良いでしょう。
次に貼り付けた「button1」をダブルクリックして、以下のコードを追加します。
private void button1_Click(object sender, EventArgs e) { this.button1.Text ="wm.SetLEDs("+ count+") を表示中"; this.wm.SetLEDs(count); count++; }
private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { button1->Text = "wm->SetLEDs("+ count +")を表示中"; wm->SetLEDs(count); count++; }
以上でコーディングは終了です。たったこれだけのプログラムでWiiRemoteのLED制御が可能になります。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; |using WiimoteLib; namespace WL_LED { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); | int count = 0; public Form1() { InitializeComponent(); | wm.Connect(); } private void button1_Click(object sender, EventArgs e) { | this.button1.Text = "wm.SetLEDs(" + count + ")を表示中"; | wm.SetLEDs(count); | count++; } } }
#pragma once namespace WLCLED { //作成したプロジェクト名、自由。 using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; /// <summary> <略> /// </summary> public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmの宣言 | public: int count; //LEDカウント用の変数countの宣言 public: Form1(void) { | wm = gcnew Wiimote(); InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // | wm->Connect(); //WiiRemoteへ接続 } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~Form1() { if (components) { delete components; } } private: System::Windows::Forms::Button^ button1; protected: <略> #pragma endregion private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { | button1->Text = "wm->SetLEDs("+ count +")を表示中"; | wm->SetLEDs(count); | count++; } }; }
若干C++のほうがコードが長くなりますが、自動で追加された以外の箇所の意味合いはC#でもC++でも、ほぼ同じであることがわかります。
それでは早速実行してみましょう。
次に、Visual C#/C++の[F5]キーを押して実行してください。実行すると下図のようなアプリケーションが起動します。
もし下図のようなエラーが発生する場合はWiiRemoteが正しく接続されているか確認してください。
フォームに表示されるボタンをクリックしていくと、WiiRemote下部のLEDが次々と光っていきます。
「×」をクリックして終了します。
LEDの点灯と消灯はこのAPIを利用します。
SetLEDs(int32 leds);
この関数の引数「leds」にint32形式の数値をを入れることで、対応するLEDが変化します。このプログラムでは変数countの値を入れ「SetLEDs(count);」としています。Formに配置されたボタンをクリックすると、count値が+1されていきます。
引数は整数(int32形式)で与えますが、これはWiiRemoteを逆さまにした状態の各LEDを4ビットの2進数で表現して、各ビットを0から15まで足していったものです。2進数に馴染みがない方のために、表で表現してみました(●が消灯、○が点灯です)。
10進数(int) | LED4 | LED3 | LED2 | LED1 |
---|---|---|---|---|
0 | ● | ● | ● | ● |
1 | ● | ● | ● | ○ |
2 | ● | ● | ○ | ● |
3 | ● | ● | ○ | ○ |
4 | ● | ○ | ● | ● |
5 | ● | ○ | ● | ○ |
6 | ● | ○ | ○ | ● |
7 | ● | ○ | ○ | ○ |
8 | ○ | ● | ● | ● |
9 | ○ | ● | ● | ○ |
10 | ○ | ● | ○ | ● |
11 | ○ | ● | ○ | ○ |
12 | ○ | ○ | ● | ● |
13 | ○ | ○ | ● | ○ |
14 | ○ | ○ | ○ | ● |
15 | ○ | ○ | ○ | ○ |
フォーム上のボタンを押し続けて、countに10進数の16になると、LED0〜LED4の桁はそれぞれ0になりLEDは全て消えますが、それ以上の値(17,18,19,...)が入っても、また下位ビットに値が入りますので、LEDはカウントアップしつづけます。
プログラムによっては2進数→10進数ではなく、個々のLEDを指定して光らせたいときもあるでしょう。そういったときは、関数フォーマットが異なる以下の形式を利用します:
SetLEDs(bool led1 ,bool led2 ,bool led3,bool led4);
同じ関数でも、引数を4つ指定することで、各々のLEDを制御することが可能になります。(プログラミング用語では、このような関数の利用の仕方をオーバーロードといいます)
以上でLEDの制御は終わります。非常にシンプルですが、アイディア次第でいろいろなことができますので、ぜひ発想を豊かにして何に使えるか考えてみてください。またその表現に合わせた便利な出力用関数も作ってみるとよいでしょう。例えば、筆者は「SetLEDbyValue(int)」という関数を作って、4段階の値を表示できるようにしました。「レースゲームでのシールド残量」や「受けたダメージの表現」、それから処理の段階を表すプログレスバーにもLEDが使えます。
上記の「SetLEDs」のようなWiimoteLibに実装されているAPI関数それぞれの機能は、WiimoteLibの「docs」フォルダにあるヘルプファイル「WiimoteLib.chm」を参照することで探すことができます。例えば、このAPI関数の場合は以下のように記載されています。
<Wiimote.SetLEDs Method>Overload List
Name | Description | |||
---|---|---|---|---|
SetLEDs(Int32) | Set the LEDs on the Wiimote | |||
SetLEDs(Boolean | Boolean | Boolean | Boolean) | Set the LEDs on the Wiimote |
See Also Wiimote Class Wiimote Members WiimoteLib Namespace
つまり関数「SetLEDs()」には、今回のようにInt32の値1つで指定する方式と、Booleanつまり点灯するかどうかの真偽(true/false)の4つで指定する方法の2種類が用意されているということです。どちらも同じ結果ではあるのですが、このようにして、WiimoteLibなどのAPIを作った人は便利にアクセスできるように、たくさんの気の利いた関数を開発しているということですね。下の「See Also」には所属しているクラスやメンバー関数などへのリンクがあります。
わからないことがあったり「こんな機能ないかな?」と思ったときは、このヘルプファイルを活用しましょう。このヘルプファイルはプログラムコードから自動生成されているようですが、検索機能も備えており、C#とVBのコードも含まれていて、勉強になります。
次のステップではWiiRemoteのボタン入力について学びます。ボタンのON/OFFを取得して、フォームに表示するシンプルなプログラムを作成します。
さきほどまでのプログラムと同様に、新しいプロジェクトを作成し、参照設定にWiimoteLibを追加して準備ができたら、「Form1」にラベル(Labelコントロール)を4つ貼り付けてください。
このLabelの文字は、後ほどプログラム側から書き換えますので設定は不要です。
WiiRemoteのボタンを押したときに発生するイベントを利用して、このラベルを表示する値を変更することで、現在のボタン入力の状態を表示するという設計でプログラムを作っていきます。
Form1.cs(C#)もしくはForm1.h(C++)に以下の部分を追加します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; |using WiimoteLib; //WimoteLibの使用を宣言 namespace WiimoteLib_Sample //←作成したプロジェクト名 { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 public Form1() { | Control.CheckForIllegalCrossThreadCalls = false; //おまじない InitializeComponent(); | wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 | wm.Connect(); //WiiRemoteと接続 } | //Wiiリモコンの値が変化したときに呼ばれる関数 | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) | { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | this.DrawForms(ws); //フォーム描写関数へ | } | //フォーム描写関数 | public void DrawForms(WiimoteState ws) | { | this.label1.Text = "Button A:" + (ws.ButtonState.A); //ボタンA | this.label2.Text = "Button B:" + (ws.ButtonState.B); //ボタンB | this.label3.Text = "Button 1:" + (ws.ButtonState.One);//ボタン1 | this.label4.Text = "Button 2:" + (ws.ButtonState.Two);//ボタン2 | } } }
#pragma once namespace WiimoteLib_Sample { //←作成したプロジェクト名 using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; //WimoteLibの使用を宣言 /// <summary> <略> /// </summary> public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 public: Form1(void) { | Control::CheckForIllegalCrossThreadCalls = false; //おまじない | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //イベント関数の登録 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | wm->SetReportType(InputReport::Buttons, true); //レポートタイプの設定 | wm->Connect(); //WiiRemoteと接続 | } public: | void wm_WiimoteChanged(Object^ \ sender,WiimoteLib::WiimoteChangedEventArgs^ args){ | WiimoteState^ ws; | ws = args->WiimoteState; | this->DrawForms(ws); | } | public: void DrawForms(WiimoteState^ ws) { | this->label1->Text = "Button A:" + (ws->ButtonState.A); | this->label2->Text = "Button B:" + (ws->ButtonState.B); | this->label3->Text = "Button 1:" + (ws->ButtonState.One); | this->label4->Text = "Button 2:" + (ws->ButtonState.Two); | } protected: <略>
さて、実行してみましょう。まずお使いのBluetoothスタックから、WiiRemoteをBluetoothでPCに接続します。接続が確認できたら、Visual C#/C++上で[F5]キーを押して実行します。コンパイルがとおり、正しく実行されると下図のようなアプリケーションが起動します。もしここでエラーが発生する場合、ほとんどがBluetooth接続がうまく接続されていないケースです。WiiRemoteが正しく接続されているか確認してください。
WiiRemoteのAボタンを押し続けます。
フォーム上の「Label1」の箇所に「Button A: True」と表示されれば成功です。さらにWiiRemoteのすべてのボタンから手を離して、すべてのボタンを押さない状態にしたとき、フォーム上のボタンのステータスを表す表示が全て「False」になれば成功です。
WiiRemoteのAボタンとBボタンを同時に押しこんでみます。
最後に、WiiRemoteの1ボタンと2ボタンを同時に押しこんでみます。
一通りの実験が終わったら、マウスで「×」をクリックするか、キーボードから[Alt+F4]をおして、プログラムを終了させます。
WiiリモコンのボタンのON/OFFによってFormのラベルを変化させています。FalseがOFF(手を離した状態), TrueがON(押下)に対応しています。以下、利用したAPI関数を解説します。
[C#] wm.WiimoteChanged += wm_WiimoteChanged; [C++] wm->WiimoteChanged += gcnew System::EventHandler<WiimoteChangedEventArgs^>( this, &Form1::wm_WiimoteChanged);
WiiRemoteのボタンが押された、加速度センサーの値が変わった、など、状態に変化があったときに呼ばれる関数を登録しています。プログラミング用語で「コールバック関数」といい、関数名を登録することで、そのイベントが発生したときに自動的にその関数が実行されるように決められた方法で指定します。ここでは、WiiRemoteのボタン状態に変化があった場合「wm_WiimoteChanged」という関数が呼ばれるように設定しています。カッコや引数などはあらかじめ規定された形式に沿っていますので、関数名を渡すだけでだけでよいのです(C++のコードが少し長いのはそのためです)。
[C#] WiimoteState ws = args.WiimoteState; [C++] WiimoteState^ ws; ws = args->WiimoteState;
ここでWiiリモコンのステータス(状態)をwsという名前の"イレモノ"にとりこんでいます。正式にはここで使っているWiimoteStateはクラスですが、その名前から想像できるように、イベントで発生したボタンなどの値が取り込まれます。以下、使い方を見てみましょう。
[C#] this.label1.Text = "Button A: " + (ws.ButtonState.A); [C++] this->label1->Text = "Button A:" + (ws->ButtonState.A);
WiiRemoteのAボタンの値をlabel1に表示しています。ボタンが押されていたら、Trueを表示します。ボタンが離されていたら、Falseを表示します。実際には「ws.ButtonState.A」が意味する値はTrueかFalseという真偽の値ですが、左側が「label1.Text」なので自動的に文字列に変換されています(.ToString()する必要はない)。
同様に「ws.ButtonState.b」などとすることでWiiRemoteのBボタン、その他全てのボタンの状態を取得することができます。
どうでしょう?とっても簡単ですね!このイベントのコールバック関数でステートを取得する方法は他のいろいろな入力に応用できます。しかし、この完成したコールバックの仕組みを簡単に利用できる背景には、様々な複雑なプログラミングの内側の技術があります。実はフォームのコンストラクタで、1つだけ、おまじないをしていました。
[C#] Control.CheckForIllegalCrossThreadCalls = false; [C++] Control::CheckForIllegalCrossThreadCalls = false;
余裕のある人は、この行をコメントアウトして、実行してみてください。なぜか実行時にボタンを押すとエラーが出てしまいます。これは、マルチスレッド(スレッド;Thread≒処理の流れ)に関わる問題です。上のボタンの状態を読み込むWiimoteLibのスレッドと、フォームの書き換えを行うスレッドがそれぞれ異なるので「スレッドセーフでない」つまり、複数のスレッドにおける処理の順序などが保証できないため実行時エラーになってしまいます。WiimoteLibの公式で紹介されている方法で、
//Wiiリモコンの値が変化したときに呼ばれる関数[C#] void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args){ WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 if (this.IsHandleCreated) { this.Invoke( (MethodInvoker)delegate() { this.DrawForms( ws ); //フォーム描写関数へ }); } }
このように、Invoke()というメソッドを使う方法もありますが、ちょっと初心者には不明瞭な書き方です。何が起きていて、どんなリスクがあるか(フォームの書き換えがスレッドセーフでなく上書きされる)ということがわかっているなら
Control.CheckForIllegalCrossThreadCalls=false;
として、不正なスレッド間コールのチェックをしない、と宣言する方法もあるので、本章では「おまじない」として以後この方法を採用することにします。
ここでWiimoteStateで参照できるメンバーを表で紹介しておきます。
名称 | 解説 |
---|---|
AccelCalibrationInfo | 現在の加速度センサーのキャリブレーション情報 |
AccelState | 現在の加速度センサーの状態 |
BalanceBoardState | 現在のWiiFitバランスボードの状態 |
Battery | 算出された現在のバッテリーレベル |
BatteryRaw | 現在のバッテリーレベルの計算前の値(生値) |
ButtonState | 現在のボタンの状態 |
ClassicControllerState | 現在の拡張クラシックコントローラーの状態 |
DrumsState | 現在の拡張ドラムコントローラーの状態 |
Extension | 拡張コントローラーが挿入されているか |
ExtensionType | 拡張コントローラーが挿入されている場合その種類 |
GuitarState | 現在のGuitarHero拡張ギターコントローラーの状態 |
IRState | 現在の赤外線センサーの状態 |
LEDState | 現在のLEDの状態 |
NunchukState | 現在の拡張ヌンチャクコントローラーの状態 |
Rumble | 現在のバイブレーターの状態 |
WiimoteLibには実に様々な拡張コントローラーが実装されており、このメンバーから状態を取得できることがわかります。これらの値やメソッドなどは、Visual StudioのIntellisense機能を使ってどんどん効率化していきましょう。先ほどのボタンの例なども、「ws.ButtonState.」と「.」を押した瞬間には正しい標記の選択肢が現れます。ボタンの名称などはいちいち覚えていられませんので、非常に便利です。なおIntellisenseはCtrl+Spaceでいつでもどこでも呼び出せます。全てのグローバルなオブジェクトが表示されます。辞書代わりに使うとよいでしょう。
さて、これまでWiimoteLibを使って、基本的な入出力を学んできました。このあたりで、実用的なプログラムの例として「ランチャー」を作成してみましょう。ボタンを押すたびに、Windowsのアクセサリ「メモ帳」や「電卓」など外部プログラムが起動するプログラムです。
前節の「ボタンイベントの取得」と基本は同じです。WiimoteLibの宣言を行い、コールバック関数内で外部プログラムを起動したり、アプリケーション自身を終了させたりします。
新しいプロジェクトを作成し、WiimoteLibを参照に追加し、Form1を右クリックして「コードを表示」し、下のコードを記述します。前節のプログラムの改造から始めても良いでしょう。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; |using WiimoteLib; //WimoteLibの使用を宣言 namespace WL_Launcher { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 public Form1() { | Control.CheckForIllegalCrossThreadCalls = false; //おまじない InitializeComponent(); | wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 | wm.Connect(); //WiiRemoteと接続 } | //Wiiリモコンの値が変化したときに呼ばれる関数 | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) | { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | //Aボタンが押されたらメモ帳を起動 | if (ws.ButtonState.A == true) | { | System.Diagnostics.Process.Start("notepad.exe"); | } | //Bボタンが押されたら電卓を起動 | if (ws.ButtonState.B == true) | { | System.Diagnostics.Process.Start("calc.exe"); | } | //HOMEボタンが押されたらこのアプリを終了 | if (ws.ButtonState.Home == true) | { | Environment.Exit(0); | } | } } }
#pragma once namespace WLCLauncher { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; | using namespace WiimoteLib; //WimoteLibの使用を宣言 public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 public: Form1(void) { | Control::CheckForIllegalCrossThreadCalls = false; //おまじない | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //イベント関数の登録 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | wm->SetReportType(InputReport::Buttons, true); //レポートタイプの設定 | wm->Connect(); //WiiRemoteと接続 | } public: void wm_WiimoteChanged(Object^ sender,WiimoteLib::WiimoteChangedEventArgs^ \ args){ | WiimoteState^ ws; | ws = args->WiimoteState; | //Aボタンが押されたらメモ帳を起動 | if (ws->ButtonState.A) { | System::Diagnostics::Process::Start("notepad.exe"); | } | //Bボタンが押されたら電卓を起動 | if (ws->ButtonState.B) { | System::Diagnostics::Process::Start("calc.exe"); | } | //HOMEボタンが押されたらこのアプリを終了 | if (ws->ButtonState.Home) { | Environment::Exit(0); | } | } <以下略>
まずは、いつも通りWiiRemoteを接続してください。そしてVisual Studioの[F5]キーを押して、作成したプログラムを実行します。もしここでエラーが発生する場合はWiiRemoteが正しく接続されているか確認してください。
WiiRemoteのAボタンを押すと、メモ帳が起動し、Bボタンを押すと、電卓が起動します。
WiiRemoteのBボタンを数回おすと、押した回数だけ、電卓が起動します。
WiiRemoteのHomeボタンを押すと、ランチャープログラムが終了します。
このプログラムの「notepad.exe」や「calc.exe」を好きな外部プログラムに書き換えれば、何でも起動できるというわけですね。なんだかいろいろなことができそうです。楽しくなってきませんか?
WiiRemoteのボタンが押されることによって設定したアプリケーションが起動するためには、先ほどのプログラムのDrawForm()で処理したような、ラベルのテキストを書き換える代わりに、.NETで用意されている仕組みを利用して、外部プログラムを起動します。
if (ws.ButtonState.A == true) { //Aボタンがおされたらメモ帳を起動 System.Diagnostics.Process.Start("notepad.exe"); }
「System.Diagnostics.Process.Start()」についてはいろいろな応用があります。テキストファイルなどを指定することで関連づけられたプログラムを使って開くことなども可能です。詳しくはインターネットで公開されている.NET Frameworkクラスライブラリのマニュアルや「Process.Start」をキーワードに検索してみると良いでしょう。
以上は、C#での記述ですが、C++/CLIでも全く違和感なく互換性が保たれています。C++でHomeボタンを押して終了する箇所のコードを見てみましょう。
//HOMEボタンが押されたらこのアプリを終了 if (ws->ButtonState.Home) { Environment::Exit(0); }
以上のように「Environment::Exit(0);」で、自分自身のアプリケーションを終了できます。
さて、こんな便利なコールを覚えると、ランチャーで起動したプログラムの終了などもやってみたくなると思います。
この先の赤外線ポインタを用いてマウスを作成する例を学習してから、さらに高機能に改造してみるとよいでしょう。
次は、WiiRemoteの加速度センサーを使ったプログラミングを実験していきます。2章で解説したとおり、WiiRemoteにはX軸、Y軸、Z軸に対応した3軸の加速度センサーが内蔵されています。
WiiRemoteに内蔵された3軸マイクロ加速度センサーは、それぞれの軸に対して8bit、つまり0〜255の値を持ちます。このセクションでは、まず値がとれるプログラムを作成し、その後、応用アプリケーションの開発を通して、加速度センサーの基本的な利用に挑戦します。
ここでは、まず手始めに、WiiRemoteの加速度を取得するプログラムを作成します。先ほどまでと同様、Visual Studio(C#/C++どちらでもかまいません!)で、新しいプロジェクトを作成し、参照に「WiimoteLib」を追加します。自動的に生成されているフォーム「Form1」に、ツールボックスからラベル(Label)を3つ貼り付けてください。ここに加速度センサーX,Y,Zのリアルタイム測定値を表示します。
続いてコーディングです。Form1を右クリックして「コードを表示」して、以下のプログラムのコメントアウトしている箇所を書き足していきます(自動生成されたコメント行は割愛しています)。また冒頭のusing句によるクラスの宣言ですが、最小限必要な物のみにしています。
using System; using System.Windows.Forms; | using WiimoteLib; //WimoteLibの使用を宣言 namespace WL_Accel { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 public Form1() { | Control.CheckForIllegalCrossThreadCalls = false; //おまじない InitializeComponent(); | wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 | wm.SetReportType(InputReport.ButtonsAccel, true); //レポートタイプの設定 | wm.Connect(); //WiiRemoteと接続 } | //Wiiリモコンの値が変化したときに呼ばれる関数 | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | this.DrawForms(ws); //フォーム描画関数へ | } | //フォーム描画関数 | public void DrawForms(WiimoteState ws) { | this.label1.Text = "X軸:" + (ws.AccelState.Values.X); | this.label2.Text = "Y軸:" + (ws.AccelState.Values.Y); | this.label3.Text = "Z軸:" + (ws.AccelState.Values.Z); | } } }
#pragma once namespace WLCAccel { using namespace System; using namespace System::Windows::Forms; | using namespace WiimoteLib; //WimoteLibの使用を宣言 public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 public: Form1(void) { | Control::CheckForIllegalCrossThreadCalls = false; //おまじない | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //イベント関数の登録 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | wm->SetReportType(InputReport::ButtonsAccel, true); //レポートタイプの設定 | wm->Connect(); //WiiRemoteと接続 } | public: | void wm_WiimoteChanged(Object^ \ sender,WiimoteLib::WiimoteChangedEventArgs^ args){ | WiimoteState^ ws; | ws = args->WiimoteState; | this->DrawForms(ws); | } | public: | void DrawForms(WiimoteState^ ws) { | this->label1->Text = "X軸:" + (ws->AccelState.Values.X); | this->label2->Text = "Y軸:" + (ws->AccelState.Values.Y); | this->label3->Text = "Z軸:" + (ws->AccelState.Values.Z); | } <以下略>
それでは実験してみましょう。まずWiiRemoteをお使いのBluetoothスタックで接続してください。
次にVisual Studio上で[F5]キーを押して実行してください。正しくプログラムが書かれておらず、エラーなどが出る場合はよく確認して、修正してください。
フォームが表示されたら、WiiRemoteを振りまわしてみてください。このとき、調子に乗って振り回しすぎて飛んでいくと危険なので、大振りするときは必ずストラップをしてください。
フォームに張り付けた、加速度のX,Y,Zの値が素早く動いていることがわかります。
【注意】起動時に3つの値がゼロのままだった場合、一旦作成したアプリケーションを終了させて、WiimoteLibのサンプル「WiimoteTest.exe」を実行してみてください。一度このサンプルを起動してから、今回作成したアプリケーションを起動すると値がとれることがあります(エラー処理や初期化を丁寧にしていないからかもしれません)、不具合があったときは試してみてください。
また、終了時に「Dispose」に関するエラーが出るときがありますが、これもいまのところ無視してかまいません。
WiiRemoteの3軸の加速度センサーのリアルタイム値を表示しました。
[C#] wm.SetReportType( InputReport.ButtonsAccel, true); [C++] wm->SetReportType(InputReport::ButtonsAccel, true);
レポートタイプ、すなわちイベントが起きたときに報告するようにWiiRemoteにお願いする「種類」をここで設定しています。「ButtonsAccel」は加速度センサーとボタンイベントを取得しています。
[C#] ws.AccelState.Values.X [C++] ws->AccelState.Values.X
WiiRemoteに内蔵された加速度センサー各軸の生値をfloatで取得します。
[C#] Control.CheckForIllegalCrossThreadCalls = false; [C++] Control::CheckForIllegalCrossThreadCalls = false;
前回と同じく、別のスレッドからフォームを書き換えることを許可します。
レポートタイプに「ButtonsAccel」を指定しているので、この状態でボタンイベントなども取得できます。余力のある人は試してみましょう。そして、実際にどれだけの値が出力されるか実験してみましょう。ブンブン振ってみると、実測でだいたい±5程度の値が計測されます。WiiRemoteを直立させると、X,Zなど2つの値はゼロになりますが、もうひとつの軸、たとえばY軸にはかならず±0.9程度の値が残ります。
これは何でしょう……?そうです、重力加速度です!普段は目に見えない重力加速度を目で見ることができます。
「レポートタイプ」とはWiiRemoteに問い合わせするときのモードのことで、このレポートタイプによって、WiiRemoteが返す返事が異なります。
WiimoteLib1.7ではInputReport内で以下のレポートタイプが定義されているようです。
レポートタイプは、データのフォーマットを設定する目的の他にも、限りある通信帯域や処理速度を最適に設定する目的があるようです。上記のWiimoteLibで実装されているレポートタイプ以外にも、わかっているだけでも、ボタンのみの入出力から、加速度センサー3種、ヌンチャク付き6種、赤外線付きかどうか、といったより高速でシンプルな入出力モードから、たくさんの値をやりとりするモードまで、各種揃っています。また赤外線センサーについても、最大4点まで扱えるモードに対して2点高速モードなど、隠しモード的なレポートタイプも存在するようです(WiimoteLibでは4点のみサポートしています)。
せっかくWiiRemoteの特徴のひとつである加速度センサーの値が取得できるようになったので「太鼓もどき」を作ってみましょう。太鼓と呼ぶには大げさかもしれませんが、加速度センサーに入力された強さが一定より強くなると...たとえば先ほどの実験で±5程度の値が測定できましたので、この値を超えた場合に、WAVファイルを再生することにします。
先ほどの加速度センサーを使うプログラムの続きから始めると良いでしょう。フォーム「Form1」のコードを表示して、以下のように追記します。
using System; using System.Windows.Forms; | using WiimoteLib; //WimoteLibの使用を宣言 | using System.Media; //System.Mediaの宣言 namespace WL_Taiko { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 | string path = null; //Wavファイル名 | SoundPlayer wavePlayer; //SoundPlayerを宣言 public Form1() { InitializeComponent(); | wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 | wm.SetReportType(InputReport.ButtonsAccel, true); //レポートタイプの設定 | wm.Connect(); //WiiRemoteと接続 | path = @"C:\WINDOWS\Media\chord.wav"; //再生するWAVファイルを指定 | wavePlayer = new SoundPlayer(path); //プレイヤーにWAVファイルを渡す } | //Wiiリモコンの値が変化したときに呼ばれる関数 | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | //WAVファイルが読み込まれているか確認 | if (this.path != null) { | float AX = Math.Abs(ws.AccelState.Values.X); //X軸の絶対値 | float AY = Math.Abs(ws.AccelState.Values.Y); //Y軸の絶対値 | float AZ = Math.Abs(ws.AccelState.Values.Z); //Z軸の絶対値 | //X,Y,Z軸の加速度センサの絶対値の合計が5を超える時に、振ったと判定 | if ((AX+AY+AZ) >= 5) { | wavePlayer.PlaySync(); //音を鳴らす | } | } | } } }
#pragma once namespace WLCTaiko { using namespace System; using namespace System::Windows::Forms; | using namespace WiimoteLib; //WimoteLibの使用を宣言 | using namespace System::Media; //System.Mediaの宣言 public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 | public: String^ path; //WAVファイルパス格納用 | public: SoundPlayer^ wavePlayer; //SoundPlayerを宣言 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //イベント関数の登録 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | wm->SetReportType(InputReport::ButtonsAccel, true); //レポートタイプの設定 | wm->Connect(); //WiiRemoteと接続 | path ="C:\\WINDOWS\\Media\\achord.wav"; //再生するWAVファイルを指定 | wavePlayer = gcnew SoundPlayer(path); //プレイヤーにWAVファイルを渡す } public: void wm_WiimoteChanged(Object^ sender,WiimoteLib::WiimoteChangedEventArgs^ \ args){ | WiimoteState^ ws; | float AX, AY, AZ; | ws = args->WiimoteState; | if (this->path!=nullptr) { | AX = Math::Abs(ws->AccelState.Values.X); //X軸の絶対値 | AY = Math::Abs(ws->AccelState.Values.Y); //Y軸の絶対値 | AZ = Math::Abs(ws->AccelState.Values.Z); //Z軸の絶対値 | //X,Y,Z軸の加速度センサの絶対値の合計が5を超える時に、振ったと判定 | if ((AX+AY+AZ)>=5) { | wavePlayer->PlaySync(); //音を鳴らす | } | } | } <以下略>
Visual C#は[F6]、Visual C++は[F7]キーを押してコンパイルエラーがないことなどを確認したら、BluetoothスタックからWiiRemoteを接続してください(切断されていなければそのまま続行してかまいません)。
Visual Studioの[F5]キーを押して実行してください。無事にエラーなく実行されると、フォームが表示されます。このフォームは今回使用しませんが、WiiRemoteを振ってみると、振りに合わせて、なんだか音が聴いたことのある音が鳴ります。
WiiRemoteの3軸の加速度センサの値を取得して、指定した加速度を検出すると指定したWAVファイルを鳴らします。
[C#] path = @"C:\WINDOWS\Media\achord.wav"; [C++] path ="C:\\WINDOWS\\Media\\achord.wav";
この[email protected]」は[email protected] string」といって、これが先頭に着いている文字列は「\」をバックスラッシュを特殊な文字としてではなく、ファイルパスとして簡単に処理できます。C++にはそれに該当する標記がないようなので「\\」として「\」を特別な1文字として扱っています。
さてここで再生するファイルを指定しています。鳴らしたいWAVファイルを指定してください。このプログラムではWindowsに用意されたWAVファイルとして、Windowsのシステムに最初から入っているWAVを指定しましたが、ご自身で用意された音楽や効果音を指定しても良いでしょう。
[c#] float AX = Math.Abs(ws.AccelState.Values.X); //X軸の絶対値 [c++] AY = Math::Abs(ws->AccelState.Values.Y); //Y軸の絶対値
WiiRemoteのX,Y,Z軸の値を取得し、その絶対値をとります。
//X,Y,Z軸の加速度センサの絶対値の合計が5を超える時に、振ったと判定 if ((AX+AY+AZ)>= 5) { wavePlayer.PlaySync(); }
X,Y,Z軸の加速度の絶対値の和が[5]を超えると音を鳴らすとは、いかにも簡単です。この[5]という値を、小さくすれば少ない動作で反応します。反対に増やせば、大きい動作で反応します。自分の好みの数字に置き換えてみて、調整してみてください。なお今回は、音が再生中にWiiRemoteを振っても反応しません(再生中にも音を連続再生したい場合はスレッド処理などを用いる必要があります)。
さて、加速度センサーをひと通り使いこなしたあとは、赤外線センサーに挑戦してみましょう。赤外線は人間の目で見ることができません。しかし最初に作成するプログラム「赤外線探知機」は、赤外線がWiiRemoteの視界にはいると、バイブレーターを鳴らすことができます。次に最大4点の赤外線光源をカウントし、その結果をLEDに表示するプログラムを作ります。さらにそれを応用し、グラフィックスに組み込む基礎を学び、最後に赤外線センサーを使ったマウス操作プログラムをステップを追って開発していきます。目に見えない赤外線が、とても面白い情報を伝えるメディアになることを体感していきましょう!
早速プログラミングを始めましょう。今回も前回に作成した加速度センサーのプログラムを改編してもよいのですが、新規で作るほうが勉強になってよいでしょう。ソリューションを新しく作成する必要はなく、ソリューションを右クリックして「追加」→「新しいプロジェクト」で新しいプロジェクト名(ここでは「IR1」)を与えて、プロジェクトができあがったら、プロジェクトを右クリックして「スタートアッププロジェクトに設定」し、参照設定にWiimoteLibを追加します。他のプロジェクトのコードやフォームを間違えて編集しないよう、一旦開いているソースコードのウィンドウをすべて閉じます。これで準備はできあがりです。動作確認のために[F5]キーを押して実行してみてもよいでしょう。
このプロジェクトではまず、WiiRemoteへの「接続」「切断」ボタンを作成します。フォームにボタン2つ張り付けてください。
貼り付けたら、「button1」のプロパティのTextを「接続」に、「button2」のプロパティのTextを「切断」に設定します。
フォーム「Form1」を右クリックして「コードの表示」をして宣言と、接続時の処理、赤外線が見えたときのバイブレーターの処理を書き足しましょう。
//不要なusing宣言は削除してかまいません using System; using System.Windows.Forms; | using WiimoteLib; //WimoteLibの使用を宣言 namespace IR1 { public partial class Form1 : Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 public Form1() { InitializeComponent(); | //他スレッドからのコントロール呼び出し許可 | Control.CheckForIllegalCrossThreadCalls = false; } | //接続ボタンが押されたら private void button1_Click(object sender, EventArgs e) { | wm.Connect(); //Wiimoteの接続 | wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 | wm.SetReportType(InputReport.IRExtensionAccel, true);//レポートタイプの設定 } | //切断ボタンが押されたら private void button2_Click(object sender, EventArgs e) { | wm.WiimoteChanged -= wm_WiimoteChanged; //イベント関数の登録解除 | wm.Disconnect(); //Wiimote切断 | wm.Dispose(); //オブジェクトの破棄 } | //Wiiリモコンの値が変化する度に呼ばれる | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | //もし赤外線を1つ発見したら | if (ws.IRState.IRSensors[0].Found) { | wm.SetRumble(true); //バイブレータON | } else { | wm.SetRumble(false); //バイブレータOFF | } | } } }
#pragma once namespace IR1 { using namespace System; using namespace System::Windows::Forms; | using namespace WiimoteLib; //WimoteLibの使用を宣言 public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //他スレッドからのコントロール呼び出し許可 | Control::CheckForIllegalCrossThreadCalls = false; } protected: ~Form1() <省略> #pragma endregion | //接続ボタンが押されたら private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->Connect(); //WiiRemoteと接続 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | wm->SetReportType(InputReport::IRExtensionAccel, true); //レポートタイプの設定 } | //切断ボタンが押されたら private: System::Void button2_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->WiimoteChanged -= | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); //イベント関数の登録解除 | wm->Disconnect(); //Wiimote切断 | wm->Dispose(); //オブジェクトの破棄 } | //WiiRemoteの値が変化する度に呼ばれる | public: | void wm_WiimoteChanged(Object^ \ sender,WiimoteLib::WiimoteChangedEventArgs^ args){ | WiimoteState^ ws = args->WiimoteState; //WiimoteStateの値を取得 | //もし赤外線を1つ発見したら | if (ws->IRState.IRSensors[0].Found) { | wm->SetRumble(true); //バイブレータON | } else { | wm->SetRumble(false); //バイブレータOFF | } | } }; }
さて、実行してみます。まず周囲に赤外線光源を用意してください。Wii本体付属のセンサーバーがあれば手っ取り早いのですが、ない場合は周りにある照明、太陽光などにあたりをつけてみましょう。BluetoothスタックからWiiRemoteを接続して、Visual Studioの[F5]キーを押して実行してください。フォームが表示されたら、[接続]ボタンを押してください。WiiRemoteをセンサーバーなど赤外線光源に向けてください。センサーバーがない場合は、太陽、ハロゲンランプなどの熱源照明、ライターの火、テレビのリモコン、携帯電話の赤外線通信などに向けてみてください。上手く検出できると、バイブレーターが鳴ります。バイブレーターが鳴っていない状態で[切断]ボタンを押してから終了しましょう。
ライターやロウソクなどを用いて赤外線光源にする場合は、火事や火傷などに十分気をつけて実験を行ってください。また、テレビリモコンを用いる場合は、ボタンが押されたときにしか赤外線を送信しませんので、何度か連打して確認をおこなうとよいでしょう。
いろいろな方向に向けてみましよう。普段は見えない赤外線ですが、身の回りにある様々な物に利用されていることに気がつくことでしょう。
上記のコードをそのままコピーしていませんか?ボタンを押したときの処理は、Form1の上にあるボタンをダブルクリックして、Windowsフォームデザイナが自動で生成したコードを使って書いていくのが確実です。もし単に、上記のコードをコピーすると、接続ボタンを押しても、適切な処理にプログラムが流れていきません。この仕組みに『どうして!?』と思った人は、プロジェクトの中にある「Form1.Designer.cs」を覗いてみましょう。ここに「#region Windowsフォームデザイナで生成されたコード」というデフォルトで非表示になっているパートがあります。
#region Windows フォーム デザイナで生成されたコード … this.button1.Click += new System.EventHandler(this.button1_Click_1); … this.button2.Click += new System.EventHandler(this.button2_Click);
ここには、GUIで作成したフォームについての位置や大きさなどのプロパティが記載されています。大事なのは、ボタンを押したときのイベントの発生です。
this.button1.Click+=new System.EventHandler(this.button1_Click_1);
まさにWiiRemoteのイベントの追加と同じように、クリック時のイベントを追加しています。ただし、上の例では「button1_Click_1」という関数になっています。「_1」の部分は、他の既存の関数と名前が衝突しないよう、自動で生成されます。つまり勝手に「button1_Click」という関数を書いていたとしても、ボタンを押したときのイベントとしてコールされることはありません。
『なんだ、わざわざGUIでダブルクリックしないといけないのか!』と思われるかもしれませんが、管理が面倒なイベント渡しなども自動で生成・管理してくれる、.NETスタイルの開発を「裏側まで理解して」使いこなす、というのがカッコイイのではないでしょうか。
このプログラムの仕組みは単純です。コールバック関数を設定して、WiiRemoteの赤外線センサーがひとつでも見つかったら、バイブレーターをONにします。
[C#] wm.SetReportType ( InputReport.IRExtensionAccel, true); [C++] wm->SetReportType(InputReport::IRExtensionAccel, true);
レポートタイプを「IRExtensionAccel」(赤外線+拡張+加速度)取得モードに設定します。このコールはかならずwm.Connect();の後に記述してください。wm.Connect();より前に記述すると赤外線センサーが正しく動作しません。
[C#] ws.IRState.IRSensors[0].Found [C++] ws->IRState.IRSensors[0].Found
このプロパティはTrue/Falseを返しますので、if文を使って赤外線を検出することができます。またWiimoteLibは、同時に4点まで赤外線光源を検出することができます。個々の光源を特定することはできませんが「IRSensors[3].Found」これがTrueなら4つの赤外線光源が見えている、ということです。
続いて、作成した基本的なプログラムを応用して、赤外線の個数を数えるプログラムに拡張します。WiimoteLibには同時に4点までの赤外線を計測することができます。ここまでのプログラムでは1点でも赤外線光源がセンサーの視界に入ると、バイブレーターが振動するようになっていましたが、青色LED(プレイヤーインジケータ)をつかって、何点検出しているかを表示するプログラムを追加します。
void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 //もし赤外線を1つ発見したら if (ws.IRState.IRSensors[0].Found) { wm.SetRumble(true); //バイブレータON } else { wm.SetRumble(false); //バイブレータOFF } | //検出された赤外線個数をWiiリモコンのLEDに表示する | wm.SetLEDs(ws.IRState.IRSensors[0].Found, ws.IRState.IRSensors[1].Found, | ws.IRState.IRSensors[2].Found, ws.IRState.IRSensors[3].Found); }
public: void wm_WiimoteChanged(Object^ sender,WiimoteLib::WiimoteChangedEventArgs^ \ args){ WiimoteState^ ws = args->WiimoteState; //WiimoteStateの値を取得 //もし赤外線を1つ発見したら if (ws->IRState.IRSensors[0].Found) { wm->SetRumble(true); //バイブレータON } else { wm->SetRumble(false); //バイブレータOFF } | //検出された赤外線個数をWiiリモコンのLEDに表示する | wm->SetLEDs(ws->IRState.IRSensors[0].Found, \ ws->IRState.IRSensors[1].Found, | ws->IRState.IRSensors[2].Found, ws->IRState.IRSensors[3].Found ); }
ここでは先ほどLEDの点灯制御で使った「SetLEDs()」関数のうち、4引数のものを使っています。WiiRemoteの赤外線センサーに複数の赤外線が発見されると、バイブレーターの振動と共にLEDインジケータを使って赤外線検出個数を表示します。
さて、赤外線光源の有無や、そのカウントができるようになったので、次は赤外線センサーによる光源座標の取得を行い、フォーム内にグラフィックス機能を使って描画します。先ほどのプロジェクトをそのまま改良して開発することにしましょう。
まず、フォームのデザインを変更します。はじめて使う新しいコントロールを配置します。「ツールボックス」の「コモンコントロール」から「PictureBox」をForm1に張り付けます。プロパティの「Size」を「200, 200」にします。他のボタンやフォームのバランスをとって配置します。
プログラムのほうはまず、冒頭のusing宣言で「System.Drawing」が宣言されていることを確認してください。初期化やボタンイベントはそのままで、WiiRemoteの状態が変化したときに呼ばれる関数「wm_WiimoteChanged()」とフォーム描画関数「DrawForms(ws)」に、描画のためのコードを加筆します。
using System; using System.Windows.Forms; | using System.Drawing; //描画のために必要 | using WiimoteLib; //WimoteLibの使用を宣言 namespace IR4 { //作成したプロジェクト名称 public partial class Form1 Form { | Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 public Form1) { InitializeComponent(); | //他スレッドからのコントロール呼び出し許可 | Control.CheckForIllegalCrossThreadCalls = false; } | //WiiRemoteの状態が変化したときに呼ばれる関数 | void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { | WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 | DrawForms(ws); //フォーム描写関数へ | } <略:ボタンイベント関係> | //フォーム描写関数 | public void DrawForms(WiimoteState ws) { | Graphics g =this.pictureBox1.CreateGraphics(); //グラフィックス取得 | g.Clear(Color.Black); //画面を黒色にクリア | //もし赤外線を1つ発見したら | if (ws.IRState.IRSensors[0].Found) { | //赤色でマーカを描写 | g.FillEllipse(Brushes.Red, | ws.IRState.IRSensors[0].Position.X * 200, | ws.IRState.IRSensors[0].Position.Y * 200, 10, 10); | } | //もし赤外線を2つ発見したら | if (ws.IRState.IRSensors[1].Found) { | //青色でマーカを描写 | g.FillEllipse(Brushes.Blue, | ws.IRState.IRSensors[1].Position.X * 200, | ws.IRState.IRSensors[1].Position.Y * 200, 10, 10); | } | //もし赤外線を3つ発見したら | if (ws.IRState.IRSensors[2].Found) { | //黄色でマーカを描写 | g.FillEllipse(Brushes.Yellow, | ws.IRState.IRSensors[2].Position.X * 200, | ws.IRState.IRSensors[2].Position.Y * 200, 10, 10); | } | //もし赤外線を4つ発見したら | if (ws.IRState.IRSensors[3].Found) { | //緑色でマーカを描写 | g.FillEllipse(Brushes.Green, | ws.IRState.IRSensors[3].Position.X * 200, | ws.IRState.IRSensors[3].Position.Y * 200, 10, 10); | } | g.Dispose();//グラフィックスの解放 | } } }
#pragma once namespace IR4 { //作成したプロジェクト名称 using namespace System::Windows::Forms; | using namespace System::Drawing; | using namespace WiimoteLib; //WimoteLibの使用を宣言 public ref class Form1 : public System::Windows::Forms::Form { | public: Wiimote^ wm; //Wiimoteオブジェクトwmを作成 public: Form1(void) { | wm = gcnew Wiimote(); //Wiimoteクラスを作成 InitializeComponent(); | //他スレッドからのコントロール呼び出し許可 | Control::CheckForIllegalCrossThreadCalls = false; } protected: /// <summary> <略> #pragma endregion | //接続ボタンが押されたら private: System::Void button1_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->Connect(); //WiiRemoteと接続 | wm->WiimoteChanged += | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); | //レポートタイプの設定 | wm->SetReportType(InputReport::IRExtensionAccel, true); } | //切断ボタンが押されたら private: System::Void button2_Click(System::Object^ sender, \ System::EventArgs^ e) { | wm->WiimoteChanged -= | gcnew System::EventHandler<WiimoteChangedEventArgs^>( | this, &Form1::wm_WiimoteChanged); //イベント関数の登録解除 | wm->Disconnect(); //Wiimote切断 } | //WiiRemoteの値が変化する度に呼ばれる | public: | void wm_WiimoteChanged(Object^ \ sender,WiimoteLib::WiimoteChangedEventArgs^ args){ | WiimoteState^ ws = args->WiimoteState; //WiimoteStateを取得 | DrawForms(ws); | } | public: | void DrawForms(WiimoteState^ ws) { | //グラフィックスを取得 | Graphics^ g = this->pictureBox1->CreateGraphics(); | g->Clear(Color::Black);//画面を黒色にクリア | if (ws->IRState.IRSensors[0].Found) { //赤外線を1つ発見したら | //赤色でマーカを描写 | g->FillEllipse( Brushes::Red , | (float)ws->IRState.IRSensors[0].Position.X * 200.0f , | (float)ws->IRState.IRSensors[0].Position.Y * 200.0f , 10.0f , 10.0f ); | } | if (ws->IRState.IRSensors[1].Found) { //赤外線を2つ発見したら | //青色でマーカを描写 | g->FillEllipse( Brushes::Blue , | (float)ws->IRState.IRSensors[1].Position.X * 200.0f , | (float)ws->IRState.IRSensors[1].Position.Y * 200.0f , 10.0f , 10.0f ); | } | if (ws->IRState.IRSensors[2].Found) { //赤外線を3つ発見したら | //黄色でマーカを描写 | g->FillEllipse( Brushes::Yellow , | (float)ws->IRState.IRSensors[2].Position.X * 200.0f , | (float)ws->IRState.IRSensors[2].Position.Y * 200.0f , 10.0f , 10.0f ); | } | if (ws->IRState.IRSensors[3].Found) { //赤外線を4つ発見したら | //緑色でマーカを描写 | g->FillEllipse( Brushes::Green , | (float)ws->IRState.IRSensors[3].Position.X * 200.0f , | (float)ws->IRState.IRSensors[3].Position.Y * 200.0f , 10.0f , 10.0f ); | } | } }; }
まず赤外線光源を用意して、BluetoothスタックからWiiRemoteを接続してください。Visual Studioから[F5]キーを押してプログラムを実行します。表示されたフォームの[接続]ボタンをクリックして、WiiRemoteをセンサーバーや電球などの赤外線光源に向けてください。
赤外線が検出されると図のようにマーカ点が表示さます。マーカの動きが激しすぎる場合は、WiiRemoteとセンサーバーとの距離を2m〜3mまで離してください。距離が長いほど安定した動きを行うことができます。
なおWii本体付属の標準のセンサーバーには赤外線LEDが左右2グループしかありません(そもそも4点検出できるという機能が存在するところが驚きです!)。複数の赤外線が見つからない場合は、日中(太陽に向けるなど)に窓の外に向けると複数の赤外線を検出できると思います。太陽の光を乱反射している様子などでも複数点を取得できることがありますが、赤外線光源同士が近すぎるとひとつのグループとして誤認識されノイズの原因になりますので、ある程度安定して取得できる条件や距離を調べてみるのもよいでしょう。上の図では、とある店舗の天井に吊られている4個のハロゲンランプの様子です。終了する場合は、切断ボタンを押してから終了させてください。
WiiRemoteの赤外線カメラの値を取得して、赤外線を発見したら画面に描画しています。
[C#] ws.IRState.IRSensors[0].Position.X [C++] ws->IRState.IRSensors[0].Position.X
赤外線の座標(x,y)の位置(Position)は、{0.0〜1.0}の値域をとります。グラフィックスとの組み合わせも意外と簡単だったのではないでしょうか。本プログラムでは、その値にpictureBoxの横幅として設定した200x200に合うように、200を掛けて出力していますが、フォームのSizeを変更したりして、お好みの画面デザインにしてみるとよいでしょう。
なお、WiiRemoteの赤外線センサーは非常に高性能です。ここでは4点の検出を行っていますが、実際に赤外線光源座標が送られてくるスピードは非常に速いことに注目です。通常のビデオカメラ等が1秒に15-30回程度の撮影を行っているのに対し、WiiRemoteは200回程度の座標取得処理を行っているようです。さすがゲーム用入力デバイスです、速度が大切です!
本章では比較的初心者の読者に向けて、Visual Studioを使い、C#とC++という複数の言語環境を使って.NETで開発されたWiimoteLibを通して、基本的なWiiRemoteプログラミングを学びました。WiimoteLibは現状最も完成度の高いAPIのひとつで、非常に安定して動作します。.NETという環境から「C#専用?」と考えられていますが、本書に向けてC++/CLIにおける解説を充実させました(おそらく世界初!です)。
WiimoteLibを使ったサンプル、具体的な開発例は8章でも扱っていきます。
WiiリモコンをFlashとつなげることで、WebサイトのようなコンテンツをWiiリモコンで扱うことができます。この章ではWindows(XP
Professional SP2)とMacintosh(OSX 10.5)【○ページより】に分けてFlashに接続するまでをご紹介します。
-WiiFlashの仕組みWiiFlashを触る前に、WiiFlashの仕組みについて理解しましょう。WiiFlashはWiiリモコンとFlashを通信するための仲介をします。
○章で扱ったように、Bluetoothを利用して、Wiiリモコンとパソコンを繋げました。FlashとWiiはWiiFlashというソフトを利用して、繋げることができます。
WiiFlashとはJoa EbertとThibault Imbertという2人のデヴェロッパにより、WiiリモコンとFlash® applicationsを繋げられるように開発された無料で使えるプロジェクトです。WiiFlashは2つのパートに分けられて提供されています。WiiFlash Server C++もしくは、.NET serverを利用して作られたバイナリソケットサーバーで、Wiiリモコンからの情報をFlashが扱える形に変化して通信します。
WiiFlash ActionScript API (SWC component) WiiFlashサーバーからの通信に対して、プログラムしやすいように提供されているのが、このActionScript(Flashのプログラミング言語) APIになります。このAPIにより、あまり通信をしている事を意識せずに、プログラムができます。
また、WindowsとMacintosh(以下Mac)に対応していますので、より多くの環境で楽しむことができます。ただしWindowsとMacではできる事が違うので注意しましょう。
■環境設定○FlashCS3のインストールまずは一般的なFlashの開発環境であるAdobe Flash CS3(http://www.adobe.com/jp/products/flash/)をパソコンインストールしましょう。ソフトウェアは購入しなくとも、体験版が利用できます。体験版は1ヶ月有効ですので、もし気に入ったら購入しましょう。
-Adobe Flash CS3 Professionalのダウンロード1.Adobeサイトを開きます。(http://www.adobe.com/jp/downloads/) [FlashR CS3 Professional]-[体験版]を選択
※ダウンロードにはAdobe IDが必要になります。
2.インストールダウンロードしたパッケージ(ZIPファイル)を解凍し、付属のインストールガイド(Readme.txt)を参照の上インストールしてください。
3.アプリケーションを起動インストールが完了したらアプリケーションを起動して確かめてください。体験版利用の場合にはソフトウェアのライセンスキー確認画面が出ますが、30日以内でしたら[体験版として使用する]をクリックすれば、通常版と同じ機能が使えます。▲体験版キャプチャ
-.NET Framework 3.0が必要です。※Windowsのみ
WiiFlash Serverは「Microsoft .NET Framework 3.0 Redistributable Package」が必要になります。(http://www.microsoft.com/japan/msdn/windowsvista/general/netfx30.aspx)
-ダウンロード1.WiiFlashサイトを開きます。(http://wiiflash.bytearray.org/)左側のメニューにある[PAGES]-[Download]を選択します。▲サイトキャプチャ
画面から[Download WiiFlash]をクリックし、ファイルをダウンロードします。執筆時のバージョン(○○○○○)▲サイトキャプチャ
○WiiFlashの展開-ファイル内容について・WiiFlash Server (latest version)・WiiFlash API (SWC, sources)・Documentation・CS3 Examples (.fla)
■環境設定【Macintosh】○FlashCS3のインストールWindowsと同じです。
○WiiFlashを利用する-ダウンロード1.WiiFlashサイトを開きます。(http://wiiflash.bytearray.org/)左側のメニューにある[PAGES]-[Download]を選択します。▲サイトキャプチャ
画面から[Download WiiFlash]をクリックし、ファイルをダウンロードします。執筆時のバージョン(○○○○○)▲サイトキャプチャ
○WiiFlashの展開-ファイル内容について・WiiFlash Server (最新バージョン)・WiiFlash API (SWC, sources)・使い方ドキュメント・CS3 Examples (.fla)
○WiiFlashの基本機能
リモコン名系統機能Windows Macintosh
Wiiリモコン【イベント系】接続ができているのか
O O
接続ができたO O
接続が切れたO O
ヌンチャクが接続されているかO O
ヌンチャクが接続されたO O
ヌンチャクが外れたO O
赤外線1点目感知O
赤外線1点目消失O
赤外線2点目感知O
赤外線2点目消失O
【ボタン系】十字キー(上下左右)O O
AボタンO O
BボタンO O
+ボタンO O
−ボタンO O
HOMEボタンO O
?ボタンO O
?ボタンO O
電源ボタンX X
【3軸加速度センサー】X軸の加速度O O
Y軸の加速度O O
Z軸の加速度O O
Yaw値O O
Pitch値O O
Roll値O O
【赤外線-IR】1点目O
2点目O
1点目のX値O
1点目のY値O
2点目のX値O
2点目のY値O
【その他】バッテリーレベルO O
バイブレーションO Oヌンチャク【ボタン系】StickX O O
StickY O O
CボタンO O
ZボタンO O
【3軸加速度センサー】X軸の加速度O O
Y軸の加速度O O
Z軸の加速度O O
Yaw値O O
Pitch値O O
Roll値O O
バランスボード左上の圧力
右上の圧力
左下の圧力
右上の圧力
○WiiFlashのデモファイルでテストする-WiiリモコンでテストするまずはWiiリモコンでテストしましょう。Wiimote Demo.fla Wiiリモコンをパソコンに繋げて、WiiFlashサーバーを立ち上げましょう。すると下記画面のように「1 Wiimote(s) found」と表示されれば繋がっているので、▲WiiFlash Serverキャプチャ(Windows)
WiiFlash Serverキャプチャ(Mac)
ダウンロードしたパッケージの中にある[cs3-examples]-[Wiimote Demo.fla]を開きましょう。このファイルでは、電源ボタンを除く、Wiiリモコンのボタン全てと「Sensor X」「Sensor Y」「Pitch」「Roll」「Yaw」と「Battery level」が確認できます。ボタンを押して動作を確認しましょう。
確認ができたら、今度はファイルの構成を見てみましょう。
新規でWiiFlashを使用したアプリケーションを作る際には、必ずAPIが入っている[org]フォルダをflaファイルと同じディレクトリに入れて作成を開始しましょう。また、ライブラリの[synchronize]というムービークリップも必要となりますので忘れずにコピーしましょう。
○プログラムの解説このサンプルファイルのスクリプトについて重要な部分のみを解説しましょう。
WiiFlashのライブラリをインポートする。import org.wiiflash.Wiimote; import org.wiiflash.events.ButtonEvent; import org.wiiflash.events.WiimoteEvent; import flash.events.*;
Wiiリモコンをインスタンス化して、使えるようにします。var myWiimote:Wiimote = new Wiimote();
WiiFlash Serverに接続します。myWiimote.connect ();
エラーや接続状況などを管理する[synchronize]というムービークリップをインスタンス化し、画面に配置します。var mySynchronize:Synchronize = new Synchronize(); addChild( mySynchronize ); mySynchronize.x = (stage.stageWidth - mySynchronize.width) / 2; mySynchronize.y = (stage.stageHeight - mySynchronize.height) / 2
エラーなどのイベントハンドラを登録します。myWiimote.addEventListener( Event.CONNECT, onWiimoteConnect ); myWiimote.addEventListener( IOErrorEvent.IO_ERROR, onWiimoteConnectError ); myWiimote.addEventListener( Event.CLOSE, onCloseConnection );
○動作のテスト▲サンプルキャプチャ【ボタン】ボタンを使うにはまず、イベントの登録をします。試しに、Aボタンの押すと画面の色が変わるサンプルを作ってみましょう。ボタンのイベント登録は基本的に[on]と[off]を設定します。[ButtonEvent.A_PRESS]はAボタンが押された際にイベントを発動します。[ButtonEvent.A_RELEASE]はボタンが離された際にイベントを発動します。myWiimote.addEventListener( ButtonEvent.A_PRESS, onAPressed ); myWiimote.addEventListener( ButtonEvent.A_RELEASE, onAReleased);
Aボタンが押された際にイベントが発動して、呼ばれるメソッドがこちらです。画面に配置された[color_mc]のフレームを2フレーム目に変更します。それにより、背景が赤くなります。function onAPressed ( pEvt:ButtonEvent ):void { color_mc.gotoAndStop(2); }
Aボタンが離された際にイベントが発動して、呼ばれるメソッドがこちらです。画面に配置された[color_mc]のフレームを元の1フレーム目に変更します。それにより、背景が白くなります。function onAReleased ( pEvt:ButtonEvent ):void { color_mc.gotoAndStop(1); }
このようなスクリプトは予め用意してあるので、このメソッドのなかみを変更するだけで簡単にWiiリモコンを使ったFlashのアプリケーションが作れます。
【3軸方向加速度センサー】▲サンプルキャプチャ次に値が常に変化する3軸方向加速度センサーを例に、UPDATEイベントを解説しましょう。[WiimoteEvent.UPDATE]は、3軸方向加速度センサーや、赤外線の位置など常に値が変化するものを取得するために作られています。このUPDATE関数は一定間隔で更新されるので、常に値の変化を監視する際に使います。
-イベント登録まずは、このUPDATE関数が使えるように、イベントの登録をします。myWiimote.addEventListener( WiimoteEvent.UPDATE, onUpdated );
-メソッド定義画面上の[sensor_txt]にsensorXとsensorYの値を表示します。このメソッドの中に書かれたものは一定間隔で更新されます。
function onUpdated ( pEvt:WiimoteEvent ):void{ sensor_txt.htmlText += "Sensor X : " + String ( pEvt.target.sensorX ) + \ "<br>Sensor Y : " + String ( pEvt.target.sensorY ); }
この章では木村秀敬さんのご協力により、WiiFlashをFlashやActionScript環境ではなく、Processingを使って利用する方法を解説してしていきます。
Processingは、マサチューセッツ工科大学のBen FryとCasey Reasによって開発されたフリーのプログラム開発環境です。映像を作り出す、インタラクティブな作品を作るといった用途に向いており、アーティストやデザイナーでも使いやすいように作られています。
【Processing】http://processing.org/
ベースはJava言語なので、Linux,Mac OS X,Windowsと多様な環境で動作します。Javaで開発というと、JDKをインストールしたり、コマンドラインを使った操作をしたりといったことが必要ではと思われてしまうかもしれませんが、Processingはそこの敷居を下げるための工夫がなされています。そのため、Javaはもとより、今までプログラミング言語を使ったことが無いという方にも向いています。
また、プログラムについても「スケッチ」という呼び方を用い,サッと書いてすぐに実行できる点を強調しています。Java言語の入門にありがちなおまじない的なプログラムは不要で、いきなり主目的となる部分から書き始めることができます。このあたりは、JavaというよりもPerlやPythonなどの軽量言語のイメージです。
このように簡単に使えるProcessingですが、低機能かというとそういう事はありません。カメラからの動画入力や、ネットワークとの通信、OpenGLを使った3Dプログラミングなど、たくさんの機能がライブラリによってサポートされています。WiiRemoteも例外ではありません。ProcessingからWiiRemoteを扱うためのライブラリとして、Wrj4P5というライブラリが用意されています。
【Wrj4P5】http://sourceforge.jp/projects/wrj4p5/wiki/FrontPage
しかしこのライブラリを使うために必要なBluetoothスタックなどがかなり限定されており、必ずしも全ての環境で動くとは限りません。特にWindows環境では動かないことが多いようです。
そこでWiiFlashの登場です。前章で紹介されている通り、WiiFlashはもともとFlashからWiiRemoteを扱うためのツール、ライブラリですが、実は他のプログラミング言語からも使うことが出来ます。一度仕組みさえ知ってしまえば、WiiFlashはWiiRemoteに対する汎用的なインタフェースとして機能します。
Processingも例外ではなく、netライブラリを使うことでWiiFlash経由でWiiRemoteを扱うことができるようになります。筆者の感覚では、WiiFlashのほうがWrj4P5よりも多くの環境で動作しているようなので、WiiFlashを使えばより多くの環境でProcessingからWiiRemoteを扱うことができるのではないかと思います。
ProcessingからWiiFlashを経由してWiiRemoteにつなぐために必要な環境は以下のとおりです。
ここでは、Processingのセットアップ方法と、WiiRemoteを扱うための方法について説明します。WiiFlashについては前章を参考にセットアップしてください。また、Processingそのものについての詳細な説明についても割愛します。
Processingのセットアップはとても簡単です。Processingのサイトから圧縮ファイルをダウンロードしてきて解凍するだけです。特にインストーラを使ってインストールするなどの作業は必要ありません。ダウンロードは以下のURLから行うことができます。http://processing.org/download/index.html
ここから、お使いのOSに合ったファイルをダウンロードしてください。Windowsについては注意書きにも書かれているとおり、特に理由が無ければ「Without Java」ではないほうをお勧めします。
なお、執筆時点での最新バージョンは11月29日公開の1.0.1ですので、以後の説明はこの1.0.1を対象とします。皆さんがこの文章を読まれているときの最新バージョンはすでに1.0.1ではなくなっているかもしれませんがご了承ください。
ダウンロードした圧縮ファイルを展開すると、以下のようなファイルが現れます。
あとはProcessing.exeを起動するだけです。起動中のスプラッシュウィンドウが表示された後、以下のような画面になれば成功です。
まずはWiiFlashを使わない、Processingそのもののサンプルを動かしてみましょう。試しにProcessingに付属しているMouse1Dというサンプルを動かすことにします。メニューから[File]の[Examples]を選択すると、とても多くのサンプルスケッチが用意されていることが分かります。この中から[Basics] - [Input] - [Mouse1D]を選択します。すると、50行ちょっとのスケッチが開かれます。
中身の理解は置いておいて、まずは実行してみましょう。左上のRunボタンを押すと、スケッチが実行されます。小さなウィンドウに2つの正方形が現れたでしょうか。マウスカーソルを左右に動かすと、それに反応して色やサイズが変化します。
一通りこのサンプルがどのような動作をするか理解したら、ウィンドウを閉じてスケッチのほうに目を向けてみます。Java言語やそれに近い言語を使ったことがある方なら、このスケッチがsetupメソッド、drawメソッド、updateメソッドの3つで構成されていることが分かるかと思います。
setupメソッドでは、ウィンドウサイズの設定などの初期化を行っています。drawメソッドでは、背景を塗りつぶし、マウスの位置に応じて2つの正方形を描く処理を行っています。updateメソッドでは、正方形の色や座標計算に使うパラメータを計算しています。
updateメソッド内の詳細な計算については特に理解しなくてもいいのですが、この引数にmouseXという変数を指定しているところに注目してください。mouseXとは、名前のとおり現在のマウスカーソルのX座標です。カーソル座標をupdateメソッドに渡すことによって、leftColor、rightColor、gx、gyといった変数に適切な値が設定されます。そしてそれらの値を使って正方形が描かれます。
さて、このサンプルではマウスカーソルのX座標が使われていますが、これをWiiRemoteを使って操作できないものでしょうか。ここからは、WiiFlashを使ってそれを実現する方法について説明します。
サンプルスケッチのMouse1DWiiを開いてみてください。これを動かすには、まずWiiFlashを起動する必要があります。前章を参考に、BluetoothでWiiRemoteを認識させ、WiiFlashを起動してください。正しくWiiFlashが起動したら、ProcessingのRunボタンを押してください。表示される画面はまったくMouse1Dと変わりませんが、WiiRemoteをひねるように回転させると、マウスカーソルを動かしたときのように画面が変化します。
それではスケッチについて詳しく見ていきましょう。スケッチには、以下のようにMouse1DWiiとWiimoteの2つのタブがあります。
Wiimoteのほうについては後で詳しく説明するので、まずはMouse1DWiiのほうに着目してください。
Mouse1DWiiは、元となっているMouse1Dを少し書き換えたものです。以下のように4行の追加、変更があります。
// 前略 float leftColor = 0.0; float rightColor = 0.0; Wiimote wiimote; ..... (1) WiiRemoteを使うための変数 void setup(){ size(200, 200); colorMode(RGB, 1.0); noStroke(); wiimote = new Wiimote(this); ..... (2) 初期化 } void draw() { wiimote.update(); ..... (3) WiiRemoteからの入力を取り込む background(0.0); update((int)((wiimote.x + 1) * width / 2)); ..... (4) 加速度センサの値を使う // 後略
まず最初に、WiiFlash経由でWiiRemoteを使うため、Wiimote型のwiimoteという変数を定義しています(1)。setupメソッドでは、この変数を初期化しています(2)。
drawメソッドではまず、WiiRemoteからの入力を取り込むために、Wiimoteクラスのupdateメソッドを呼び出します(3)。updateメソッドの詳細にはここでは踏み込みませんが、これによって現在のWiiRemoteの加速度などのパラメータが使えるようになります。例えばWiiRemoteを正面に向けたときにひねる方向(X方向)の加速度は、wiimote.xという変数に入っています。
最後は加速度を描画する正方形のサイズに反映させる部分です(4)。元のMouse1Dでは、updateメソッドにマウスカーソルのX座標(mouseX)を渡していました。Mouse1DWiiでは、これにならう形でWiiRemoteのX方向の加速度を変換し、updateメソッドに渡しています。
X方向の加速度を表すwiimote.xは、完全に左に傾けたときに-1、逆に完全に右に傾けたときに1という値になります。勢いがついていたりするとこの範囲を超えますが、基本的には-1から1の範囲と考えて問題ありません。スケッチに書いたとおり、1を足して画面幅(width)をかけ、2で割れば、-1から1の範囲を0から画面幅の範囲に変換できます。
WiiRemoteをProcessingから使うためには、Wiimoteクラスを使います。Wiimoteクラスは、元のWiiFlashの実装を参考に筆者がオリジナルで作成したもので、動作については無保証とします。このWiimoteを使うためのステップは大きく分けて3つです。
newでWiimoteのコンストラクタを呼び出し、Wiimote型のインスタンスを作ります。このとき、内部ではWiiFlashとの接続が行われるので、WiiFlashが起動していないとここでエラーが発生し、以降の処理がうまくいきません。この処理は基本的に1度だけ呼べばいいので、setupメソッドの中で呼び出すのがよいでしょう。
Wiimote wiimote; // 変数の宣言 wiimote = new Wiimote(this); // setupメソッド内などで
Wiimoteのupdateメソッドを呼び出し、WiiRemoteから、WiiFlash経由でデータを取得します。この処理は一般的にはdrawメソッドの中で一度だけ行います。
// drawメソッド内などで wiimote.update();
一度updateメソッドを呼んでから次にupdateメソッドが呼び出されるまでは、WiiRemoteクラスから得られるWiiRemoteの状態は変わらないままです。
Wiimoteのxやyなどのフィールドを参照することで、現在のWiiRemoteの状態を知ることができます。主に用いられるのは、各方向の加速度と、ボタンの状態でしょう。
// (100, 100)の点からWiiRemoteの向きに応じて線を引く line(100, 100, 100 + wiimote.x * 50, 100 + wiimote.y * 50);
Wiimoteクラスを使って得られるデータは表の通りです。
フィールド名 | 型 | 意味 |
---|---|---|
x,y,z | float | 各軸加速度。-1〜+1の値をとり、1Gの時に値が1となる。 |
one,two,a,b,plus,minus,home,up,down,right,left | Button | それぞれのボタンの状態 |
batteryLevel | float | バッテリーの残量 |
extensionType | int | 拡張コントローラのタイプ |
ボタン関連のフィールドは、Buttonクラスを使って表されています。Buttonクラスには、pressedとpushedの2つのboolean変数があります。
これら2つの変数タイプをうまく使い分けてください。
// 上下ボタンが押されている間パラメータを上下させる if (wiimote.up.pressed) { someparam++; } else if (wiimote.down.pressed) { someparam--; } if (wiimote.a.pushed) { background(10); // Aボタンが押されたら画面を消去 }
WiiRemoteを応用したスケッチをつくるにあたって、1からProcessingのスケッチを組んでそれをWiiRemote対応にすると、Wiimoteを使うという点からフォーカスがずれてしまいます。幸い、Processingには多数のサンプルスケッチが添付されていますので、これらのうちいくつかをWiiRemote対応にすることで解説していきます。
ここでは、FireCube、Directional、LightsGL、SineWaveSignalの4つのスケッチをWiiRemoteに対応させた例について紹介します。WiiRemoteに対応させたスケッチは、元のサンプルスケッチと区別するために、名前の最後にWiiを付けています。
FireCubeは、炎が燃え上がるような複雑なエフェクトが100行程度で書かれたものです。このサンプルを、WiiRemoteを振れば振るほど炎が出てくるようにしてみました。サンプルはFireCubeWiiです。
drawの中では、加速度からpowerという値を計算しています。この値に応じて炎の出る量が変わります。
float power = sqrt(wiimote.x * wiimote.x + wiimote.y * wiimote.y + wiimote.z * wiimote.z); power = constrain(power - 1, 0.1, 1);
最初に計算しているのはWiiRemoteにかかっている加速度です。WiiRemoteが動かない状態では、この値が大体1になります。この状態では炎を出したくないので、次の行で1を引くとともに、constrainという関数を使って値を0.1から1の間に収めています。
ここで計算したpowerは、立方体と、下から上がってくる炎の初期値に設定します。詳しくはスケッチ中のpowerという変数を検索してみてください。
Directionalは、マウスカーソルの位置に応じてピンポン球のようなものがライトアップされるものです。これにWiiFlashから得られるX方向、Y方向の加速度を使うことで、あたかもWiiRemoteが懐中電灯になったかのような感覚が味わえます。
このスケッチでは、懐中電灯のような感覚を出すために、WiiRemoteのAボタンが押されているときだけ球が表示されるようにしています。下記のように、Aボタンが押されていればdrawメソッドから途中で抜け出します。
if (!wiimote.a.pressed) { return; }
光を当てる方向については、加速度の値をそのまま使っています。
directionalLight(204, 204, 204, wiimote.x, wiimote.y, -1);
最後は1つ変わったネタを取り上げます。WaveSignalWiiは今までのスケッチとは異なり、音を使ったスケッチです。元となっているスケッチは[Libaries] - [Minim (Sound)] - [SineWaveSignal]です。このスケッチを起動すると、プーというような音が鳴り出します。これは画面にも表示されている通り、いわゆる正弦波です。マウスカーソルを上下左右に動かすことで、音の高さ(ピッチ)が上下したり、ステレオの左右のバランス(パン)が移動したりするのが分かるでしょうか。
Processingでこのように音が鳴らせるのは、Minimというライブラリのおかげです。正弦波を表す変数を作り、ピッチやパンを指定するだけで、このように音を鳴らすことができます。
SineWaveSignalはその名の通り正弦波にしか対応していないのですが、これをWiiRemote対応させたWaveSignalWiiは、のこぎり波や矩形波にも対応させました。左右ボタンを押すことでこれらを切り替えることができます。また、上下ボタンで音量を変えることもできます。
また、向きを変えることでピッチやパンも変化します。上に向けると音が高く、下に向けると音が低くなります。左右方向に向けると、そちらの方から音が聞こえてくるようになります。
さて、ここまで特にこの話の詳細には踏み込んできませんでしたが、なぜProcessingからWiiFlashを使うことができるのでしょうか。答えはWiiFlashに添付されているソースコードの中にあります。
メインとなるソースコードは、Core/api/source-classes/org/wiiflashディレクトリの中にあります。ここにあるのはWiiFlashそのもののソースコードではなく、FlashからWiiFlashにつなぐためのActionScript3のソースコードですが、これだけでWiiFlashの挙動を推測することができます。Wiimote.asとWiiSocket.asを見れば、基本的な挙動をつかむことができます。
WiiSocket.asでは、connectメソッドでlocalhostの19028番に接続しています。このことから、WiiFlashが19028番のポートを使ってサーバを立てていることが分かります。そしてソケットからデータを受信したときの処理はonSocketDataメソッドに書かれています。ここを見ると、データが80バイト単位で受信されていることが分かります。最初の1バイトはコントローラのIDとなっており、それ以降のデータはWiimoteクラスのupdateメソッドで読み込まれています。WiiFlashから送られてくる基本的なデータをまとめると、表のようになります。
基本データレイアウト
名前 | 位置 | 型 | 意味 |
index | 0 | byte | コントローラのID |
batteryLevel | 1 | byte | バッテリー残量 |
buttonState | 2 | ushort | ボタンの状態 |
x | 4 | float | X方向の加速度 |
y | 8 | float | Y方向の加速度 |
z | 12 | float | Z方向の加速度 |
extensionType | 16 | byte | 拡張タイプ |
拡張タイプには、ヌンチャク、クラシックコントローラ、バランスボードがあります。このタイプによって、17バイト目以降のデータの解釈方法が変わります。これらについてはNunchuk.as、ClassicController.as、BalanceBoard.asを見るとデータの内容が分かりますが、本書ではそれらについての説明は割愛します。
ProcessingからWiiFlashにつなぐためには、netライブラリのClientクラスを使います。Clientクラスを使うことで、ソケットを使ってWiiFlashと通信することができます。つないだ後は、readメソッドなどを使うことでWiiFlashからバイト列を読み込むことができます。
Wiimoteクラスの詳細な実装については本章では述べませんが、ソースコードは見られる状態となっているので、気になる方はそちらを参照してください。
WiiYourself!はgl.tter氏による、非常に多機能なNative C++用APIです。第4章で紹介したWiimoteLibによる.NET環境での高機能・平易なプログラミングと異なり、古き良きC/C++言語による高速で直接的なプログラミングが行えることが魅力です。
本章ではWiiYourself!をC++によるコマンドラインプログラミング環境で使ってみることを通して、インタラクション技術の基盤となる技術を学びます。
http://wiiyourself.gl.tter.org/
WiiYourself!のホームページにはWiiYourself!を用いたゲームや、3DCGソフトMayaの操作、空撮カメラの制御などいくつかのプロジェクトが紹介されています。gl.tter氏は実際にFPS(一人称シューティング)ゲーム「GUN FRENZY!2」を制作するためにこのライブラリを作成しているようです。以下は、WiiYourself!のホームページに記載されている機能一覧です。
ホームページを読んでみると、WiimoteLibのもととなったBrian Peek氏のプロジェクト「Managed Wiimote Library」と原点を同じくしていることがわかります。WiiYourself!が他のAPIと比較した上での特徴として挙げられるのは、ネイティブC++の静的ライブラリ(.lib)であり、DLL等が不要であること、DirectXなどによる旧来のゲームプログラミング手法に親和性があること、実験的ながらスピーカーへのWAV出力や4点の赤外線の検出など常にアップデートを続けている点が挙げられるでしょう。北米・欧州の多くの開発者が利用しています。APIコアの開発はgl.tter氏が集約的に行っていますが、メーリングリストでのディスカッションが比較的活発で、初心者から研究者まで様々な人が利用しています。購読しているだけでも世界中のWiiRemote利用者が何を考えて、どんなトレンドにあるのかが見えて楽しいです。今後もいろいろな発展が期待できるプロジェクトでしょう。
WiiYourself!原稿執筆時点の最新版は2008年7月24日に公開された「v1.01a」です。なお公式メーリングリストでは次期バージョンにあたる「v1.11beta」が準備されていますが、大きな変更はないので本書ではメジャーバージョンである「v1.01a」で解説します。WiiYourself!はZIPファイルのダウンロードで入手することができます。
http://wiiyourself.gl.tter.org/WiiYourself!_1.01a.zip
このZIPファイルの中に、WiiYourself!のソースコード、静的リンク用ライブラリファイル、デモプログラム、それらをビルドするためのプロジェクトファイル、唯一のマニュアルに当たるREADMEファイル、ライセンスファイルなどが含まれています。
インストールとしては、どこにファイルを配置してもよいのですが、本書では解説のためにZIPファイルから解凍した「WiiYourself!」フォルダを「C:\WiiRemote\WiiYourself!」というパスに配置することにします。
なお、WiiYourself!はその名前も個性的ですが、かなり個性的なREADMEとライセンスを持っています。以下、参考訳を掲載します。商用利用可能ということで、個人でシェアウェア作家などをやっていらっしゃる方は嬉しいのではないでしょうか。
- WiiYourself! - native C++ Wiimote library v1.01 (c) gl.tter 2007-8 - http://gl.tter.org
これは完全に無料で完全機能の(現在は)Windows用のWiiRemoteネイティブC++ライブラリです。Brian Peek氏の「Managed Wiimote Library」(http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx)をもとに、完全に書き直し、拡張しました。
いまのところドキュメンテーションはありません。「WindowsでWiiRemote」の全容と一般的な情報についてはBrianの書き込みをチェックしてください。ソースコードは広範囲にわたるコメントがあり、デモアプリは全てを理解する上で助けになるでしょう(難しくはないです)。質問については私のメーリングリストに参加してください。いくつかの使用における制限については「License.txt」を参照してください。
【付記】
・VC 2005 C++のプロジェクトが含まれています(VC 2008に読み込ませてください)。リンクエラーを防ぐために、プロジェクトのプロパティ→「C/C++」→「コード生成」で「ランタイムライブラリ」の設定を適応させる必要があります。
・MinGW環境のためのMSYS makefileが含まれています。MSYSプロンプトにおいて、「make -f Makefile.MSYS」と入力してください。MinGWという名前のフォルダと適切なフォルダ構造によるバイナリを生成します。
・ビルドにはマイクロソフトのDriver Development Kit(DDK)が必要となります(HID APIのため)。登録の必要なし、無料でダウンロードできます。
http://www.microsoft.com/whdc/devtools/ddk/default.mspx
インクルードパスにDDKの「inc/wxp」を追加し、ライブラリパスに「lib/wxp/i386」を追加してください。(利点はないと思いますが)より最近のヘッダファイル、APIを含むWinDDKを使うこともできます。
・ライブラリはtchar.hでUnicode化可能です(VCプロパティのビルドオプション「U」をつけてあります)。
・VCを使っていないなら、以下のライブラリをリンクする必要があります。「setupapi.lib」、「winmm.lib」、「hid.lib(DDKから入手)」。
【WiiRemoteインストールに関する付記】
WiiRemoteは使用したいPCに事前に「paired」の状態、つまりBluetooth接続された状態にある必要があります。[1]+[2]ボタンを同時に押しておくことで、数秒間、発見可能(discoverable)モードに入ります(LEDが点滅します、LEDの数はバッテリーレベルに依存)。「Nintendo RVL-CNT-01」として発見されます。
■スタック特有の解説:
<本書では既に3章で解説済みなので割愛します>
・切断方法(各スタック共通)
WiiRemoteのPOWERボタンを数秒押してください。これで自動的に切断できます。([1]+[2]ボタンを押して)再度ペアリングモードに入って、LEDが数秒点滅している状態でタイムアウトさせれば、効果的に電源を切ることができます。
メーリングリストにサインアップして、フィードバックを与え、アイディアを交換し、参加するというループに入ってください。
http://wiiyourself.gl.tter.org/todo.htm
もし貴方がWiiYourself!を使っているなら、是非教えてください。リンクを張らせていただきたいと思います。楽しんで!
gl.tter (glATr-i-lDOTnet)
- WiiYourself! - native C++ Wiimote library v1.01 (c) gl.tter 2007-8 - http://gl.tter.org
■ライセンス:私のWiiRemoteライブラリはいかなる利用(商用含む)に関しても、以下の条件において無料です。
(1)直接、間接にかかわらず人を傷つけるために使わないでください。軍事利用を含みますがそれに限ったことではありません(エゴを叩くのはいいことです・笑)。
(2)バイナリ形式のいかなる配布(例:あなたのプログラムにリンクされたもの)においても、以下のテキストをReadMeファイル、ヘルプファイル、AboutBoxやスプラッシュスクリーンに含めてください。
contains WiiYourself! wiimote code by gl.tter
http://gl.tter.org
(3)ソースコード形式のどんな配布形式でも、オリジナルの私の著作権表示を変更しないで保持すること(あなたが加えた変更は追加できます)、そしてこのライセンス文を含めてください(このファイルをあなたの配布物に含むか、あなた自身のライセンス文に貼り付けてください)。
(4)あなた自身がかなり書き直さない限り、このコードに競合するライブラリを生み出す行為に使わないでください(例:他の言語にコンバートするなど、まず相談してください)。
その代わり、後に新機能、バグフィックスやアイデアを提供してください。
gl.tter (http://gl.tter.org | glATr-i-lDOTnet)
さて、ライセンスなどを理解したら、まずはDemoフォルダにある「Demo.exe」を起動してみましょう。事前にWiiRemoteに接続しておくのを忘れずに(詳細は第3章で解説)。
一見、地味なデモに見えますが、実は非常に多機能です。特に[A],[1],[2]ボタンを押すことでWiiRemoteのスピーカーから音が出ることを確認してください。高音質ではありませんが、音声が再生されています。[A]で矩形波(くけいは;square)、[1]でサイン波、[2]で音声のような物(DAISY)、[B]でバイブレーター駆動です。その他、各ボタンのステータスと加速度の表示、LEDのナイトライダー的アニメーション、バッテリー残量、4点の赤外線(サイズ測定付き)、拡張端子へのヌンチャクの挿抜が「Extnsn.」に表示されています。面白いのは「Orient:」の行に「Pitch, Roll」といった姿勢推定に加えて「UpdateAge」として、測定頻度の計測があることです。シンプルですが非常によくできたデモです。また多くの情報がこのソースコードであるDemo.cppに記載されていますので余裕がある人は、解読してみるとよいのですが、まずは次の章に進みWiiYourselfのリビルドを行い、実行して、動作を見ながら自分のものにしていくことにしましょう。
WiiYourself!付属の「Demo.exe」について、一通りの動作を試したら、次はリビルドです。ここではVisual C ++ 2008 Expressなど無料で入手できる環境でWiiYourself!をリビルドできるよう、順を追って解説します。
本書の読者のほとんどは、既に第4章でVisual C++ 2008もしくはVisual Studio 2008(以下VC 2008)のセットアップをすませていることでしょう。次にリビルドに必要になる、Driver Development Kit (DDK)もしくはWindows Driver Kit (WDK)のセットアップを行います。DDKはその名が示すとおり、ドライバ開発のためのキットであり、WindowsのOS内部とユーザのアプリケーションプログラムの中間に位置するドライバプログラムを開発しやすくするためのヘッダやライブラリが含まれています。WiiRemoteを使ったプログラミングでは主にBluetooth経由のHID(Human Interface Device)との通信のためにこのヘッダやライブラリを必要とします。
WDKはDDKの後継で、DDKやWindowsドライバーの安定性や信頼性をチェックするためのテストを含む「統合されたドライバー開発システム」です。現在のところWiiRemote関係において、WDKを利用するかDDKを利用するか、内容的な大きな差は見あたりません。またWDKの入手にはMSDNサブスクリプションへの契約か、Microsoft Connectへの参加が必要になりますので、本書では「誰でも簡単に入手できる」という視点でDDKを中心に解説します。
なお、WiiRemoteプログラミングを行う上で、DDKのすべてのファイルが必要になるわけではありません。実際に必要になるヘッダファイルとライブラリファイル6つが揃ってしまえば、あとは(ライセンス上問題がなければ)ファイルコピーでも全く問題ありません。
それでは、DDKのインストールを始めましょう。まずマイクロソフトのDDKのホームページを訪問し、「Windows Server 2003 SP1 DDK」のISOファイルを入手します。
http://www.microsoft.com/japan/whdc/DevTools/ddk/default.mspx
「Windows Server 2003 SP1 DDK」とありますが、WindowsXP等でも利用できます。「Windows XP SP1 DDK」以前のDDK(NT,98,2000など)は既にサポートが終了していますので、WDKなどできるだけ新しい物を利用した方が良いでしょう。現状の主力OSである、Windows Vista、Windows Server 2003、Windows XP、そしてWindows 2000上で動作するドライバをビルドするには、最低でもこのWindows Server 2003 SP1 DDKに含まれる「Windows 2000向けビルド環境」を使用してください。
まず、ダウンロードしたISOファイルを使ってCD-ROMを作成します。ただしこのCD-ROMは一度しか使いません。ISOファイルをドライブとしてマウントできる仮想CDのようなソフトウェアがあればそちらを利用してもかまいませんし、最近では「7-zip」というオープンソースのソフトウェアを使ってISOファイルを直接展開することができます。
http://sevenzip.sourceforge.jp/
作成したCD-ROM、もしくは展開したフォルダから「setup.exe」を実行します。
エンドユーザライセンス承諾書(EULA)を確認します。
インストール先は本書ではデフォルトの「C:\WINDDK\3790.1830」とします。
ここでインストールするファイルを選択します。デフォルトのまま、もしくはすべてを選択しても良いのですが、ヘッダファイルのような小さなテキストファイルが700MB以上ありますので、環境によってはインストールに軽く1時間ぐらいかかってしまいます。
大量のファイルをインストールすることに特に抵抗がない方はすべてにチェックを入れても良いでしょう。余計なファイルは必要ない、という方は最低限「Build Environment」の該当するプラットフォームにチェックが入っていればよいでしょう(Windows XP Headers, X86 Libraries)。具体的には「C:\WINDDK\3790.1830\inc\wxp」にある「hidpi.h, hidsdi.h, hidusage.h, setupapi.h」というヘッダファイルと、「C:\WINDDK\3790.1830\lib\wxp\i386\」にある「hid.lib, setupapi.lib」というライブラリファイルが必要です。その他のファイル、ツール類はインストールして試してみても良いですが、本書では扱いません。
DDKのインストールが終わったら、さっそく次の章でWiiYourself!のリビルドを通してライブラリの設定を行います。
本書執筆現在、gl.tter氏は最新版WiiYourself!の公開を準備しています。WiiBoardやWiiMotionPlusのサポートを行った魅力あるバージョン(1.12beta以降)ですが、フォルダ構成が本書で紹介しているバージョンとは若干異なるようです。
本書の読者向けの大きな変更点としては、WiiYourself!ライブラリ自身のプロジェクトが無くなったことです。
デモのリビルドを行うときは、Demo.slnを直接開いて、DDKファイルのインクルードとライブラリの設定を行ってください。
VCに知識のある方であればそれほど難しい作業ではありませんが、最初のリビルドを円滑に進めるために以下の手順に従ってください。まずVC2008を先に立ち上げて、「ファイル」→「開く」→「プロジェクト/ソリューション」から、「C:\WiiRemote\WiiYourself!」フォルダにある「WiiYourself!.sln」というVC2005(VC8)のソリューションファイルを開きます。「Visual Studio変換ウィザード」が起動しますので、VC2008(VC9)用のプロジェクトに変換してください。問題なく変換は終了するはずです。「Ctrl+Shift+B」でリビルドしてみてください。
1>------ ビルド開始: プロジェクト: WiiYourself! lib, 構成: Debug Win32 ------ 1>コンパイルしています... 1>wiimote.cpp 1>c:\wiiremote\wiiyourself1\wiimote.cpp(41) : fatal error C1083: include ファイルを開けません。 'hidsdi.h': No such file or directory
このようにエラー「C1083」が出て、「hidsdi.h」のインクルードが要求されれば正常です。次にDDKのディレクトリを設定します。
ソリューションエクスプローラーの「WiiYourself! lib」のアイコンの上で右クリックしてプロジェクトのプロパティページを開いてください。まず「構成」を「すべての構成」とし、「構成のプロパティ」→「C/C++」→「全般」の「追加のインクルードディレクトリ」に「C:\WINDDK\3790.1830\inc\wxp」を設定、続いて、「ライブラリアン」→「全般」の「追加のライブラリディレクトリ」に「C:\WINDDK\3790.1830\lib\wxp\i386」を設定します。
設定が終わったら「Ctrl+Alt+F7」でリビルド(一旦クリーンな状態にしてからビルド)を行います。1件、Unicodeに関する警告(C4819)が出ますが、無視してかまいません。問題なく、
1>ライブラリを作成しています... 1>ビルドログは "file://c:\WiiRemote\WiiYourself! \Debug\BuildLog.htm" に保存されました。 1>WiiYourself! lib - エラー 0、警告 1 ========== すべてリビルド: 1 正常終了、0 失敗、0 スキップ ==========
このように表示されればライブラリのリビルドは成功です。次のステップに進んでください。再度「ファイル」→「開く」→「プロジェクト/ソリューション」から、今度は「C:\WiiRemote\WiiYourself!\Demo」フォルダにある「Demo.sln」というVC2005(VC8)のソリューションファイルを開きます。これは同梱のデモアプリケーションです。同様に「Visual Studio変換ウィザード」が起動しますので、VC2008用のプロジェクトに変換してください。いきなり「完了」を選んでも問題なく変換は終了するはずです。変換後、「Ctrl+Alt+F7」でリビルドしてみてください。
2>Demo.cpp 2>リンクしています... 2>LINK : fatal error LNK1104: ファイル 'hid.lib' を開くことができません。
最後に、「Alt+F7」でプロジェクト(この場合「Demo」)のプロパティを開き、「全ての構成」の「構成プロパティ」→「リンカ」→「全般」→「追加のライブラリディレクトリ」にDDKのライブラリパスである「C:\WINDDK\3790.1830\lib\wxp\i386」を設定してください。これで特に大きなエラーも出ずにリビルドに成功するはずです。
WiiRemoteとのBluetooth接続を行ってから、「F5」キーでデバッグ開始(実行)です。無事にデモプログラムが実行できましたでしょうか?以上の流れに沿えば簡単なのですが、手順を間違える、例えば先にDemo.slnの変換を行ってしまうと、VC2005のプロジェクトの変換を通してWiiYourself!を参照するライブラリ名が変わってしまったりして、意図せず時間がかかってしまいますので注意を。さて、これでgl.tter氏のデモソースコード「demo.cpp」を改変してWiiYourself!を学ぶ環境が整いました。コマンドラインプログラムやC/C++に詳しい方は、このままソースコードを掘り下げていくことができると思います。
前節で[Demo.sln]のリビルドに成功しましたので、その続きからはじめましょう。このソリューションは「Demo」というアプリケーションのプロジェクトと、「WiiYourself! lib」というライブラリ部分の2つのプロジェクトから構成されています。
「Demo」には、コマンドラインサンプルアプリケーションの本体である「Demo.cpp」と「Demo.h」のソースコード、それからwav,rawファイル形式によるスピーカー再生のための音声ファイルが置かれています。
ライブラリ部分は「C:\WiiRemote\WiiYourself!\Demo\lib」というディレクトリ(※今後リリースされる予定のv1.11Betaでは若干フォルダ構成が変わる可能性があります)に各々プロジェクトの構成により「Release」もしくは「Debug」そして、Unicode対応と非対応のライブラリを生成します。WiiYourself!の配布初期状態ではそれぞれ4つのlibファイルが存在するはずです(既にリビルド作業により削除されているかもしれません)。「WiiYourself!_d.lib」がデバッグ用、「WiiYourself!_dU.lib」がUnicode版デバッグ、「WiiYourself!_U.lib」がUnicode版リリース、無印の「WiiYourself!.lib」が非Unicodeリリース版、つまり最も最適化され、最も軽い(デバッグ情報の処理などを省いた)ライブラリです。
「Wiiyourself! lib」プロジェクトには、このライブラリのソースコードも含まれています。「wiimote.cpp」、「wiimote.h」、「wiimote_state.h」がソースコードです。これらのコードとプロジェクトが付属しているおかげで、WiiYourself!は非常に勉強しやすい状況になっています。
ではさっそく各々のライブラリをビルドしてみましょう。VC2008のメニュー「ビルド」→「構成マネージャ」から「アクティブソリューション構成」で、現在の構成を「Release」に切り替え「閉じる」を押します。Ctrl+Alt+F7で「リビルド」することができます。「Demo」は「WiiYourself! lib」プロジェクトに『依存』する設定にしてありますので、変更したアクティブな構成に従って、生成されるライブラリも、それぞれ異なる.libファイルが生成していることを、ファイルエクスプローラーで確認してみるとよいでしょう。
「誰かが作ったプログラムを利用する」という行為はAPIプログラミングを代表として、日常的に「よくあること」なのですが、さもすると内部の動作などはブラックボックス化してしまいがちです。学生さんなど、Googleでどこからか「良さそうな使えそうなコード」を探してきて、コピペ(コピー→張り付け)して「先生、(なんとなく)できました!」なんていうプログラミング「らしきこと」をしている光景もよく見かけたりします。
筆者にもそういう経験はあるのですが、もしあなたが大学生〜大学院生の理工系の学生さんなら「その習慣」は今すぐ戒めた方が良いと思います。その「コピペ病」は貴方のプログラミングスキルを確実に落としていきます…。
話を戻して、OSなどのプラットフォームAPIに従ったプログラミングであれば、ドキュメントなどの仕様書を参照すればよいのですが、WiiRemoteのような未知のデバイス系プログラミングでは必ずしも満足なドキュメントがあるわけではありませんし、提供されているAPIも内部の挙動としては、ユーザーの意図と異なる、ひどいときには間違っている…という可能性すらあります(次世代ゲーム機なんてその最たる例...おっと)。オープンソースのプロジェクトであれば、利用者自身でコードよ読んだり、掘り下げたり、機能を追加したりすることができますし、作者に間違いを指摘したり機能追加を共有したりすることもできるでしょう。
非常に地味なやり方ではありますが、「(英語が苦手だから)オープンソースは使っても、貢献はしないよ/できないよ」という方々にはぜひ一度試してみていただきたいトレーニングです。オープンソースの世界で作者の心意気を読み、言語の壁を越えて、コードで共有するための最短ルートです。
そう、我々は英語よりも便利な世界共通言語である「C/C++言語」が使えるではないですか!「コピペ」よりも「貢献」です。
新しい言語や環境などでプログラミングを学ぶときには、大きく分けて、2つの方法があるのではないでしょうか。非常によくできたサンプルプログラムを改造しながら学習する方法と、さまざまな装飾要素を取り払い、ゼロから自分で一歩一歩組み上げていく方法です。WiiYourself!の「Demo.cpp」は良くできたデモですが、装飾要素が多いため、初心者にはお勧めできそうにありません。
このセクションでは、まず大学の最初のC言語の授業で出てくるような「Hello, world!」と呼ばれる初歩の初歩のプログラムから、オープンソースであるWiiYourself!を自分のプロジェクトに組み込んでいくことでWiiYourself!を理解するという「ちょっと回り道」をします。
C++のプログラミングに詳しい方は、ナナメ読みでもかまいません。
それではまず、Visual C 2008 Express(以下VC2008)上で、プログラミングの最初の一歩「Hello, world!」プログラムをつくってみましょう。これは「Hello, world!」と画面に表示するだけのプログラムです。文字は何でも良いのですが歴史的に「Hello, world!」という文字であることが多く、こう呼ばれています。VC2008においても、同様のチュートリアルが用意されています。VC2008をインストールするとスタートページ「作業の開始」に「最初のアプリケーションを作成」というリンクが現れます。このセクションでは、ここからたどれる「標準C++プログラムの作成(C++)」というマイクロソフト提供のドキュメントを参考にしています。
まずは新しいプロジェクトを作成します。[ファイル]メニューの[新規作成]から、[Visual C++]プロジェクト[Win32]をクリックし、次に[Win32コンソールアプリケーション]をクリックします。プロジェクト名は何でも良いのですが、この先もしばらく使いますので「WiiMyself」としておきます。[場所]には「C:\WiiRemote\WiiYourself!」を指定して、[OK]をクリックして、新しいプロジェクトを作成します。
「Win32アプリケーションウィザード」が起動しますので、[空のプロジェクト]を選択して[完了]をクリックします。このあと、何もおきていないように見える場合、「ソリューションエクスプローラ」が表示されていないのかもしれません。[表示]メニューの[ソリューションエクスプローラ]をクリックして表示します。ウィンドウレイアウトがいつもと違う場合は「ウィンドウ」→「ウィンドウレイアウトのリセット」を実行するとよいでしょう。
ソリューションエクスプローラの[ソースファイル]フォルダを右クリックし、[追加]をポイントして[新しい項目]をクリックします。[コード]ノードの[C++ファイル(.cpp)]をクリックし、ファイル名[main.cpp]を入力して[追加]をクリックし、プロジェクトに新しいソースファイルを追加し、以下のコードを記述します。
#include <iostream> using namespace std; int main() { cout << "Hello, world!" << endl; return 0; }
さて、[F7]キーでコンパイルを通したら、実行環境を立ち上げましょう。[F5]キーで実行してもよいのですが一瞬で終了して、消えて見えなくなってしまうからです。エクスプローラーで生成された「WiiMyself.exe」をダブルクリックしても同様です。
そこで「Windowsキー+R」で「ファイル名を指定して実行」ダイアログを立ち上げて「cmd」とタイプして「ok」を押します。コマンドラインウィンドウが表示されたら、
cd C:\WiiRemote\WiiYourself!\WiiMyself\Debug
として、今コンパイルにより生成した「WiiMyself.exe」のあるディレクトリに移動します。長いパス名をタイプするのは面倒ですので、まず「cd (+半角スペース)」とタイプしてから、エクスプローラーのショートカット(フォルダのアイコン)をコマンドラインウィンドウにドラッグ&ドロップすると、パス名が表示されて便利です。[Enter]を押してコマンドを入力します。またクリップボードも使えます。コマンドラインウィンドウの左上をクリックすることで「編集」→「貼り付け」として、利用できますので覚えておくとよいでしょう。
さて「cd」コマンドで現在のパスを移動したら、WiiYourself!を実行します。
C:\WiiRemote\WiiYourself!\WiiMyself\Debug>WiiMyself.exe Hello, world!
無事に有名な「Hello, world!」が表示されましたでしょうか?以上がコマンドラインプログラムの作成の基本です。
「Hello world!」で喜んでいる場合ではありません。続いて、WiiYourself!を組み込んでいきます。ソリューションエクスプローラーの「ソリューション'WiiMyself'」を右クリックして「追加」→「既存のプロジェクト」として一つ上のフォルダにある「WiiYourself!.vcproj」を選んでください。
図のようにソリューションが取り込まれます。
続いて、「WiiMySelf」(自分のプロジェクト)のアイコンの上で右クリックしてプロジェクトのプロパティページを開いてください。まず「構成」を「すべての構成」とし、「構成のプロパティ」→「C/C++」→「全般」の「追加のインクルードディレクトリ」に「C:\WINDDK\3790.1830\inc\wxp」を設定、続いて、「リンカ」→「全般」の「追加のライブラリディレクトリ」に「C:\WINDDK\3790.1830\lib\wxp\i386」を設定します。
最後にメニュー「プロジェクト」→「プロジェクトの依存関係」で自分のプロジェクトが「WiiYourself! lib」に依存することを明示的にチェックします。この作業により、「WiiMySelf」の親として「WiiYourself! lib」を設定したことになります。これを忘れると、ライブラリ本体の更新が、WiiMySelfに伝わりませんし、継承関係が見えずうに思わぬ失敗を呼ぶことがありますので必ずチェックしてください。
これで、自分のプロジェクトからWiiYourself!のオブジェクトを参照することができるようになりました。実験してみましょう。さきほどのHello, world!を以下のように書き換えます。ついでですからWiiYourself!のライセンスに従って、ライセンス表示もしましょう。
#include "../../wiimote.h" int _tmain(int argc, _TCHAR* argv[]) { wiimote cWiiRemote; _tprintf(_T("Hello, WiiRemote!\n")); _tprintf(_T("contains WiiYourself! wiimote code by gl.tter\nhttp://gl.tter.org\n")); //ライセンス表示 return 0; }
main関数がtmainになり、unicodeと引数をサポートする形にしたり、coutではなくtprintfにして、wiimoteオブジェクトを作成している以外は何も変わりません。「Ctrl+Alt+F7」でリビルドします。コマンドラインウィンドウに移り、実行結果を確認してください。
C:\WiiRemote\WiiYourself!\WiiMyself\Debug>WiiMyself.exe Hello, WiiRemote! contains WiiYourself! wiimote code by gl.tter http://gl.tter.org
まず最初の実験はクリアです、次はVC2008でクラスが参照できているか実験です。ソースコードウィンドウ「wiimote cWiiRemote」というあたりにマウスを持っていき「wiimote」を右クリックして「宣言へ移動」を試してみてください。WiiYoureself!のソースコードである、ヘッダファイル「wiimote.h」が表示されれば正しい動作です。
さて実験を続けます。VC2008では「Intellisense」という入力補完機能がとても便利です(これが使いこなせなければ、VCはテキストエディタとあまり役割が変わりません!)。試しに「cWiiRemote.」とタイプしてみてください。クラスのプロパティやメソッドの一覧が表示されれば成功です。
では次に、このプログラムを「WiiRemoteに接続し、Bボタンで振動する」というプログラムまで拡張してみましょう。
#include "../../wiimote.h" int _tmain(int argc, _TCHAR* argv[]) { wiimote cWiiRemote; _tprintf(_T("Hello, WiiRemote!\n")); _tprintf(_T("contains WiiYourself! wiimote code by \ gl.tter\nhttp://gl.tter.org\n")); //WiiRemoteと接続 while(!cWiiRemote.Connect(wiimote::FIRST_AVAILABLE)) { _tprintf(_T("Connecting to a WiiRemote.\n")); Sleep(1000); } _tprintf(_T("connected.\n")); cWiiRemote.SetLEDs(0x0f); //LEDを全点灯 Sleep(1000); cWiiRemote.SetReportType(wiimote::IN_BUTTONS); //Homeボタンで終了 while(!cWiiRemote.Button.Home()) { while(cWiiRemote.RefreshState() == NO_CHANGE) { Sleep(1); } //Bボタンでバイブレーターが振動 cWiiRemote.SetRumble(cWiiRemote.Button.B()); } //切断・終了 cWiiRemote.Disconnect(); _tprintf(_T("Disconnected.\n")); return 0; }
まだ全ての行が理解できているわけではないかもしれませんが、「//」で記述されたコメントを頼りに、Intellisenseを使ってプログラム全文を自分で打ち込んでみてください。完成したら、「Ctrl+S」で保存して、「Ctrl+Alt+F7」でリビルドします。
まずはWiiRemoteをBluetooth接続してから、コマンドラインで「↑」キーを押して過去のコマンドを探し、「WiiMyself.exe」を見つけたら「Enter」を押して起動してください。接続成功するとLEDを点灯し、[B]ボタンを押すとバイブレーターが振動します。[Home]ボタンをおすとプログラム終了です。
C:\WiiRemote\WiiYourself!\WiiMyself\Debug>WiiMyself.exe Hello, WiiRemote! contains WiiYourself! wiimote code by gl.tter http://gl.tter.org connected. <ここでBボタンを押すとバイブレーターが振動> Disconnected.
あまりに地味な画面なのですが、ちゃんとLEDが点灯し、Bバイブレーターが振動していることが確認できます。Homeボタンで終了します。.NET版に比べて起動時の待ち時間がほとんどないのがコマンドラインプログラムの特徴です。
.NETはマイクロソフトが提供する現在主流のプログラミング環境です。対してコマンドラインは古くから使われているだけに、互換性や無駄を省いた実行速度などに利点があります。
たしかに.NET環境でつくったフォームによるプログラムとコマンドラインプログラムでは起動時初期化の時間の差があります。これはおそらく共通言語ランタイム(CLR)を経由して、.NETのフォームに関係のあるDLLを読み込んでから実行することに起因する差でしょう。コマンドラインプログラム命!という読者(筆者も好きです)は、実行速度以外の優位点として「EXEファイルサイズもきっと小さいに違いない」と思われるかもしれません。そこで上記の「WiiMyself.exe」を調べてみるとデバッグ版83.5KB、リリースビルドでは35KBと確かに小さいです。このコードはボタンイベントだけですから、ほぼWiiYourself!のwiimoteのオブジェクトの大きさでしょう。しかし.NETの同様の実行ファイルの大きさを調べてみると…なんと「10KB以下」。たしかに.NET FrameworkにかかわるDLLはOS側に存在するわけですから当然といえば当然、しかし画面のフォームのためのコードやWiimoteLibオブジェクトはどこにいってしまったのでしょう?あまりに差が大きすぎますよね?そうです…隣にある「WiimoteLib.DLL」のファイルサイズも忘れてはいけません。調べてみると32.5KB。足すと42.5KBですから、これでだいたい計算が合いますね。
前のセクションでは、シンプルな「Hello, WiiRemote!」プログラムを作ることで、自分自身でWiiYourself!を使って学ぶ第一歩を踏んでみることにしました。このように「自分自身で{ゼロから/いちから}書いてみる」という手法は、C/C++などのプログラミングを一通り勉強したけれど『挫折しました』という方には特にお勧めの方法です。
しかし地味なコマンドラインプログラムが続いています。続くこのセクションでは、ちょっと派手なことをしてみましょう。コマンドラインプログラムを使って「テルミン(Theremin)的なもの」を作ってみます。テルミンとは電波をつかった不思議な楽器ですが、ここではWiiYourself!からWindowsのプラットフォームAPIであるWin32を利用し、ソフトウェアMIDIを叩くことで、PCから音を鳴らします。せっかくのWiiRemoteですから、たくさんあるボタンや加速度センサーを使った操作を実装したいと思います。
プログラミング的にも、Win32など既存のC++環境で強力に利用できるプラットフォーム関数群をつかっていきます。プログラミング行数は短くても、非常に実用的なプログラミングを行えることがWiiYourself!を使う利点でもあります。
通称テルミン(Терменвокс[thereminvox]チルミンヴォークス)は、1920年にロシアの音響物理学者・アマチュア音楽家であったレオン・テルミン(Leon Theremin)が「エテロフォン」として発表した世界初の電波楽器です。「世界初の電気楽器」として有名ですが、実際には19世紀後半のエジソンによる「歌うアーク灯」などがありますので、テルミンはその演奏方法として特色のある「電波を使った無線演奏」が世界初の特徴としてふさわしいのかもしれません。電子回路的にはシンプルで、アナログラジオの特性を利用した音波生成なのですが、本書ではこともあろうに電子楽器インタフェースであるMIDI(Musical Instrument Digital Interface、電子楽器デジタルインタフェース)を利用して、さらにこともあろうにPCに付属しているソフトウェアMIDIを利用して実現しています。WiiRemoteを用いた入力方法は「テルミン的」ではありますが、発音方法がこんなにデジタルでは本来のテルミンを語るにはあまりにデジタル的で「Digital Theremin」とでも呼ばなければならない代物です。解説のためとはいえ、テルミンファンの皆さん、ごめんなさい。
ところで、このような「似て非なる創作」というのは、テクノロジーやメディアを駆使したアート分野である「メディアアート」の世界でも多々起きるようになってきました。学生さんなどが制作するときに、そのアイディアの元になるものの歴史や魂をきちんと調べて理解してから制作に臨まないと、このようなまがい物が氾濫してしまいます(それはそれでアートなのかもしれませんが!)。あまり説教じみたことにはしたくはありませんので、まずは「本物のテルミン」の演奏、音色を聴いてみてください。
■Theremin by Masami Takeuchi (Youtube)
http://www.youtube.com/watch?v=XwqLyeq9OJI
そしてチャンスがあったら、本物のテルミンを演奏してみてください。簡単に演奏できる楽器ではありません。でもピアノだって最初は同じですよね?楽器としての「難度」と「習熟」が、その神秘的な音楽的美しさに直結していることだってあるわけです…。
さて、最初のステップとして「ボタン操作で音が鳴るテルミン」を作ります。ボタンのイベントでMIDIを鳴らすだけなので、なんだかテルミンらしくはないですが、玩具としては十分楽しめるものです。プロジェクトとしては先ほどまでの「WiiMyself」をそのまま改造していきましょう。
まずはプログラムの前半を解説します。
#pragma comment(lib,"winmm.lib") #include <windows.h> //MIDI特有のエンディアンを変換するマクロ #define MIDIMSG(status,channel,data1,data2) ( (DWORD)((status<<4) | channel \ | (data1<<8) | (data2<<16)) ) #include "../../wiimote.h" //WiiYourself!を取り込む static HMIDIOUT hMidiOut; //MIDIハンドラ static BYTE note=0x3C, velocity=0x40; //音階と音量 static BYTE program=0x0; //音色 int _tmain(int argc, _TCHAR* argv[]) { wiimote cWiiRemote; HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); printf("WiiRemote-Theremin button version by Akihiko SHIRAI\n"); //LICENSE printf("contains WiiYourself! wiimote code by \ gl.tter\nhttp://gl.tter.org\n"); //MIDIを開く midiOutOpen(&hMidiOut,MIDIMAPPER,NULL,0,CALLBACK_NULL); //最初につながったWiiRemoteに接続する while(!cWiiRemote.Connect(wiimote::FIRST_AVAILABLE)) { printf("WiiRemoteに接続してください(0x%02X)\n",program); midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,0)); //ミュート Sleep(1000); program++; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); //音色変更 //接続失敗するたびに鳴る midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); } printf("接続しました!\n [1]/[2]音色 [↑]/[↓]音階 [←][→]音量 [Home]終了\n\n"); Sleep(1000);
まずプログラムの冒頭部分で、MIDIを扱うために"windows.h"と"winmm.lib"を取り込んでいます。さらにMIDI特有のメッセージ形式を簡単に発行するためにマクロという、単純な命令を変換する変換式を定義しています。HMIDIOUTはMIDIハードウェアそのものを捕まえるためのハンドルと呼ばれるもので、BYTE型変数「note, velocity, program」はそれぞれ音階、音量、音色を格納する変数です。
プログラムのタイトルとライセンス表示をして、WiiRemoteに接続しています。ちょっとした演出で、接続されるまで音色(program)がだんだん変わっていきます。
このままではコンパイルも実行もできない状態ですから、続きのコードを書いていきましょう。
//今回はボタンイベントだけが更新を伝える cWiiRemote.SetReportType(wiimote::IN_BUTTONS); while(!cWiiRemote.Button.Home()) { //Homeで終了 while(cWiiRemote.RefreshState() == NO_CHANGE) Sleep(1); //これがないと更新が速すぎる cWiiRemote.SetRumble(cWiiRemote.Button.B()); //Bで振動 switch (cWiiRemote.Button.Bits) { //ボタンごとでswitchする例 //音量 [←]/[→] case wiimote_state::buttons::RIGHT : if(velocity<0x7F) velocity++; midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; case wiimote_state::buttons::LEFT : if(velocity>0) velocity--; midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; //音色(=program) [1]/[2] case wiimote_state::buttons::ONE : if(program>0) program--; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); //音色変更 midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; case wiimote_state::buttons::TWO: if(program<0x7F) program++; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); //音色変更 midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; //音階 up/down case wiimote_state::buttons::UP : if(note<0x7F) note++; midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; case wiimote_state::buttons::DOWN: if(note>0) note--; midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; //[A]/[B]で同じ音をもう一度鳴らす case wiimote_state::buttons::_A : case wiimote_state::buttons::_B : midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); break; //その他のイベント、つまりボタンを離したときミュート。 default : midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,0)); } //現在のMIDIメッセージを同じ場所にテキスト表示 COORD pos = { 10, 7 }; SetConsoleCursorPosition(console, pos); printf("音色 = 0x%02X , 音階 = 0x%02X , 打鍵強度 = 0x%02X\n", program,note,velocity); } //終了 midiOutReset(hMidiOut); midiOutClose(hMidiOut); cWiiRemote.SetLEDs(0); cWiiRemote.SetRumble(0); cWiiRemote.Disconnect() printf("演奏終了\n"); CloseHandle(console); return 0; }
ボタンイベントの部分が長いのですが、前のセクションの改造ですし、ここは「コピペ」してもかまいません、編集や符号の向きに気をつけて「他の行との違いを意識しながら」コピペをするのがテクニックです。またprintf()の表示部分は英語や好きなメッセージに変えていただいてもかまいません。
無事にコンパイルができたら、WiiRemoteをBluetoothに接続する準備をして、実行してみましょう。
WiiRemote-Theremin button version by Akihiko SHIRAI contains WiiYourself! wiimote code by gl.tter http://gl.tter.org WiiRemoteに接続してください(0x00) WiiRemoteに接続してください(0x01) ...
というように表示されPCのスピーカーから音が鳴り始めたら成功です。WiiRemoteをBluetoothに接続してみてください。もしこの時点で音がなっていなかったら、PCのマスターボリュームを確認します。音声関係のボリュームがすべて正常で、他のプログラムからは音が出るのに「なぜかMIDIだけ鳴らない!」というときは、コントロールパネルの「サウンドとオーディオデバイス」から「デバイスの音量」の「詳細設定」を選んで「SWシンセサイザ」の音量がゼロ(ミュート)になっていないか確認してください(筆者はこの設定のおかげでプログラムが間違っているのかと何日も悩んだ経験があります...)。
さて、無事に音が出ていたら、以下のような画面になっているはずです。
接続しました! [1]/[2]音色 [↑]/[↓]音階 [←][→]音量 [Home]終了 音色 = 0x00 , 音階 = 0x50 , 打鍵強度 = 0x6A
WiiRemoteの十字キーの右や上を押すと表示されている値が変わることを確認してください。[A]ボタンを押すと「ポーン」という電子ピアノの音が鳴るはずです。[B]ボタンを押すとMIDIの発声に加えてバイブレーターが鳴るはずです。[1][2]ボタンで音色、すなわちMIDIの楽器(インスツルメント)を変えることができます。[Home]ボタンを押すと終了です。
さて、これでボタンイベントによるMIDI発声は完成しました。MIDIの楽器(program)は127種類もあります。筆者は[0x76]あたりの打楽器系のインスツルメントが好きです。
次はよりテルミンらしく加速度センサーでMIDIを鳴らせるようにしましょう。復習もあわせて、今まで使ってきたソリューションに新しい「Thermin-Acc」を加えることで新しいプロジェクトの作り方も学びます。
既に「WiiYourself! lib」や現在ボタン式テルミンになっているはずの「WiiMyself」を含むソリューション「WiiMyself」を右クリックして「追加」→「新しいプロジェクト」を選びます。「新しいプロジェクトの追加」ダイアログが現れたら「プロジェクト名」を「Theremin-Acc」とします。
「Win32アプリケーションウィザード」が起動しますので、ステップに従い「コンソールアプリケーション」をクリック、「空のプロジェクト」のチェックが外れていることを確認してください。完了すると新しいプロジェクトが現れます。
次に、ソリューションエクスプローラーで「Theremin-Acc」を右クリックし「スタートアッププロジェクトに設定」します(これを忘れると、[F5]キーでデバッグしたときに「ボタン版テルミン」が起動してしまいます)。この状態で[F5]キーによるビルドとデバッグを試すことはできますが、WiiYourself!の組み込みまで終わらせてしまいましょう。
メニューバーの「プロジェクト」→「プロジェクトの依存関係」を表示して「Theremin-Acc」に対して「WiiYourself! lib」にチェックします。
最後に、プロジェクトのプロパティを追加します。プロパティを表示し「アクティブ(Debug)」となっている構成を「全ての構成」に切り替えて、「C++」→「全般」→「追加のインクルードディレクトリ」に「C:\WINDDK\3790.1830\inc\wxp」を設定します。同様に「すべての構成」に対して、「リンカ」→「全般」→「追加のライブラリディレクトリ」に「C:\WINDDK\3790.1830\lib\wxp\i386」を設定します。
さて、これで準備完了です。前回作成したボタン版テルミンから一部コードを流用して、プログラミングを進めていくと楽でしょう。1/3づつ解説していきます。
#include "stdafx.h" #pragma comment(lib,"winmm.lib") #include <windows.h> //MIDI特有のエンディアンを変換するマクロ #define MIDIMSG(status,channel,data1,data2) ( (DWORD)((status<<4) | channel \ | (data1<<8) | (data2<<16)) ) #include "../../wiimote.h" //WiiYourself!を取り込む static HMIDIOUT hMidiOut; //MIDIハンドラ static BYTE note=0x3C, velocity=0x40; //音階と音量 static BYTE program=0x0; //音色 int _tmain(int argc, _TCHAR* argv[]) { wiimote cWiiRemote; HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTitle(_T("WiiRemote-Theremin Acceleration version")); printf("WiiRemote-Theremin Acceleration version by Akihiko SHIRAI\n"); //LICENSE printf("contains WiiYourself! wiimote code by \ gl.tter\nhttp://gl.tter.org\n"); //MIDIを開く midiOutOpen(&hMidiOut,MIDIMAPPER,NULL,0,CALLBACK_NULL); //最初につながったWiiRemoteに接続する while(!cWiiRemote.Connect(wiimote::FIRST_AVAILABLE)) { printf("WiiRemoteに接続してください(0x%02X)\n",program); midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,0)); //ミュート Sleep(1000); program++; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); //音色変更 midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); //接続しないたび鳴る } printf("接続しました!\n"); printf("\n\t [B]打鍵 [Roll]音量 [Pitch]音階 [1]/[2]音色 [Home]終了\n"); Sleep(1000);
さて、ここまでは前回のボタン版テルミンとほとんど何も変わりません。余裕があればテキスト表示部分なども好きに変えてみるとよいのではないでしょうか(ただしライセンス表示を変えないように注意!)。
//今回はボタン+加速度イベントが更新を伝える cWiiRemote.SetReportType(wiimote::IN_BUTTONS_ACCEL); while(!cWiiRemote.Button.Home()) { //Homeで終了 //RefreshStateは内部更新のために呼ばれる必要がある while(cWiiRemote.RefreshState() == NO_CHANGE) { Sleep(1); //これがないと更新が速すぎる } switch (cWiiRemote.Button.Bits) { //ボタンごとでswitchする //音色(=program) [1]/[2] case wiimote_state::buttons::ONE : if(program>0) program--; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); //音色変更 break; case wiimote_state::buttons::TWO: if(program<0x7F) program++; midiOutShortMsg(hMidiOut,MIDIMSG(0xC,0,program,0)); break; default: //音量 [傾きPitch] velocity = \ (int)(127*(cWiiRemote.Acceleration.Orientation.Pitch+90.0f)/180.0f); if(velocity>0x7F) velocity=0x7f; if(velocity<0x00) velocity=0x00; //音階 [傾きRoll] note = (int)(127*(cWiiRemote.Acceleration.Orientation.Roll+90.0f)/180.0f); if(note>0x7F) note=0x7F; if(note<0) note=0; if(cWiiRemote.Button.B()) { //[B]打鍵 midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,velocity)); } else { midiOutShortMsg(hMidiOut,MIDIMSG(0x9,0x0,note,0)); }
注意深く読めば、それほど難しいことはしていないことに気づくと思います。「cWiiRemote.SetReportType(wiimote::IN_BUTTONS_ACCEL)」で、前回はボタン更新だけだったリポートモードを、ボタンと加速度の変化があったときにリポートする、というモードに変えています。これも「::」をタイプすると、Intellisenseが自動で表示してくれますから、適切なモードを選択肢から選びましょう。
なおボタンイベントはコールバックで処理してもいいのですが、多くの読者の理解のために「switch (cWiiRemote.Button.Bits)」として表現しています(コールバック化したい方は後の「demo.cpp」を参考にするとよいでしょう)。Case文で音色変更に必要となるボタン[1][2]を分岐させて、音色(program)を変更し、MIDIコマンドを送信しています。なお前回のボタン版と違って、音色の変更だけで打鍵はしていません。
そしてこのswitch文におけるほとんどのイベントは「default:」に流れます。ここで加速度センサーの値、特にWiiYourself!で取得できる姿勢推定による「Pitch(仰角;首を上下にする方向)」と「Roll(ロール;首を左右に傾ける方向)」をそのまま「音量」と「音階」にわりあててみました。
…『割り当ててみました』という表現をあえて使ったのは、これは別に「cWiimote.Acceleration.RawX」などの値でも全く問題ない、ということです(そのほうが変換も不要)。この場合、PitchやRollは±90度(-90度〜+90度)の値をとります。
velocity = \ (int)(127*(cWiiRemote.Acceleration.Orientation.Pitch+90.0f)/180.0f); note = (int)(127*(cWiiRemote.Acceleration.Orientation.Roll+90.0f)/180.0f);
この式を参考にすることで、たいていの入力は自分の望みの値域に変換できるよう、例として、今回はこのような変換式を利用しています。インタラクションをデザインする上で、必要なボタン、必要な角度、わかりやすい利用方法などなど適切と思われる変換式を考える必要があります。参考にしてください。
//座標指定テキスト表示 COORD pos = { 10, 7 }; SetConsoleCursorPosition(console, pos); printf("加速度 X = %+3.4f[0x%02X] Y = %+3.4f[0x%02X] Z = %+3.4f[0x%02X] ", cWiiRemote.Acceleration.X, cWiiRemote.Acceleration.RawX, cWiiRemote.Acceleration.Y, cWiiRemote.Acceleration.RawY, cWiiRemote.Acceleration.Z, cWiiRemote.Acceleration.RawZ ); pos.X=10; pos.Y=9; SetConsoleCursorPosition(console, pos); printf("姿勢推定 Pitch = %+3.4f Roll = %+3.4f Update=%d ", cWiiRemote.Acceleration.Orientation.Pitch, cWiiRemote.Acceleration.Orientation.Roll, cWiiRemote.Acceleration.Orientation.UpdateAge ); pos.X=10; pos.Y=11; SetConsoleCursorPosition(console, pos); printf("音色 = [0x%02X] , 音階 = [0x%02X] , 打鍵強度 = [0x%02X] ", program,note,velocity); break; } } //終了 midiOutReset(hMidiOut); midiOutClose(hMidiOut); cWiiRemote.SetLEDs(0); cWiiRemote.SetRumble(0); cWiiRemote.Disconnect(); printf("演奏終了\n"); CloseHandle(console); return 0; }
最後のパートは「プログラムの見た目」に関わる場所です。コマンドラインプログラムですから地味でもいいのですが、現在の加速度の変換前の値、つまりWiiRemoteからの生値である「RawX」、「RawY」、「RawZ」を観察して見る価値はあるので、あえて16進数で表現しています。地味な16進数表示ではなく、テルミンらしいアンテナや、何か派手なものを表示するプログラムに変えていただいても全く問題ありません。
最後に、終了パートでMIDIを閉じて、LEDやバイブレーターを停止させてから切断しています(バイブレーターは使っていませんが、これを忘れるとバイブレーターを使ったまま、間違えて終了してしまったときなど大変です)。
なおcWiiRemoteオブジェクトの終了方法ですが、裏で走っている測定スレッドは必要が無くなれば自動的に削除されるので、「Disconnect()」をコールして切断さえしておけばこのまま終了してもよいようです。
地味なコマンドラインプログラムですが「SetConsoleCursorPosition」を使うことで昔懐かしいBASICでのLOCATE文にあたる表示位置指定を扱うことができます。ここでは目的の位置にprintfを表示させるためだけに使っていますが、1980年代の子供たちはこれで「ブロック崩し」ぐらいは作ったものです。他にもWiiYourself!の「demo.cpp」には色をつけたり、点滅させたり、音を鳴らしたりといった往年のコマンドラインプログラムの技が随所に見られます。最近のグラフィックスは「ブロック崩し」の数万倍も高解像度ですが、テキストグラフィックス(文字を使った絵作り)も使いこなせばなかなかに「クールな技」になるのではないでしょうか。…単に人々の想像力が低下しているだけなのかも?
さて、これでプログラムは完成です。無事にコンパイルが通ったら、WiiRemoteにBluetoothを接続して、カッコよく構えましょう。何か面白いことをする上で「間(ま)と構え」は非常に重要です。
まずものすごいスピードで何かが測定できているのが見られるはずです。
[B]打鍵 [Roll]音量 [Pitch]音階 [1]/[2]音色 [Home]終了 加速度 X = -0.3846[0x7B] Y = -0.5556[0x77] Z = +0.6923[0x97] 姿勢推定 Pitch = +35.0491 Roll = -23.4268 Update=0 音色 = [0x00] , 音階 = [0x2E] , 打鍵強度 = [0x58]
この値は高速に動いている数字は何でしょう?そうです「加速度センサー」の値です。ものすごいスピードで取得できていることがわかります。さて、[B]ボタンで「打鍵」(鍵盤を叩くこと)をしてみましょう。「ボロロロローン」と、情熱的かつ前衛的なピアノ演奏が聞こえれば、成功です。時には指揮者のように、時には舞踊のように、[B]ボタンを押しながら、WiiRemoteに仰角やロール角を与えてみましょう。腕の曲げ伸ばしが「音階」、ひねりが「音量」になんとなく当てはまっているはずです(制御はとても難しいのですが、不可能ではないはず)。[2]ボタンを押して、音色を変えたりして試してみましょう。飽きるまで遊んだら[Home]ボタンで終了します。
多くの読者はここで、すぐに記述したコードを変更してみたくなったはずです。これが「正しいプログラミング姿勢」です。このページにしおりを挟んだら、思う存分、テルミンのチューニングや、画面の表示デコレーションを変更して遊んできてください。
なお改良のヒントとしては以下のような要素があります。
本プログラムのWin32によるMIDI制御はこちらのWebサイトでの解説を参考にしています。
■Windowsプログラミング研究所(kymats氏)
http://www13.plala.or.jp/kymats/study/MULTIMEDIA/midiOutShortMsg.html
他にもMIDIを制御する方法はたくさんありますが、こちらのサンプルがもっとも「テルミン向き」でした。たくさんの使えるサンプルを用意されている、作者のkymatsさんにはこの場をお借りして感謝を述べさせていただきます。
以上でWiiYourself!によるWin32を用いたテルミンの開発を終わります。Win32の資産やその他のプロジェクトを利用する上での実際的な助けになったでしょうか?
またゲームプログラマー的な思考をする方は、テルミンの実行を通して「WiiRemoteは一体どれぐらいの速度で動いているんだろう?」「どうやったら最大のパフォーマンスを出せるのだろう?」といった疑問も出てきたのではないでしょうか?この疑問に対するコーディングと実験は次のセクションに続きます。
なお筆者はこのセクションで2つも玩具プログラムができたので、「インタラクション技術の研究者」という科学的な興味から、自分の子供たちで実験をしてみました。結果だけ述べると、6歳の息子はどちらかというと加速度センサー版のテルミンのほうが好きなようでした。しかし2歳の子供はボタン式のほうが断然好きで、しかも音色は「バキュン!バキュン!」という[0x7F]を確実に嗜好していることが観察から見て取ることができました。どちらの場合も、被験者(あえてこう呼ぶ)は、画面やPCのスピーカーと、手元のWiiRemoteが関係があるということは確実にわかっていましたが、表示されているものが何なのかは理解していないようです。
<★この辺イラストあったほうが良いですか?>
子供の嗜好というのは同じ環境で育っていても異なりますし、年齢によってもその理解や楽しさは異なります。「あたりまえのこと」ではありますが、この種の地味な実験は、最近のゲームデザインでは、意外と忘れられている点でもあります(倫理規定によるレーティングはありますが、インタラクション可能か?楽しめるかどうか?という点で)。しかもこのような「興味を引くか・楽しめるか」という視点での実験は最新の認知科学の話題にアプローチする科学の実験であるともいえるでしょう。それがこんな数十行たらずのプログラムでも行えるわけです。
しかし注意があります。一般の子供を使ったこの種の心理実験やデータ取得には、同意書の取得などが必要です(言語やサインが理解できない年齢ではより困難です)。本書は、掲載したプログラムを利用した実験などによる直接・間接的障害や損害について一切責任を負いません。人間を使った実験(嫌な言い方をすれば「人体実験」)に関わる倫理規定について興味がある方は「ヘルシンキ宣言」を参考にするとよいでしょう。研究を進める上での考え方としては「ポケモン光てんかん」などの規定策定に関わられた国立小児病院の二瓶健次先生による「バーチャルリアリティは子どもに何ができるか-臨床場面でのVR-」などが非常によくまとまっていて参考に値します。さまざまなエンタテイメント技術を研究対象にする上での基本的な考え方として引用できる論文としては、筆者の博士論文の一部である「エンタテイメントシステム」(芸術科学会論文誌第3巻vol.1)が参考になるかもしれません。
このセクションではWiiYourself!によるコマンドラインプログラムを使うことで、WiiRemoteをより高度な計測器として利用することに挑戦してみます。
重力・姿勢・動作周波数を測定するプログラムを作成し、WiiRemoteの更新パラメータを変えることで、加速度センサーが『いったいどれぐらいの速度で動いているのか?』を測定してみます。
この後、WiiRemoteを使いこなす上で非常に重要な実験なのですが、非常に地味な上に「物理が苦手すぎて寝てしまいます!」という読者はナナメ読みでもかまいません。無理強いは体によくないので、結果だけ利用しましょう。
先ほどの加速度センサー版テルミンと同じく、今度もまた新しいプロジェクト「Measurement」をソリューションに追加してみましょう(せっかく作ったテルミンを壊してもいいのであれば、止めません)。面倒な人は現在、ボタン版テルミンになっている「WiiMyself」のmain.cppを置き換える形で作成してもよいでしょう(少しでも未練がある人は、コメントアウトして活用するとよいでしょう)。
測定プログラムは60行程度です。_tmain()関数しか使いません。理解のしやすさと解説の都合から2つのパートに分けますが、テルミンからのソースが利用できる箇所も多々ありますので、可能であれば一気にコーディングしてしまうとよいでしょう。
#include "../../wiimote.h" #include <mmsystem.h> // for timeGetTime() #include <conio.h> // for _kbhit() int _tmain(int argc, _TCHAR* argv[]) { wiimote cWiiRemote; DWORD currentTime=0; //現在の時刻を格納する変数 //動作周波数測定用 DWORD startTime=0, Refreshed=0, Sampled=0; float duration=0.0f; //経過時間[秒] bool continuous=false; _tprintf(_T("WiiRemote Measurement\n")); _tprintf(_T("contains WiiYourself! wiimote code by \ gl.tter\nhttp://gl.tter.org\n")); while(!cWiiRemote.Connect(wiimote::FIRST_AVAILABLE)) { _tprintf(_T("Connecting to a WiiRemote.\n")); Sleep(1000); } _tprintf(_T("connected.\n")); cWiiRemote.SetLEDs(0x0f); Sleep(1000); //何か引数が設定された場合、周期モードに設定 if (argv[1]) { _tprintf(_T("ReportType continuous = true [%s]\n"), argv[1]); continuous = true; } //ボタンか加速度に変化があったときにリポートするよう設定 //第2引数をtrueにすることでデータ更新を定期化(10msec程度)する //デフォルトはfalseでポーリング(データがあるときだけ)受信モード cWiiRemote.SetReportType(wiimote::IN_BUTTONS_ACCEL, continuous); startTime=timeGetTime(); //開始時の時間を保存
コメントにも記載はしていますが、大事なところを補足しておきます。
WiiYourself!のAPI関数「SetReportType()」の詳細が気になっていた読者もいると思います。この関数は2つのフォーマットがあり、いままでは「bool continuous」が無いタイプを使って、ボタンや加速度の変化といったリポートモードを指定していました。関数を右クリックして「宣言へ移動」すると、以下のようなgl.tter氏のコメントを読むことができます。
//set wiimote reporting mode (call after Connnect()) //continous = true forces the wiimote to send constant updates, even when // nothing has changed. // = false only sends data when something has changed (note that // acceleration data will cause frequent updates anyway as it // jitters even when the wiimote is stationary) void SetReportType (input_report type, bool continuous = false); 【参考訳】WiiRemoteのリポートモードを設定します、Connect()の後にコールして下さい。 continuousをtrueにすることで、WiiRemoteに変更があったかどうかに関わらず、周期的に更新を送らせるよう設定できます。falseのときは何か変化があったときだけデータを送信します。WiiRemoteががっちりしたところに置かれているときはビクビク(jitter)してしまいますが、加速度データはよく(frequnet)更新をするでしょう。
この「更新があったときだけ送信」という通信方法を通信用語で「ポーリング(polling)」といいます。送信要求を受けたデバイスが『あるよ〜』と答えたときだけ、実際にデータが流れるので通信帯域を節約できます。実際にボタン、加速度、赤外線、拡張端子…とフルスペックである「IN_BUTTONS_ACCEL_IR_EXT」を宣言すると、Bluetoothの帯域を圧迫してしまうこともあるようですので、これは調べてみなければなりません。
それでは後半のコード、接続後のループに続きます。
//Homeがおされるまで、もしくは10秒間測定 while(!cWiiRemote.Button.Home() && duration<10) { while(cWiiRemote.RefreshState() == NO_CHANGE) { Refreshed++; //リフレッシュされた回数を記録 Sleep(1); //CPUを無駄に占有しないように } Sampled++; //データに変更があったときにカウントアップ cWiiRemote.SetRumble(cWiiRemote.Button.B()); _tprintf(_T("TGT:%d %+03d[msec] R:%d S:%d D:%1.0f Accel: X %+2.3f Y %+2.3f \ Z %+2.3f\n"), timeGetTime(), //現在の時刻 timeGetTime() - currentTime, Refreshed, Sampled,duration, cWiiRemote.Acceleration.X, cWiiRemote.Acceleration.Y, cWiiRemote.Acceleration.Z); currentTime = timeGetTime(); duration = (timeGetTime()-startTime)/1000.0f; } cWiiRemote.Disconnect(); _tprintf(_T("Disconnected.\n")); duration = (timeGetTime()-startTime)/1000.0f; printf("接続時間%4.2f秒 更新%d回 データ受信%d回\n 更新周波数%.2fHz サンプリング%.2fHz\n", duration, Refreshed, Sampled, (float)Refreshed/duration, (float)Sampled/duration); while (true) if (_kbhit()) {break;} //何かキーを押すまで待つ return 0; }
さて、コーディングが終わり、コンパイルが通ったら、WiiRemoteをBluetooth接続して、机の上などに立てて置いてみましょう。下の実行例では、拡張端子を下にして安定した机の上に立てています。
... TGT:189427129 +10[msec] R:4818 S:582 D:10 Accel: X +0.538 Y \ -0.407 Z -0.308 TGT:189427139 +10[msec] R:4823 S:583 D:10 Accel: X +0.423 Y \ -0.370 Z -0.231 TGT:189427149 +10[msec] R:4828 S:584 D:10 Accel: X +0.308 Y \ -0.222 Z -0.115 TGT:189427159 +09[msec] R:4833 S:585 D:10 Accel: X +0.231 Y \ -0.037 Z +0.115 TGT:189427168 +08[msec] R:4837 S:586 D:10 Accel: X +0.192 Y \ +0.148 Z +0.385 Disconnected. 接続時間10.21秒 更新4837回 データ受信586回 更新周波数473.84Hz サンプリング57.41Hz
引数は指定していないのでポーリング受信モードで動作しています。最後に表示されるメッセージとその解釈について解説します。
何かボタンを押すと終了します(_kbhit関数)。安定な場所にいると、本当に少ししかデータが流れませんが、手に持っていたりすると、ものすごい速さでデータが流れているのがわかります。
ポーリングモードではない、周期モード(continuous)も試してみましょう。デバッグ時の引数指定は簡単です。プロジェクトのプロパティ「デバッグ」→「コマンドの引数」に何か文字を書いてあげることでtrueになります。
今度はWiiRemoteを持っているかどうかによらず、以下のような結果になったのではないでしょうか?
接続時間10.21秒 更新5076回 データ受信993回 更新周波数497.16Hz サンプリング97.26Hz
周期モードは10msecごとに1回データを送るようですから、10秒で1000回、周波数にして100Hzとなり、上記の結果とほぼ一致します。
他にもリビルドが必要になりますが、リポートモードに「IN_BUTTONS」や「IN_BUTTONS_ACCEL_IR_EXT」を入れて、変化を見てみると良いでしょう。なおWiiYourself!で実験したところでは、赤外線系モードにおいては周期モードが基本になっているようです。
さて、この節ではWiiRemoteの加速度センサーを計測器として使うための実験プログラムを作成して、各リポートモードのベンチマーク的なことを行ってみました。WiiYourself!においてはAPIとして隠蔽されているわけではないので、発見も多かったのではないでしょうか?
測定してみると、WiiRemoteは周期モードなら100Hzぐらい、ポーリングなら60-80Hzぐらいで動作できるようです。ゲームコントローラや計測器として考えたときに、これは高速なのでしょうか?
たとえば普通のゲーム機が1秒あたり30-60回のグラフィックスを更新し、Webカメラが1秒当たり15-30回の画像を取得して、そこから画像処理をして座標を検出しているわけですから、WiiRemoteの動作速度は「かなり速い」と表現できるのではないでしょうか。サンプリング定理を引用すると、実行周波数の倍は必要、ということで少なくともゲームに使うなら「十分な速度」といえるかもしれません。
しかし、計測器としてみるとちょっと足りないかもしれません。まず加速度センサーは8bit(256レベル)あるのですが、世の中の携帯電話には12bit(1024レベル)とれるものも実装されていたりします。どれぐらい違うかというと、ポーリングモードのときに、机に置いたり「そーっと動かしてもデータが取れる」というレベルです。センサーの精度特性にも関係があるので一概にbit数では語れませんが、物理的な計測として、加速度と時間がわかれば、そこから速度と距離が算出できるはずですが、WiiRemoteの分解能では「そーっと動かしたぐらいで針がふれない」ので、一番最初の「加速度」が取れていないことになります。つまりこの方法で加速度の積算から速度や距離を算出するのはとても困難であることが想像できるでしょう(赤外線を組み合わせれば不可能ではないですが)。
しかしこのセクションで紹介したプログラムの実行結果を見ると「握って普通に動かした状態」では加速度が高速に取得できているようです。これは「重力加速度」なのですが、この値を使うことで「WiiRemoteの姿勢」も推定できますし「重力加速度よりも強い力の入力」たとえば、テニスのスイングやボクシングのパンチなどを簡単に見分けることができます。良くも悪くも「ゲームのために設計されたデバイス」なのでしょう。
さて読者の多くはこの節で「動作周波数」や「サンプリング周波数」など普段聞きなれない言葉を目にしたのではないでしょうか。玄人のゲームプログラマーや研究者でも無い限り、普段はこういったことに目を向けたりはしないと思います。しかしプログラミングの上でインタラクションを考えると、すぐにこの挙動特性については問題が出てきます。
たとえば有名な格闘ゲーム「ストリートファイター」シリーズにおいて、波動拳や昇竜拳といったボタン連携によるイベント発生があります。例えば波動拳の場合は【←、→、→+強パンチ】といったボタンコンビネーションになっており、これを確実に入力できることがゲームを有利に進めるスキルになるのですが、一方でゲームプログラムがこのボタンコンビネーションを認識するためには「ポーリングかどうか?」、「どれぐらいの更新速度で」、という情報を理解して、ゲームの『操作感』(feeling of controling)や『操作難易度』(difficulty of controlling)を設定することが重要になります。そういった特性をつかむ上で、この手のレポートモードの精査、実験プログラムの作成は「すばらしい操作感」を実現するために非常に重要な策定パラメーターであり、ゲーム会社のエンジニアにとって、このプログラムはそのための予備実験なのです(地味ですが)。
実際のゲーム開発においては、グラフィックスの速度などもこのプログラムの上に載ってきますし、最大のパフォーマンス(例えば、弾幕を何個描くと動作が遅くなるか、といった最大処理能力)を設計する上で、どこに描画ループをおき、当たり判定をおき、入力更新をおくのか、といった設計は、アクション性の高いゲームの開発の初期段階において、最高に重要な実験要素になります。上記のプログラムにおける「Sleep(1)」の場所にどれぐらい余裕があるのか、という話ですね。
WiiRemoteの加速度センサーも、実は基本は昇竜拳を判定するボタンと考え方にあまり変わりはありません。加速度センサー特有のデータ利用(セガ「レッツタップ」のような)ではなく、入力されたアクションに対してボタンを割り当てるようなときは、「どれぐらいの秒数で」、「どのような特性を持った」、「どれぐらいの強度で」といった情報を、いかに「誰でも操作できるように」というように割り当てる必要があります。これをif文などで書いていくのは大変な作業ですし、WiiRemoteをブンブン振って試しているうちに「自分の動作がはたして一般的なのか?」と疑問になってしまうこともあるでしょう。実際そういった「操作が大変なゲーム」もWii初期には多く発売されておりますが、「新しいエンタテイメント体験を作り出すこと」と「ユーザインタフェースとしての一般性を維持すること」というのはとても難しいトレードオフであることがわかります。
「私は物理が苦手なんですが...」そんな読者の方には、意外に簡単な結論があります。まず「ポーリングではなく、周期モード」を使うことです。動作環境によるプログラムの詰まりを軽減し、10ミリ秒に一度確実に値が返ってくるというモードです。
しかもWiiRemoteの計測周波数特性については、もうこのセクションで実験してしまいましたので、「取得できた加速度センサーの値は1/100秒あたりのデータである」と推定してしまってよいでしょう(WiiYourself!を使った場合の、ですが)。
例えば、何か一つのモーションを入力したときに、100回データが入ったら、それは「1秒かかるモーション」だった、ということです。「1秒間に16回のボタン連打」を入力として検出したかったら、「6.25回のサンプリングに対し1回ボタン入力のOff-On-Offが実現できればいい」ということになります。逆に1/100秒の更新周期ならどんなに頑張っても(ズルしても)、論理上は1秒間あたり50回しかボタンのOn→Offは入力しようがありません。小難しい数学や物理よりも、こういったことがしっかりイメージできるかどうかのほうがインタラクションを支える技術としては大事で、今回紹介したような細かい実験を沢山作ることで、インタラクションを支えるプログラミング技術はグングン上がっていきます。
ここに例題を出しておきます。アメリカの古い映画のフェスティバルのシーンなどに出てくる「ハンマーで叩いた力を測定するゲーム」をご存じでしょうか。正式名称がわからないので「パンチ力測定」としておきます。いままでの測定プログラムの応用で、この「パンチ力測定」ゲームを作ることができるはずです。
力の大きさを計算するには、マグニチュード、すわなち「加速度センサー各軸の要素を二乗した物の和」で算出できます。
F = X * X + Y * Y + Z * Z;
このFが大きくなれば大きくなるほど、測定時間あたりの力が大きいことがわかります(この式は正の値しかとりません)。ちなみに、筆者のWiiRemoteの設置状態では重力加速度はある軸に約1.0弱で検出され、他の軸はゼロになっていますから「F≒1.0」…これが何もしていない状態の地上の重力を示しています。これを仮にGと呼びます。
ここで思いっきりWiiRemoteを握りしめ(ストラップもつけてくださいね!)、ハンマーよろしく『ブンッ』と降りおろしたときの最大のFがGの何倍か、を算出することで、パンチ力測定ができるわけです(試してみると、だいたい5G〜16Gがゲームになるあたりです)。
では次は、これを応用して「テニスのモーション」を作ってみましょう。実はこれは意地悪な練習問題です。実際に作っていろんな人で測定してみると、テニスのモーションは、ハンマーゲームとは似て異なり、同じような加速度の振る舞いとして測定されません。多くの人が、(素振りとは異なり)ボールが当たるであろう「インパクトの瞬間」に『グッ』と力を込めて止めてしまいますので、一番良い瞬間に逆向きのGがかかってしまうのです。しかも、この逆向きの力もかからず、きれいに「フォロースルー」を入力できる人もいます。何故かインパクトの瞬間にねじれる人もいます。またゲーム的にもパンチ力測定のように「さあ叩いてください!」という感じにも作りづらいです(ボールが弾んだ瞬間を使うと良いでしょう)。どの瞬間にサンプリングを開始して、どれぐらいの長さの記録をして、どのような検出アルゴリズムを作るのか、ここまで学んだ皆さんはぜひ「if文のカタマリ」以外で作ってみることに挑戦してください!
ここまでWiiYourself!の深みを実験したあなたはもう、加速度センサーについて基礎的なことは学ぶことはない、と思うかもしれません。たしかにここまでのサンプルプログラムを使うことで「レッツタップ」のようなアクションは作れそうです(目コピでゲームをまねして開発するのはプログラミングの勉強にはなりますが、本書では避けることにします!)。上記の練習問題「テニスのモーション」もぜひやってみてください、以外と奥深いです。
それから、大学で信号処理を勉強している人は、if文による条件分岐、数式によるエレガントな認識を延長して、ぜひここで信号処理の技術をWiiRemoteに適用してみてください。バンドパスフィルターや分類器、機械学習といった理論が面白いほど加速度センサーに利用できますし、まだまだ未開拓の部分でもあります。
そして研究者のアナタ、「もうWiiRemote研究はやり尽くされた」と思っていませんか?私はまだまだ可能性があると感じています。認識にHMM(Hidden Markov Model;隠れマルコフモデル)やSVM(Support Vector Machine)などの分類器も使えるかもしれません。
おっと、「SVM」や「HMM」がなんだかわからない人は、ごめんなさい。簡単に表現すると「人工知能がWiiRemoteから入力された信号を認識できる」という話で、ある「モーションA」と、別の「モーションB」をそれぞれサンプルとして学習させると、その違いを自動的に認識して、以後は「なんだかよくわからない入力」が入力されても「A」か「B」に分類することができるという仕組みです。HMMは入力の前後関係を自動で獲得します。SVMは手書き認識に使われたりもしています。
最近では特に機械学習系の話題は面白く、ドイツのOldenburg大の学生Benjamin Poppinga氏による「WiiGee」というプロジェクトはJava5.0とJSR-82というBluetoothライブラリを使ってHMMを利用したモーション認識を実現しています。
■WiiGee
http://www.wiigee.org/
ところで「AiLive」というゲーム開発者用の製品を任天堂からライセンスを得て販売しているアメリカ・シリコンバレーの会社もあります。実はもうWii本体用のゲームプロダクトには利用されはじめているのかもしれませんね。
■AiLive(日本語ページ有り)
http://www.ailive.net/
ちなみにこの会社の採用ページでは「Artificial Intelligence Researcher(人工知能の研究者)」を募集しています。腕に自信がある人は採用試験を受けてみては?
このセクションではWiiYourself!が現在有する実験的機能のうちでも最も先進的な「スピーカーの再生」を利用します。もちろん実験的機能なので、制限もあり、品質も十分ではないですが、試してみる価値は十分にあります。
WiiRemoteに搭載されているサウンドプロセッサが非常に特殊なので、世界中のハッカーたちが挑戦していますが、特定の周波数以外鳴らすのは非常に難しいらしく、WiiYourself!だけがWAVファイルからの再生に成功しています。とはいえ、まだ完全な状態ではありません。
※この実験はWiiYourself!v1.01で試しています。またTOSHIBA製スタックとBroadcom製スタックでのみ実験しています。お使いの環境で「Demo.exe」を起動して「2」ボタンを押してDAISYモードで何も聞こえない環境ですと、この実験は徒労に終わるかもしれません。
まず、WiiYourself!で利用できる音声ファイルを用意しましょう。現在のところ、サポートしているのは「16ビット・モノラル」の非圧縮WAVファイルで、さらにサンプリング周波数は「2470〜4200Hz」となっています(詳細はWiiYourself!の「Load16bitMonoSampleWAV」関数を参照)。こんな形式のWAVファイルが簡単に手にはいるわけではありませんので、ツールを使ってコンバートしましょう。
こんな用途のために便利なツールを見つけました。「KanaWave」という、好きなひらがなからWAVファイルを作成するツールと、「SCMPX」というch3氏の公開している再サンプリングが可能なツールです。
■KanaWave(河合章悟氏)
http://www.vector.co.jp/soft/win95/art/se232653.html
■SCMPX(CH3氏)
http://www.din.or.jp/~ch3/
「KanaWave」は説明が不要なぐらい簡単なツールです。ひらがなで作りたい音の雰囲気を擬音で書くと、その音にあった波形を生成します。ここでは「うぃーん」という音を作ってみました(文字を入れた他は、雰囲気を出すために音の高さを1目盛りだけ下げてみています)。再生ボタンを押して視聴して、気に入ったら「KanaWave」メニューから「Waveファイルに変換」として保存します。ここでは「Wiin.wav」としました。
次に変換です。「SCMPX」を起動したら「CONVERT」から「Single file...」→「Resample...」を選んでください。ファイル選択ダイアログが表示されますので、先ほどKanaWaveで生成したWAVファイル、もしくは特にWAVファイルがなければ「C:\Windows\Media」より聞き慣れたWindows提供のWAVファイルを選んでください。
サンプルレートは「4200」(現在WiiYourself!がサポートしている最高音質)、量子化ビットは「16bit」、チャンネルは「mono」で保存します(ここでは「Wiin_rs.wav」になりました)。
このファイルをWiiYourself!の「Demo」フォルダ内「Daisy16 (3130).wav」と置き換えるか、ソースコードのLoad16bitMonoSampleWAV関数での読み込みファイル名「Wiin_rs.wav」と変更することでロードすることができます。ファイル内部の形式が少しでも間違っているとエラーになってしまいますが、上記の方法に従って生成したWAVファイルなら、必ずこの方法でロードは成功します。根性のある人はVCのデバッグ機能を使って根気よく追いかけることで、WAVファイル読み込みの内部動作も理解できるので玄人にはお勧めです。
「Demo.exe」実行時にエラーが出なければ「2」ボタンを押すことで再生されます。明らかに音が出ていない場合は「A」ボタンを押して、矩形波(ピー...)を出してみると、復活したりします。いずれにせよ4.2KHzですから音質については課題があります。また音声ファイルを再生中に突然WiiRemoteから切断されたりもします。
以上で「スピーカー利用実験」は終わりです。本書を執筆している時点では安定性、音質面双方で「あまり実用的ではない」と表現しておきましょう。しかしWii本体のゲームプロダクトでは、そこそこの音質で表現力豊かに再生できていますので、任天堂がゲーム開発者に提供している公式開発ツールでは比較的簡単に変換できてしまうのかもしれません。原理的には不可能ではないのでしょうが、搭載しているサウンドチップの解析が進む、もしくは波形再生時など、もっと気を遣わないといけないことがあるのかもしれません。いずれにせよ、本書で扱う範囲としては、ここまででとどめておくことにいたします。
WAVファイルからの再生は上記の通り、残念ながら完璧ではないのですが、不可能とも言い難い状態です。しかし冷静に考えれば、今も既に「Demo.exe」で「A」ボタンや「1」ボタンを押すことで、矩形波やサイン波など単純な波形は出力できるので「目覚まし時計」ぐらいに使えるかもしれません(その場合は切断切れ&電池切れに注意です)。そもそも多くのサウンドプログラマが「それぐらいの波形」だけでいろんな音楽を作っている歴史もありますので、贅沢は言えないでしょう。
さて、これで本章は終わりです。WiiYourself!についてかなりディープに取り組んでみましたが、ついてくることができたでしょうか?コマンドラインプログラムに関する細かいテクニックもかなり扱いました。これを機会にC++によるプログラミングを勉強し直しできた人も多いのではないでしょうか。
しかし、WiiYourself!はコマンドラインプログラムのためだけにあるのではありません。gl.tter氏の3Dシューティングゲームの例を挙げるまでもなく、これはネイティブC言語が使える環境なら、何にでも利用できるライブラリです。具体的にはDirectXやOpenGLといったリアルタイム3DCG、MayaやVirtoolsといったコンテンツ制作ソフトウェアのプラグインなど、かなり幅広く使えるわけです。
また、作者のgl.tter氏はとても個性的な人です。「真面目なハッカー」で、今でもWiiYourself!を更新しています(筆者もかなり手伝っていますが…)。近々新しいバージョンがリリースされることも確実ですし、本章では扱わなかった、赤外線機能も「4点検出+大きさ」も扱えます。スピーカー機能の拡張や、バランスボードのサポートなどもより高度になっていくでしょう。.NETによるWiimoteLibとはまた違った魅力があるプロジェクトですので、読者の皆さんが「利用する→参加する→貢献する」という輪に入ることで、どんどん高機能になっていくでしょう。これからも楽しみなプロジェクトです。
この章では第4章で学んだWiimoteLibによる赤外線センサー機能をさらにすすめて、.NETによるマウス制御プログラム「WiiRemoteMouse」を開発します。実践的な開発プロセスを通して、応用できるWiiRemote利用インタラクティブ技術を体験し、次章の「演習問題」へのステップとします。
ところでマウスといえば、既に第3章で「GlovePIE」を使って高機能なマウスをスクリプティングで実現しました。ここではこれをプロトタイプとして、.NET環境における高度なアプリケーション開発をステップを追って解説していきます。単にGlovePIEでできることを.NETに移植しても面白くないですから、特に、ここでは第4章では扱わなかった.NETの開発手法や独自クラスの作成、外部DLLの取り込み、グラフィックスやユーザビリティを向上させるチューニングなども実際の開発を通して解説します。
今回開発するマウス操作プログラム「WiiRemoteMouse」で利用する基本技術の多くは、いままで学んだ技術の組み合わせです。最終的には、皆さんで新しい機能を追加したくなるようにできており、比べて大きなプログラムになっていくことでしょう。第4章では、小さな機能の確認のために入り交じったコードを書いていましたが、このコーディングスタイルのまま大きなプログラムになっていくと、可読性が悪い、ごちゃ混ぜになったプログラムコードになっていくことが予想されます。このようなプログラムは俗称「スパゲティコード」と呼ばれます。個人での開発はともかくとして、チームでの開発においては、可読性やデバッグのしづらさから、プロジェクトの進行を困難にする原因にもなります。
今回取り組む「WiiRemoteMouse」のように「一気に書き上げることができない中規模〜大規模のプログラム」を開発するときは、まずは一旦、プログラミングから離れ、やりたいことや、実現したいインタラクション、課題など、「仕様」を簡単に書き出します。そこから実装する単位や順番を表などまとめ、関数やクラスといったまとまった処理の単位で開発を進めていくと比較的うまくいきます。
はじめて使う技術の開発であれば「行き詰まってから仕様を再考」という手法でも良いのですが、今回は既に第3章4節「GlovePIEでつくる『高機能マウス』」や、第4章9節「赤外線センサーを使う」において基本となる技術は実験済みですので、今回の「WiiRemoteMouse」の開発では、特に実装する機能と流れ、プライオリティ(優先順位)を、以下のようにまとめてみることができるのではないでしょうか。
プライオリティ | WiiRemote側入力 | 機能 |
---|---|---|
1 | 赤外線とボタンの状態 | フォームに描画 |
2 | 赤外線ポインタの移動 | マウスポインタの移動 |
3 | [A]ボタン | マウス左ボタン |
4 | [A]ボタン長押し | マウス右ボタン |
5 | バッテリー残量 | LEDに電池残量レベル表示 |
もっともっと、盛り込みたい機能もあるとおもいます。例えばランチャーや、キー入力の代わりなど、既に学習した機能を他のボタンに割り当ててみても良いでしょう。表の下の方にプライオリティとともに書き足してみてください。この作業を一般的に「概要設計」といいます。どういうことがしたい、という「概要」を今のうちに設計しておきます。
本書はプログラミングの本なのですが「インタラクティブ技術」については少しだけ深く、体系的にあつかっています。
このセクションで行う「概要設計」とは、まさにインタラクティブ技術の核となる重要なポイントです。今回は「実装したい機能」を表に書き出して、プライオリティをつける…という方法を採用しましたが、これは全ての場合において推奨するやり方ではなく、むしろ「解説のために仕方なく」一般的なソフトウェア開発手法の流れをとりました。
インタラクティブ技術における概要設計に重要なことは「想像すること」です。機能的な制限や「WiiRemoteにこういう機能があるから...」といった「機能指向(functionally-oriented)」の設計ではなく、これから「開発する何か(something)」が、「どんな体験(experience)」をユーザーに与え、この体験を通して「どんな可能性(possibilities)」を感じて、「どんなリアクション(reaction)」につながるのか。まずはここに想像力を使う努力をしてください。つまり、時間をかけてください。
同じ分野で「ユーザーインターフェースデザイン」という考え方があります。人間中心デザイン(HCD:Human Centered Design)やユーザビリティ向上のための評価手法や設計手法などは、近年はWebやGUI(Graphical User Interface)のインターフェース設計などを中心に、以前よりもはるかに体系的に整備されてきています。例えば国際標準規格「ISO13407」に「Human-centred design processes for interactive systems(インタラクティブシステムの人間中心設計過程)」として規定されています。
国際規格というと、難しく感じるかもしれませんが上で書いたとおり、「機能ではなく人間中心」に考えることです。設計の初期で具体的なユーザーや、そこで起こりうる体験を「想像」し、そして実際に作ったモノとユーザーの状況を観察し、繰り返し設計を行う…というプロセスになります。
こういったユーザーインターフェースデザインの改良を企業として実践し、成果を上げている企業もあります。こちらのWebサイトには、この話題に関して役に立ちそうな本が沢山紹介されています。
■ソシオメディア社
https://www.sociomedia.co.jp/category/books
実際に実装する機能とその順番が決まりましたので、次は処理の単位ごとに開発の流れを考えます。これを一般的には「計画」と言います。もちろん始めて体験する人にとって、先のことは見通しがつきませんから「いま想定している流れ」でかまいません。書き出してみます。
「概要設計」や「計画」をほんの少し意識する習慣をつけるだけで、プロジェクトの進行は大きく変わります。ここでは「概要設計」と簡単な計画を作成しました。実際のプロジェクトでは、ここに「期日」、「見通しのついていない技術」、「実験と評価」、「設計の見直し」などを盛り込んでいくと、よりプロジェクトらしくなっていきます。「概要設計」をより詳細な画面イメージや機能、実装する上でのパラメーター、たとえば「長押し」が何秒押すことなのか、などを盛り込んでいくと「詳細設計」になります。
本書はWiiRemoteにおけるプログラミング解説とその独習が目的なのでここまでのレベルにとどめておきます。興味のある人は「プロジェクトマネジメント」について書店の実用書コーナーを探してみると良いでしょう。プログラミングから業務のプロジェクトまでさまざまな実用書があるはずです(検定試験もあります)。実はIT用語のプロジェクトマネジメントと、ビジネス用語のプロジェクトマネジメントは意味するところと扱う範疇がずいぶんと異なりますが、いずれにせよ「立ち読みしてみて役に立つ実用書」なら、買って読んでみても損はないでしょう。
まずは復習もかねて、新しいプロジェクトを作成します。赤外線センサーの入力を受信してフォームに描画するプログラムを作りましょう。第4章9節3で紹介した赤外線4点検出による「座標の描画」プログラムをベースにして、改変しても良いのですが、復習もかねてポイントを流れで説明しますので実際に手を動かしてみてください。
まずC#.NET2008で新規プロジェクトを作成します。「Visual C#」→「Windowsフォームアプリケーション」でプロジェクト名を「WiiRemoteMouse」とします。ソリューションエクスプローラーにある「参照設定」を右クリックし、参照の追加で「最近使用したファイル」から「WiimoteLib.dll」(バージョン1.7.0.0)を選択します。「表示」→「ツールボックス」を選び、「Form1」に対して2つのボタンを配置しTextプロパティを「接続」、「切断」とします。配置したボタン2つをそれぞれダブルクリックして、ボタンを押したときのイベントを自動生成します。また「Form1」にPictureBoxを配置しサイズを「256, 128」に設定します。デバッグ用の文字列を表示する場所として「Label1」を配置します。
まずはスタート地点となる「最小の状態」になるまでコードを整理しましょう。コードの上で右クリックし「usingの整理」→「未使用のusing」の削除とすることで、using宣言にある必要ないクラスは削除することができます。必要なクラス「WiimoteLib」を書き足します。これを最初の一歩とします。
using System; using System.Drawing; using System.Windows.Forms; using WiimoteLib; namespace WiiRemoteMouse { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { } } }
ここまでのステップで間違いは起きないはずですが、確認のため一度[F5]キーで実行しておく癖をつけておくと良いでしょう。正しくフォームが表示されたら終了し、プロジェクト全体を保存します。「ファイル」→「全ての保存」として「C:\WiiRemote」にソリューション名「WiiRemoteMouse」で保存しましょう。
それでは、第4章9節「赤外線センサーを使う」で開発したコードを参考にして、以下のような基本コードを作成しましょう。
using System; using System.Drawing; using System.Windows.Forms; using WiimoteLib; //WimoteLibの使用を宣言 namespace WiiRemoteMouse { public partial class Form1 : Form { Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 Boolean isConnected = false; //WiiRemoteが接続されたか public Form1() { InitializeComponent(); //他スレッドからのコントロール呼び出し許可 Control.CheckForIllegalCrossThreadCalls = false; } //WiiRemoteの状態が変化したときに呼ばれる関数 void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 DrawForms(ws); //フォーム描写関数へ } //フォーム描写関数 public void DrawForms(WiimoteState ws) { //グラフィックスを取得 Graphics g = this.pictureBox1.CreateGraphics(); g.Clear(Color.Black);//画面を黒色にクリア g.Dispose();//グラフィックスの解放 } //接続ボタンが押されたら private void button1_Click(object sender, EventArgs e) { wm.Connect(); //WiiReoteの接続 wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 //レポートタイプの設定 wm.SetReportType(InputReport.IRAccel, true); } //切断ボタンが押されたら private void button2_Click(object sender, EventArgs e) { wm.WiimoteChanged -= wm_WiimoteChanged; //イベント関数の登録解除 wm.Disconnect(); //WiiRemote切断 wm.Dispose(); //オブジェクトの破棄 } } }
コンパイルして動作確認をします。Form1の冒頭でWiiRemoteの接続状態を管理する変数「Boolean isConnected」を宣言しています。今回レポートタイプは「IRAccel」、つまり『赤外線+加速度センサー』とします。「IRExtensionAccel」でも良いのかもしれませんが、ここでは拡張端子を使う予定はありませんので、最適なモードを選択しておきましょう。
ここで、今後大規模になっていくであろうこのプログラムの全体の構造を整理しておきたいとおもいます。この段階でのコーディングは初期化など基本的なところだけにとどめ、個々の機能の実装に入る前に、一拍おきましょう。まずはディープなコーディングを始める前に、簡単なコメントを書いておくことが大事です。さらに事前に「こういう機能を実装したい、する予定」というブロックや関数にまとめておくことで、全体の見通しを良くします。
まず処理のブロック化を学びましょう。Visual Studioでは、プログラムコード中に「#region〜#endregion」と書くことで、コードをブロック(=ひとつのカタマリ)ごとにわけることができます。このブロックごとにVisual Studioコードエディタのアウトライン機能を使用して、展開や折りたたみができるようになります。
使い方も簡単で、ブロックを挿入したいプログラムの行で右クリックして「ブロックの挿入」で「#region」を選択するだけです。ここでは上記の基本コードにおける、フォームの接続ボタンと切断ボタンのブロックに対して「フォームのボタン処理(接続・切断)」という名前をつけましょう「#region」を選んで、名前をつけます。
「ブロックの挿入」を選び、何も設定しないと下のようなコードが挿入されます。
#region MyRegion #endregion
名前をつけ間違えても、場所を間違えても問題ではありません。「#region」はあくまでC#のプログラムに書かれた「補足的な情報」であり、ビルド時、最終的には無視されますから、気軽に使って良いのです。では「フォームのボタン処理(接続・切断)」をまとめるために正しい場所に書いてみましょう。
#region フォームのボタン処理(接続・切断) //接続ボタンが押されたら private void button1_Click(object sender, EventArgs e) { wm.Connect(); //WiiRemoteの接続 wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録 //レポートタイプの設定 wm.SetReportType(InputReport.IRAccel, true); } //切断ボタンが押されたら private void button2_Click(object sender, EventArgs e) { wm.WiimoteChanged -= wm_WiimoteChanged; //イベント関数の登録解除 wm.Disconnect(); //WiiRemote切断 wm.Dispose(); //オブジェクトの破棄 } #endregion } }
表示を折りたたむには、コードの左側(行頭)にある小さな「−」をクリックすると、コードブロックを隠すことができます。
なお「#endregion」を挿入する場所に注意してください。近所にある「}」(関数の終わり)の位置を間違えてもプログラムは動きますし、コードブロックを折りたたむときも全くエラーは起きませんが、自分があとでコードを読むときに大変なので、習慣として気を遣いましょう。
ブロック化の基本を学んだら、次はWiiRemoteの状態が更新されたときに呼ばれるコールバック関数「wm_WiimoteChanged()」をこれから実装する処理の単位でブロックに分解していきます。それぞれの機能単位で関数を作り、ブロックと空(カラ)の関数を用意しておきます。以下の通りにコードをブロック化してみてください。
<前略> #region WiiRemoteの状態が変化したときに呼ばれる関数 void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { if (isConnected == true) { WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 DrawForms(ws); // フォーム描画関数へ IR_Cursor(ws); // 赤外線でマウスカーソル移動 Events(ws); //ボタンイベント処理(ダミー関数) EffectsOut(ws); // LED・装飾 } else { //切断 this.wm.SetLEDs(0); // LED消灯 this.wm.SetRumble(false); // バイブレーター停止 this.wm.Disconnect(); // WiiRemoteと切断 this.wm.Dispose(); // オブジェクトの廃棄 } } #endregion #region ボタンイベント開発用 public void Events(WiimoteState ws) { } #endregion #region フォーム描画関数 public void DrawForms(WiimoteState ws) { //グラフィックスを取得 Graphics g = this.pictureBox1.CreateGraphics(); g.Clear(Color.Black);//画面を黒色にクリア g.Dispose();//グラフィックスの解放 } #endregion #region 赤外線でマウスカーソル移動 public void IR_Cursor(WiimoteState ws) { } #endregion #region LED・装飾 public void EffectsOut(WiimoteState ws) { } #endregion #region フォームのボタン処理(接続・切断) <以下略>
空っぽの関数を書くのは不安があるかもしれませんが、これでも問題なくビルドは通ります。確認しておきましょう。
途中「Events(ws);」について「ダミー関数」とコメントしておきました。これはWiiRemoteがもつそれぞれのボタンイベントを処理する関数を想定しています。後々大規模になることが予想されるのと、クラスとしてあとで再利用できそうなので、Form1.csではなく、別に新しいクラスオブジェクトを作成して実装する予定です。今の段階では『別クラスにしたらいいか、見通しつかないよ!』という状態なので「Events()」という仮の関数で実装し、あとで別のクラスに移植していきます。
「#region」を使うことで、コメントと統合できて、見やすくなりました。Visual Studioでは関数単位も行頭にある「−」をクリックすることで隠す事ができますが、本書の以下の解説ではブロック単位で解説しますので、#region〜#endregionの位置はしっかり設定しておいてください。
ゲーム開発などにも代表される多くのインタラクション技術を使ったプログラムは「スパゲティコード」になりがちです。書いた本人に聞くと、主な理由は「(これに関して)教科書とかないし…」という回答が多いのですが、前のコラムでも紹介したとおり、教科書は沢山出ています。
ポイントは「ユーザーインターフェースデザインには開発のループがある」ということを見極めているかどうか、かもしれません。操作感や体験の印象に直結する「人間中心」の機能を実装するのですから、コーディング→テスト→チューニング→追加機能というループの中に「人間中心」が入ってきます。
このようにインタラクティブ技術とは「人間」が間に入る技術です。そのため「機能」が中心になる業務アプリケーションの一般的開発手法のように「これだ」と決め打ちで仕様を作り、その通りに作っても、実際にでき上がったものを人間が触って、そこからもう一度、理想的なインタラクションになるよう、レビューと設計、フィードバックを繰り返さなければ、完成度の高い体験や、表現したい世界はなかなか伝わりません(とはいえ業務アプリケーションも「銀行のATM」のように人間中心で考えるべき要素は多分にあります)。
なんとなくプログラミングしていると自然とスパゲティ化するインタラクション技術を、混乱無くコードに落とし込めるよう、本書では五月蠅いぐらいに丁寧に説明しています(中級プログラマにとっては回りくどく感じることでしょう!)。本書で解説しているコーディングスタイルも「完璧」というものではありませんし、ここで解説しているブロック化やクラス化も、ただ分割すればいいというものでもありません。
少なくとも言えることは、このようなインタラクション開発プロジェクトの見通しを良くするには、一気に作ったスパゲティを「茹で続ける」よりも、実現したい機能を空っぽのまま配置して、ひとつひとつ実験と評価をしながら順に解決していく方法が役に立つ、ということです。ちょうどコース料理の「皿の構成」を先に考えて、そこから『どういう順番で料理するべきか?』を考えるようなものでしょう。場合によってはコースの途中で、お客さんの反応を見て料理を差し替えることも考えなければなりません。
そういう意味では、ユーザーに対して『いま作るべき料理は、スパゲティかコース料理か?デザートはあるのか?』を、まず作り手が理解している必要があります。ここに時間を割かないと、スパゲティどころか、お客さんの顔も見ないで突然「ドンブリに盛った闇鍋」がでてくることになるかもしれません。
ブロック化することでコードが見やすくなりました。しかしこの状態でプログラムを実行すると、様々な不具合が残っているはずです。慌てず、ひとつづつ片付けていきましょう。
まずはプログラムが起動したあとのフォームのイベント処理を整理しながら実装していきましょう。現在の状態ではWiiRemoteへの「接続」と「切断」が丁寧ではないので、「isConnected」というbool型の変数を用意して、接続状態を管理していきます(WiimoteLibにもこれにあたるプロパティがあってもよさそうなものなのですが、現状のWiimoteLibの設計では個々のアプリケーション側で実装する方がよさそうです)。
#region フォームのボタン処理(接続・切断) //接続ボタンが押されたら private void button1_Click(object sender, EventArgs e) { if (this.isConnected == false) { this.wm = new Wiimote(); //WiiRemoteの初期化 this.wm.Connect(); //WiiRemote接続 this.wm.SetReportType(InputReport.IRAccel, true); //リポートタイプの設定 this.wm.SetLEDs(0); //LED を消す this.wm.SetRumble(false); //バイブレータストップ this.button1.Enabled = false; //接続ボタンを無効 this.button2.Enabled = true; //切断ボタンを有効 this.wm.WiimoteChanged += wm_WiimoteChanged; //コールバックを登録 this.isConnected = true; //接続状態をtrue } } //切断ボタンが押されたら private void button2_Click(object sender, EventArgs e) { if (this.isConnected == true) { this.wm.WiimoteChanged -= wm_WiimoteChanged; //コールバックを削除 this.button1.Enabled = true; //接続ボタンを有効 this.button2.Enabled = false; //切断ボタンを無効 this.isConnected = false; //接続状態をfalse } } #endregion
フォームのボタンが押されたとき、「isConnected」を確認し、もしまだ接続されていないなら、接続処理、リポートタイプの設定、そしてコールバック関数を登録して、変数「isConnected」をtrueにします。
同様に「切断」ボタンが押されたときは既に接続されているWiiRemoteオブジェクト(wm)に登録されたコールバック関数を削除しています。
フォーム上の「接続」や「切断」ボタンは「Enabled=false」とすることで無効化、つまり「押せない状態」にすることができます。このようにどちらかを押すと、どちらかの値が排他的に変わる、部屋の照明のようなボタンを「トグル(toggle)」といいますが、それをソフトウェアで実装していることになります。
本書では原理や動作を中心に解説していますので、ユーザーの不意の終了やWiiRemoteの電池切れ、その他エラー処理などは(要所要所で説明してはいますが)完全には扱い切れていません。皆さんがフリーウェアなど、自分のプログラムを『幅広い、誰か』に使ってもらうには特に気を遣った方が良いでしょう。
習慣として「初期化-終了」、「オブジェクトの追加-削除」はワンセットでコーディングしていくと思わぬミスの軽減に役立ちます。特にC#の場合は、ユーザーフレンドリーに設計された言語環境なので、削除を自動で実施してくれる仕組みがあります。意識して使うことができればエレガントなのですが、逆に「作りっぱなし、削除は…何だっけ」というプログラミングスタイルが板につくと、オブジェクトのスコープ(生存期限)が見えづらくなり、プログラムの動作自体は完成しているのに、残存するオブジェクトのおかげで不明のエラーを実行時に起こしたり、長時間起動しておくとメモリリーク(メモリ漏れ)を起こし、挙動が突然遅くなったり、クラッシュしたりする『あとあと手のかかるプログラム』を生み出します。
特にオブジェクトの終了や破棄は忘れがちです。WiimoteLibのように誰かが作ったライブラリの場合は単に「終了」というAPIがあっても、内部で何をやっているかわからない場合もあります。コーディングの流れ上「いまここで終了して良いかわからない」といったときもあるでしょう。そんなときは「//To俺:ここで破棄?」など『未来の自分宛』にコメントを入れておくことで、後々のコード整理の時に見事に役に立ったりします。
次は赤外線センサーを利用して、マウスポインタを動かす部分の実装をします。いきなりマウスを動かす部分を実装してもいいのですが、赤外線の状況が見えないと開発が難航しますので、まずはフォーム描画関数「DrawForms()」に手を加えて赤外線がWiiRemoteの視界に入ったら、グラフィックスと文字で測定値を表示するようにします。
#region フォーム描画関数 public void DrawForms(WiimoteState ws) { //グラフィックスを取得 Graphics g = this.pictureBox1.CreateGraphics(); g.Clear(Color.Black);//画面を黒色にクリア //もし赤外線を1つでも発見したら if (ws.IRState.IRSensors[0].Found) { //赤色でマーカ0を描画 g.FillEllipse(Brushes.Red, ws.IRState.IRSensors[0].Position.X * 256 , ws.IRState.IRSensors[0].Position.Y * 128 , 5, 5); //青色でマーカ1を描画 g.FillEllipse(Brushes.Blue, ws.IRState.IRSensors[1].Position.X * 256, ws.IRState.IRSensors[1].Position.Y * 128, 5, 5); } g.Dispose();//グラフィックスの解放 label1.Text = "IR[0] " + ws.IRState.IRSensors[0].RawPosition.ToString() + "\nIR[1] " + ws.IRState.IRSensors[1].RawPosition.ToString(); } #endregion
赤と青、2つのポインタを小さめに表示しています。フォームの「label1」に表示されるテキストや、座標の方向など「見え方」について、お好みで改良していただいてかまいませんが、最後に「装飾」として大幅拡張する予定です。このステップではあまり気にせず、先に進みましょう。
次はマウスポインターを赤外線で動かせるようにします。まず、初期化コードの中に、変数「ScreenSize」を追加しましょう。
Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 System.Drawing.Point ScreenSize; //|画面サイズを格納 Boolean isConnected = false; //WiiRemoteが接続されたか
次に、関数「IR_Cursor」を実装します。これは赤外線の位置にあわせて、マウスポインタを移動させるコードです。
#region 赤外線でマウスカーソル移動 public void IR_Cursor(WiimoteState ws) { ScreenSize.X = Screen.PrimaryScreen.Bounds.Width; //画面サイズ横幅 ScreenSize.Y = Screen.PrimaryScreen.Bounds.Height; //画面サイズ縦幅 //もし赤外線を1つ発見したら if (ws.IRState.IRSensors[0].Found) { //赤外線座標(0.0〜1.0)を画面サイズと掛け合わせる int px = (int)(ws.IRState.IRSensors[0].Position.X * ScreenSize.X); int py = (int)(ws.IRState.IRSensors[0].Position.Y * ScreenSize.Y); //X座標を反転させる px = ScreenSize.X - px; //マウスカーソルを指定位置へ移動 System.Windows.Forms.Cursor.Position = new System.Drawing.Point(px, py); } } #endregion
取得した赤外線マーカーの1個目のX,Y座標をマウスカーソルの位置に設定しています。System.Drawingに用意されている2次元の点を扱う型Point(px,py)をつかって、マウスカーソル位置を変更するためSystem.Windows.Cursor.Poitionに代入しています。
早速実験してみましょう。WiiRemoteをBluetooth接続し、センサーバーなどの赤外線光源を準備してから[F5]キーを押してデバッグ開始します。表示されたフォームの「接続」ボタンを押し、問題なく接続されたら、WiiRemoteを赤外線光源に向けてください。
少なくとも1点でも赤外線が検出されるとフォーム内に赤いマーカーが表示され、Windowsのマウスカーソルが手の動きにそって移動します(右に動かせば、右にマウスカーソルが動くはずです)。赤外線を検出している間は、PCに接続されているマウスを触っても思い通りに動かすことはできません。
なお、実行時にマウスカーソルがバタバタする場合があります。赤外線センサーの状態や複数のマーカーがWiiRemoteの視界に入っていることに起因する不安定な検出状態によるものです。WiiRemoteとセンサーバーとの距離を2m程度まで離してみたりすると安定になりますが、後ほどコードの見直しとチューニングを実施しますので、特に今は気にしなくてもよいでしょう。
終了する場合は、赤外線を検出しないようにする(センサー部分を下にして立てるとお洒落です)と、マウスの制御が戻りますので。「切断」ボタンを押してから終了させてください。マウスカーソルに頼らず、[TAB]キーを数回押し、[Enter]キーで「切断」を入力する事でも、簡単に終了することができます。
次はボタンイベントです。先ほどは空っぽにしていたボタンイベントを処理するダミー関数「Events()」を実装していきましょう。
ボタンイベントと簡単に言っても、WiiRemoteのボタンはたくさんありますし、マウスやWiiRemoteに付いているデジタル入力ボタンには以下の「3つの状態」があります。
【DOWN】…ボタンを押した状態(Push)。
【HOLD】…DOWN後、ボタンを押しっぱなしにしている状態(Press)。
【UP】…ボタンを離した状態(Release)。
これらを内部できっちり処理しないと、ダブルクリックなどを検出するのは難しくなります。
概要設計に従って、WiiRemoteの[A]ボタンに対して以下のマウス動作を割り当てることにします。
・Aボタンが押されると(DOWN)、マウスの左クリックを発行します。
・Aボタンが長押しされると(HOLD)、マウスの右クリックを発行します。
・Aボタンが離されると(UP)、マウスボタンを押していない状態にします。
「長押し(HOLD)」は1秒間押しっぱなしにすること、としておきましょう。
まずは確実に長押しイベントが拾えるように「メッセージボックス」を使って確認します。
<初期化部分に追加> //ボタンイベント開発用 bool isDown; int StartTime, PressTime = 1000; string State = ""; <中略> #region ボタンイベント開発用 public void Events(WiimoteState ws) { if(ws.ButtonState.A) { if (isDown == false) { //もしも初めてボタンが押されたとき StartTime = System.Environment.TickCount; //押された時間を記録 State = "DOWN"; isDown = true; } else { //押されている時間がPressTimeより長ければHOLD if ((System.Environment.TickCount - StartTime) >= PressTime) { State = "HOLD"; //押され続けている //メッセージボックスを表示 MessageBox.Show(State); } } } else { if (isDown == true) { //ボタンが離された State = "UP"; isDown = false; } } } #endregion
この段階でテストをしてみましょう。プログラムを起動して接続し、[A]ボタンを押しっぱなしにして1秒まつと、「HOLD」と書かれたメッセージボックスが表示されます。
MessageBox.Show()で利用できる「メッセージボックス」はこの種のデバッグや開発に非常に役に立ちます。ここではもう確認が終わりましたので、この行はコメントアウトもしくは削除してしまって問題ありません。
プログラムの動作を確かめるために、デバッグが必要になることがあります。Visual Studioの標準機能では[F9]を押すことでブレークポイントを挿入することができます。しかしプログラムを止めるまでもなく、ちょっとした値を見たいときなどもあります。
今回紹介した「メッセージボックス」以外のテキスト表示の方法として、C#では「Console.WriteLine()」を使ってメッセージを出力することができます。この出力結果はVisual Studio上の標準出力「表示(V)→出力(O)」で見ることができます。(なお同様の関数がC++にもありますが、なぜかVisual C++上で出力ウィンドウを見ても出力されないようです…)。
このようなちょっとしたテクニックは知っていると便利です。ただし実行時はパフォーマンス低下を産む場合もあるので、最終的なバージョンでは忘れずにコメントアウトしておくか、「#if DEBUG〜#endif」ディレクティブを使うことでデバッグ版だけコードを活かすこともできます。
デバッグテクニックは、インタラクションを向上させる為のこまめな実験や評価、チューニングに非常に役に立ちます。
続いて、WiiRemoteのボタンダウンにあわせて、マウスボタンのイベントを発行します。プログラムが長くなってしまいますので、これからボタンイベントの検出を別の.csファイルの別クラスに移植します。
まずVisual Studioの「プロジェクト」から「新しい項目の追加」(Ctrl+Shift+A)を行います。
「テンプレート」で「クラス」を選びファイル名を「ButtonEvents.cs」として「追加」を押します。プロジェクトエクスプローラーに「ButtonEvents.cs」が追加され、以下のような初期コードが表示されるはずです。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WiiRemoteMouse { class ButtonEvents { } }
このままでは何もおきませんので、いままでコーディングの中心になっていた「Form1.cs」からEvents()関数のコードと変数を移植します。「#region」も忘れずに記述しておきましょう。
using WiimoteLib; namespace WiiRemoteMouse { class ButtonEvents { bool isDown; int StartTime, PressTime = 1000; string State = ""; #region ボタンイベント処理 public void Events(WiimoteState ws) { if (ws.ButtonState.A) { if (isDown == false) { //もしも初めてボタンが押されたとき StartTime = System.Environment.TickCount; //押された時間を記録 State = "DOWN"; isDown = true; } else { //押されている時間がPressTimeより長ければHOLD if ((System.Environment.TickCount - StartTime) >= PressTime) { State = "HOLD"; //押され続けている //メッセージボックスを表示(確認用) System.Windows.Forms.MessageBox.Show(State); } } } else { if (isDown == true) { //ボタンが離された State = "UP"; isDown = false; } } } #endregion } }
移植したコードはForm1.csから削除、もしくはコメントアウトします。
<前略> namespace WiiRemoteMouse { public partial class Form1 : Form { Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 ButtonEvents wbe = new ButtonEvents(); //ボタンイベントクラスを作成 <以下の初期化は削除してかまいません> /* //ボタンイベント開発用 bool isDown; int StartTime, PressTime = 1000; string State = ""; */ <ここで関数名の前にクラス名「wbe.」を追加します> wbe.Events(ws); //ボタンイベント処理 <以下略>
この段階でかならず動作試験を行ってください。[A]ボタンを長押しすると、メッセージボックスが表示されるはずです。
「ButtonEvents wbe = new ButtonEvents();」によってwbeというクラスを新規作成し、移植した関数(メソッド)「wbe.Events(ws);」をボタンイベントの処理として呼んでいます。
このようにソースコードを移植した後も問題なく実行できれば、複数のクラスをまたがるプログラムの作成に成功したといえます。
これで、ボタンイベント部分を別のクラスが記述されたソースコード「ButtonEvents.cs」に分けることに成功しました。いままでは全てForm1.csのForm1クラスに記述していたのですが、プログラムが巨大になったときや、複数のプログラマによるチームで開発するときには、適切なタイミングでクラスやファイルをわけていくことが重要です。
次はWiIRemoteのボタンを押されたときに、マウスボタンのクリックイベントが発行されるべきパートのコードを書いていきます。この「マウスボタンイベントの発行」は単にマウスカーソルを動かすときと異なり少々複雑になります。まず.NET Framework3.5ではマウスカーソルの位置は変更できても、クリックするイベントを発行できるAPIが用意されていないようです。そこで従来から存在するWin32プラットフォームSDKのWindowsユーザーインターフェースサービス「user32.dll」というDLLに含まれる「SendInput()」というAPIとSendInput()のための構造体を取り込むことで、この機能を実現します。
DLLインポートと構造体は、ある程度形式に沿った記述が必要です。ここでは「SendInput()」というAPIを取り込み、その関数の引数となる構造体「INPUT」とINPUTが利用するマウスイベントの詳細を記述する構造体「MOUSEINPUT」を取り込みます。
using WiimoteLib; //DllImportに必要なusingを追加 using System; using System.Runtime.InteropServices; namespace WiiRemoteMouse { class ButtonEvents { bool isDown; int StartTime, PressTime = 1000; string State = ""; #region DLLインポート [DllImport("user32.dll")] //DLL読み込み extern static uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); [StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public MOUSEINPUT mi; } [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public int mouseData; public int dwFlags; public int time; public IntPtr dwExtraInfo; } #endregion <以下略>
この構造体はWin32(C++)のヘッダファイルである「WinUser.h」に記述されているものです。多少面倒ですが、間違えずに記述してください。なお、この構造体の定義をおろそかにすると、SendInputが正しく動いてくれません。
C#でマウスに希望のイベントを発行するときは、以下のようにしてイベントを送信します。
input[0].mi.dwFlags = 0x0002; //左マウスダウン SendInput(1, input, Marshal.SizeOf(input[0])); //マウスイベントを送信
「Marshal」はアンマネージコードのメモリ割り当てのためなどに用意されたクラスです。DLLと構造体のインポートは記述さえ間違えなければ特に気負う必要はありません、そのまま下に続く、ボタンイベントの実装を行いましょう。
<コメントの頭に「|」がついている箇所が新規追加部分です> #region ボタンイベント処理 public void Events(WiimoteState ws) { INPUT[] input = new INPUT[1]; //|マウスイベントを格納 if (ws.ButtonState.A) { if (isDown == false) { //もしも初めてボタンが押されたとき StartTime = System.Environment.TickCount; //押された時間を記録 State = "DOWN"; isDown = true; input[0].mi.dwFlags = 0x0002; //|左マウスダウン SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 } else { //押されている時間がPressTimeより長ければHOLD→右クリック if ((System.Environment.TickCount - StartTime) >= PressTime) { State = "HOLD"; //押され続けている input[0].mi.dwFlags = 0x0008; //|右マウスダウン SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 } } } else { if (isDown == true) { //ボタンが離された State = "UP"; isDown = false; input[0].mi.dwFlags = 0x0004; //|左マウスアップ SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 input[0].mi.dwFlags = 0x0010; //|右マウスアップ SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 } } } #endregion } }
各イベントに対して「input[0].mi.dwFlags = 0x0004」とすることでボタンの押されている状態を発行することができます。この「0x0002」や「0x0004」という16進数表現のフラグ(dwFlags)はプラットフォームSDKで定められている定数で、「WinUser.h」で確認することができます。他にも右クリックやホイールなどのデータも送ることができます。
動作 | 意味 | 値 |
---|---|---|
MOUSEEVENTF_MOVE | マウスが移動 | 0x0001 |
MOUSEEVENTF_LEFTDOWN | 左ボタンが押された | 0x0002 |
MOUSEEVENTF_LEFTUP | 左ボタンが離された | 0x0004 |
MOUSEEVENTF_RIGHTDOWN | 右ボタンが押された | 0x0008 |
MOUSEEVENTF_RIGHTUP | 右ボタンが離された | 0x0010 |
MOUSEEVENTF_MIDDLEDOWN | 中央ボタンが押された | 0x0020 |
MOUSEEVENTF_MIDDLEUP | 中央ボタンが離された | 0x0040 |
MOUSEEVENTF_WHEEL | ホイールが回転 | 0x0800 |
これらのAPIや構造体のフォーマットは、マイクロソフトのドキュメントやSDKに含まれるヘッダファイルで与えられています。過去脈々とした長い歴史を持つ、Win32プラットフォームにおけるC++を解説している個人のホームページに掲載されたサンプルなども役に立ちます。C#のコーディングをしているからといって「ああこれはC++のサンプルだ、私には関係ない…」と思う必要はないのです!
.NET世代のC#プログラマにとってアンマネージコードの取り込みは、.NET Frameworkに保護されていない「未知の恐怖」があるかもしれませんが、慣れてしまえば便利なものです。今回のようなSendInputはアンマネージドな実装を頼らなくても、将来的に.NET Frameworkに取り込まれ、気軽に使えるようになることを望みますが…。
■SendInput関数
http://msdn.microsoft.com/ja-jp/library/cc411004.aspx
■mouse_event関数
http://msdn.microsoft.com/ja-jp/library/cc410921.aspx
これで基本機能はほぼ完成です。さっそく実行してみましょう。プログラムを起動してWiiRemoteをBluetooth接続し「接続」とすると、視界に入った赤外線によってマウスカーソルを動かせるようになります。
[A]ボタンを押すとマウスの左クリック、1秒間長押しすると右クリックになります。ボタンから手を離すと、左右両方のマウスボタンを離した状態(Up)になります。
以上で、最初に「概要設計」で設計した全ての機能の実装が終わりました。お疲れ様でした!
今回のWiiRemoteMouseではC#.NETのみで開発し、第4章のようにC++版を扱いませんでしたが、実はC++.NET環境はこのような非.NET混在環境(アンマネージドコード)に強く、冒頭で「windows.h」を#include宣言するだけで、関連する構造体やAPIを利用できるようになります。またC#のコードで「0x0002」と書いていたような定数も「MOUSEEVENTF_LEFTUP」として表現できるよう、すべて自動で取り込んでくれます。そのままビルドすると「関数の実体が見つからない」というエラーが出るのですが、「プロジェクトのプロパティ」→「構成プロパティ」→「リンカ」→「入力」→「追加の依存ファイル」を表示して「親またはプロジェクト規定値からの継承」にチェックを入れることで、ビルド時に実際の関数をリンクしてくれるようになります。
概要設計で設計した機能は実装し終わりましたが、これで終わりではありません。むしろここからがはじまりです。
まずユーザービリティの向上のために、ここで「自分以外の誰か」に触ってもらってください。周りに誰もいないときは、自分自身で実際使うであろうユーザー(UI専門用語で「ペルソナ」といいます)を想像しながら触ってみます。何か気がついたことがあったら、どんどんメモしていきます。
こういう作業に時間を費やすと、コーディングだけしている時には見えないことが見えてきます。
例えば起動直後、『label1』と表示されているところは、このプログラムをはじめて触る人はなんだかわかりません。ここはlabel1のプロパティを『「接続」ボタンを押してください』とするべきですね。もしかしたらもっと詳細に「WiiRemoteをお使いのBluetoothスタックで接続してから、」と書き加える必要もあるかもしれません。フォームの上部に表示されている「Form1」のTextプロパティも「WiiRemoteMouse」とするべきかもしれませんね。想定しているユーザー「ペルソナ」が見えていなければ、どこまでも無骨なプログラムになってしまいます。
this.label1.Text = "WiiRemoteをお使いのBluetoothスタックで接続してから、\n「接続」ボタンを押してください"; this.Text = "WiiRemoteMouse";
このようなフィードバック開発は何度繰り返しても「終わり」というものはありません。しかしこの繰り返しループにかける時間で、プログラムの完成度はどんどんと高まっていきます。
ユーザーテストをやってみると、この先、GlovePIEで実装したように、さまざまなボタンなどに沢山のアクションを割り当てていきたくなるとおもいます。残念なことに、ここから先はどんどんWiiRemoteとは直接関係ない話になってしまいますので適度に割愛しながら解説したいと思います。
例えば、ボタンアクションの開発について、[B]にもマウス右ボタンを割り当てたいとします。その場合、
if (ws.ButtonState.B) { input[0].mi.dwFlags = 0x0008; //|右マウスダウン SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 } else { input[0].mi.dwFlags = 0x0010; //|右マウスアップ SendInput(1, input, Marshal.SizeOf(input[0])); //|イベントを送信 }
このように書き加えれば良いわけです。しかしこのように個々のボタンイベントについてif文で実装していくと、[A+B]などのボタンコンビネーションアクションなども加わっていくと、さらに複雑になっていきます(バグも増えます)。せっかくこの部分をクラス化したので、うまく再利用できる方法を考えたいところです。
本書の著者のひとりである小坂先生は、先ほど実装したStateのような文字列を拡張して「押されているキーを文字列として扱う」というアイディアで、以下のような方法で新しいクラスを設計してみました。これが正解かどうかは場合によりけりですが、良いアイディアだとおもいますので簡単に解説します(完成品は小坂研究室のHPからダウンロードできます★)。
まず現在のイベントクラスに「ButtonEvent」というクラスを追加します。
public ButtonEvent(String buttonName) { this.isDown = false; //初期値はfalse this.State = ""; //初期値は"" this.onButtonTime = 1000; //長押し時間 this.Flg = false; //初期値はfalse this.ButtonName = buttonName; //ボタンの名前を取得 }
個々のボタンのキーとして文字列を定義しておきます。
Aボタン → A Homeボタン→ Home Bボタン → B ↑ボタン → Up 1ボタン → One ↓ボタン → Down 2ボタン → Two ←ボタン → Left -ボタン → Minus →ボタン → Right +ボタン → Plus
そして[A]ボタンについて管理する場合、「A」という文字列を使ってButtonEventクラスをnewして「ButtonA」というクラスオブジェクトを作成します。この方法で、各々のボタンについてイベントを管理するクラス群ができあがります。
public ButtonEvent ButtonA = new ButtonEvent("A"); public ButtonEvent ButtonB = new ButtonEvent("B"); public ButtonEvent ButtonUP = new ButtonEvent("Up"); public ButtonEvent ButtonDOWN = new ButtonEvent("Down") <...以下すべてのボタンについてnewします>
さらにButtonEventクラスに「GetOnButton(WiimoteState ws)」というStringを返すメソッドを用意し、押されたボタンのテキストを返します。以下のようにコーディングすることができます。
public String GetOnButton(WiimoteState ws) { //Aが押された → " A" //Bが押された → " B" //A,Bが押された → " A B" //A,B,1,2が押された → " A B One Two" String onButtons = ""; if (ws.ButtonState.A) { onButtons += " A"; } if (ws.ButtonState.B) { onButtons += " B"; } if (ws.ButtonState.One) { onButtons += " One"; } <以下全てのボタン> return onButtons; //押されたボタンonButtonsを返す }
このメソッドを、
if (this.ButtonName.Equals(this.GetOnButton(ws).Trim()))
…と使うことで、押されているボタンが注目したいボタンであるButtonNameと同じかどうか調べる事ができます。なお前後のスペースを除去してくれるメソッドTrim()やEquals()は、String型を継承しているので、追加実装なしで利用できます。
switch (ButtonA.onButton(ws))
とすることで、switch文を用いてそれぞれの[Down]、[Up]、[Hold]対応するコードを書いていくこともできます。
単純にif文の組み合わせで書いていく方法も悪くはないのですが、その後のチューニングでスパゲティ化を招き、ユーザーテスト時に「長押しの時間を変えたい」といった細かいチューニングの繰り返しに苦しめられることになるかもしれません。クラスを使った汎化、関数表現化ができればこういった問題もずいぶんと整理が付くようになります。
マウスとしての基本機能が完成すると、ユーザーテストによっては「キーボード機能も欲しい」という評価にもなるでしょう。
.NETには便利なAPI「SendKeys.SendWait()」というメソッドがあり、ここに発行したいキーボード入力を文字列を渡すことで、キーボード入力を発行することができます。例えば「Alt+F4」のような特別なキーが混ざった入力も発行できます。
SendKeys.SendWait("%{F4}");
カーソルキーやCtrlキーなどほとんどのボタンコンビネーションはこの方法で作り出すことができます。詳細はSendKeysについて調べると、全ての記述ルールを見つけることができるでしょう。
また使用環境によっては「このツール自身の表示を隠したい」という要求もあると思います。そんなときは、以下のコールでこのプログラムをプログラム自身から最小化することができます。
this.WindowState = FormWindowState.Minimized;
このように.NETの機能をフル活用し、WiiRemoteのイベントに対してマウスとキーボードの入力を割り当てたり、既に「ランチャー」で学んだアプリケーションの実行などを組み合わせたり、時には外部のAPIも活用しながら自分で好きな機能を盛り込んで、より誰かの役に立つ「WiiRemoteMouse」を作りこんでみてください。
インタラクション技術、特にGUI(グラフィカルユーザーインターフェース)にとって「見た目」は『重要な機能』です。ここでは「装飾要素」と呼んでいますが、決して軽く見積もっているのではありません。関数化をすることで、初期で作り込むのを避けたことを思い出しましょう。これは「概要設計」で基本機能を素早く実現し、ユーザーテストの繰り返しのループにおいて「十分な時間をかけて装飾したい」という目的によるものです。
もしこの開発の順番が逆だと、開発の初期で「見た目」にばかり時間を遣い、必要な技術課題の解決も済まないまま、ひいてはプロジェクトの進行を初期の段階でつまずかせることになります。「見た目が重要」だからこそ、いまユーザーテストのループの中で、思う存分実装しましょう。
まずはちょっとお洒落にLEDの表示部分を実装します。
イメージとしてはLEDにはバッテリーの残量を{25%以下、50%、75%、75%以上}といった4段階で表示したいのでSetLEDs()関数を利用して、
wm.SetLEDs(1); //□■■■ 25%以下 wm.SetLEDs(3); //□□■■ 50% wm.SetLEDs(7); //□□□■ 75% wm.SetLEDs(15); //□□□□ 75%以上
このように表現していくこともできるでしょう。
しかし「switch〜case」文を使ってロジック(論理)で表現していく方法に対して、数式で1行にまとめるエレガントな方法もあります。今回は1行で書ける数式で実装してみます。
#region LED・装飾 public void EffectsOut(WiimoteState ws) { //25%ずつLEDを表示させる wm.SetLEDs((int)Math.Pow(2.0f, (int)(ws.Battery / 25) +1 ) - 1); } #endregion
たった1行の式ですが、以下のような意味を持っています。
バッテリーの値は[0<Battery<100]のfloat型で手に入りますので、それを25で割って、整数化(小数点以下を切り落とし)します。するとバッテリーの残量に応じて「0,1,2,3」という整数になります。nを自然数(1,2,3,...)とするとき、2のべき乗[2^n]は「2,4,8,16,...」という値をとりますので、そこを-1してあげることで、必要な「1,3,7,15」という4つのLED出力用の整数を得ることができます。
このように法則性があるものは可能な限り数式、つまり関数で表現できるようにするクセをつけると、コーディングも驚くほど短くなりますので、デバッグするときも見落としが減ります。何より学校で学んだ数学が非常に役に立ちます。「数学」というよりも「算数パズル」のようなものなので、無理して関数化するのではなく『楽しんで解いてみよう!』というところでしょうか。
インタラクティブ技術をプログラム化するとき、例えばゲーム開発や研究開発において「とりあえず完成した状態」から、そのチューニングをしていく上で関数化、言い換えれば「経験的なロジックを数学で扱う習慣」をつけることは非常に重要です。
本書に掲載しているプログラムは紙面ですので、できるだけ掲載するコードの行数に無駄が無く、かつよりよい理解のために流れを追いやすく掲載するようにしています。これは筆者が小〜中学生の頃流行していた「マイコンBASICマガジン」(電波新聞社)の考え方を採用しています。当時、良質なプログラムの主な流通方法はWebや電子メールではなく「紙面」でしたので『いかに短くて美しいコードを書くか』という、今から考えると恐ろしくストイックなコーディングスタイルが流行していたわけです。加えて、BASICマガジンは月刊誌でしたので、適度な締切や、編集部の妙なノリが、品質な高い「みんなで作っていく文化」を作り出していました。
このような「集合知」や文化…もっと高尚な言い方をすれば「集合知による創発的コーディング」、最近の流行で表現すれば「『ニコニコ動画・技術部』で作ってみた」がかなり近い感覚でしょう。『ニコ動』でのインタラクティブ技術に関する注目は非常に高いものがあります(みんなこんな事も知らないのか…と驚くことも多いのですが!)。
そして本書の読者が『ニコ動文化』に貢献できることも大きいとおもいます。皆さんもぜひ、いろんな作品や活動を映像化して、衆目にさらしてみるとよいでしょう。「すげwww!」と賞賛されたあとに、勢い余って公開したプログラムが「何このスパゲティコード!!」とガッカリされないように、再利用しやすく、他人の勉強になるコーディングスタイルを極めてみてみるのもカッコイイとおもいます。
まずユーザービリティ向上にも関係する要素として、赤外線の品質を向上させたいとおもいます。現状のプログラムだとマウスカーソルはガタガタしているはずです。赤外線を手で隠したりして、よく様子を観察するとわかるのですが、センサーバーの2点のLEDのうち「どちらか1点」がそのときの状況で採用されているのが原因ではないでしょうか。現状のプログラムでは「最初に見えた1点」をマウスカーソルの座標に変換していますので、細かい操作をしようとすると、隣にある「2つ目の赤外線」邪魔をして、値が飛んでしまい安定感がなくなっているのです。
このような状況に対するひとつの解としては「2つLEDが見えたときは右にある赤外線を採用」といったロジックでルールを作ることです。
#region 赤外線でマウスカーソル移動 public void IR_Cursor(WiimoteState ws) { ScreenSize.X = Screen.PrimaryScreen.Bounds.Width; //画面サイズ横幅 ScreenSize.Y = Screen.PrimaryScreen.Bounds.Height; //画面サイズ縦幅 //赤外線座標(見えたときだけ更新) float Ix1 = 0.5f, Iy1 = 0.5f, Ix0 = 0.5f, Iy0 = 0.5f; float Ix, Iy; //赤外線座標の平均 int px, py; //最終的なマウスカーソルの位置 if (ws.IRState.IRSensors[1].Found) { Ix1 = ws.IRState.IRSensors[1].Position.X; Iy1 = ws.IRState.IRSensors[1].Position.Y; Ix0 = ws.IRState.IRSensors[0].Position.X; Iy0 = ws.IRState.IRSensors[0].Position.Y; //Ix1,Iy1に大きい方(左)を格納したい if (Ix1<Ix0) { Ix0 = Ix1; Iy0 = Iy1; Ix1 = ws.IRState.IRSensors[0].Position.X; Iy1 = ws.IRState.IRSensors[0].Position.Y; } Ix = Ix0; Iy = Iy0; //ここで平均をとっても良いだろう px = (int)(ScreenSize.X * (1 - Ix)); //X座標は反転 py = (int)( Iy * ScreenSize.Y); //マウスカーソルを指定位置へ移動 System.Windows.Forms.Cursor.Position = new System.Drawing.Point(px, py); } } #endregion
実行してみると(環境にもよりますが)バタバタ感は多少は改善されているのではないでしょうか。このパートはこのようなチューニング作業、改善を繰り返すことで、ユーザービリティ向上に大きく貢献できる可能性があります。例えば、赤外線が見えなかったときの処理として、過去の値を使ったり、平均をとったり、履歴をとったり…という処理を上のコードを基本として追加することができます。
環境や状況によってより多くテストをし、アイディアを盛り込んでいくことで、不安定な動作を軽減することができるでしょう。
PictureBoxにカッコイイ文字列を表示したい!と思うこともあるでしょう。特に先ほどの赤外線品質の向上をチューニングする上では、各々のマーカーの値が表示できると、作業がはかどります。しかしメッセージボックスやラベル文字列では情報の量が多い上に速すぎて、役に立ちません。そこで、今回は特にRawPosition(生の測定値)をPictureBoxに画像として描画します。これはプログラマにとっては「作業の効率化」の一環ですが、「見た目にカッコイイ」という「機能」も併せ持ちます。
#region フォーム描画関数DrawString版 public void DrawForms(WiimoteState ws) { //グラフィックスを取得 Graphics g = this.pictureBox1.CreateGraphics(); Font drawFont = new Font("Arial", 9); //|フォントを指定 SolidBrush drawBrush = new SolidBrush(Color.White); //|色は白 String drawString = "Text"; //|描画文字列 //|描画位置を扱うPoint型変数 System.Drawing.Point pos = new System.Drawing.Point(0, 0); int irsize; //|検出した赤外線マーカーのサイズ g.Clear(Color.Black);//画面を黒色にクリア //もし赤外線を1つでも発見したら if (ws.IRState.IRSensors[0].Found) { //マーカ0の描画 pos.X = (int)(ws.IRState.IRSensors[0].Position.X * 256); pos.Y = (int)(ws.IRState.IRSensors[0].Position.Y * 128); irsize = ws.IRState.IRSensors[0].Size + 5; g.FillEllipse(Brushes.Red, pos.X, pos.Y,irsize, irsize); drawString = "[" + ws.IRState.IRSensors[0].RawPosition.X + ", " + ws.IRState.IRSensors[0].RawPosition.Y + "]"; g.DrawString(drawString, drawFont, drawBrush, pos); //マーカ1の描画 pos.X = (int)(ws.IRState.IRSensors[1].Position.X * 256); pos.Y = (int)(ws.IRState.IRSensors[1].Position.Y * 128); irsize = ws.IRState.IRSensors[0].Size + 5; g.FillEllipse(Brushes.Blue, pos.X, pos.Y, irsize, irsize); drawString = "[" + ws.IRState.IRSensors[1].RawPosition.X + ", " + ws.IRState.IRSensors[1].RawPosition.Y + "]"; g.DrawString(drawString, drawFont, drawBrush, pos); } g.Dispose();//グラフィックスの解放 label1.Text = "IR[0] " + ws.IRState.IRSensors[0].RawPosition.ToString() + "\nIR[1] " + ws.IRState.IRSensors[1].RawPosition.ToString(); } #endregion
ここではDrawForm()は「DrawFormOrg()」としてコピー&ペーストでそのまま残して、テキスト表示用機能を追加しています。「マーカー0の描画」というあたりから、大幅に書き換えていますが、より読みやすくなっているはずです。せっかくPictureBoxに情報を表示するので、楕円を描くFillEllipse()の第4、第5引数に、検出された赤外線の大きさを与えて、意味をもたせています。
実行してみましょう。センサーバーに近づけるとマーカーを示す円が大きめに描画されます。このように、赤外線の様子をじっくり観察できるので、ユーザーの動作も理解しやすくなり、意外なチューニングのヒントになります。
テキストが表示できるようになると、便利で格好いいことがいろいろやれるようになりますので活用しましょう。
最後に、WiiRemoteの「傾きを表示」できるようにします。あのWii本体でよく出てくる「指カーソル」で表示されているように、WiiRemoteの傾きを画面で表現できると、よりWiiRemoteらしくなります。しかしあの指ポインタの傾きは加速度センサーによるものではないようです。加速度センサーの値を使わなくても、センサーバーからの2つのマーカー座標が取得できているなら「その2点をつなぐ線の傾き」で表現することができます。
また、せっかくの.NETによる開発ですから、ネットワークを使った技術も紹介します。具体的には「指カーソル」に使う画像を、ハードディスク内のファイルではなくインターネット上から取得して利用します。
こちらの画像ファイルは著者のホームページ★「http://akihiko.shirai.as/projects/WiiRemote/finger.bmp」にておいてあるものです。Internet ExplorerなどのブラウザでこのURLを指定すると、画像を表示できます。ちなみにこの指カーソル画像はペイントを使って5分ぐらいで描いたものです野で、皆さん自身で用意していただいてもかまいません。ただし背景を「透明」に抜くために、決めた1色で塗っています(通称「抜き色」)。
namespace WiiRemoteMouse { public partial class Form1 : Form { Wiimote wm = new Wiimote(); //Wiimoteクラスを作成 ButtonEvents wbe = new ButtonEvents(); //ボタンイベントクラスを作成 Bitmap bmp; //|指ポインタ描画用 System.Drawing.Point ScreenSize; //画面サイズを格納 Boolean isConnected = false; //WiiRemoteが接続されたか public Form1() { InitializeComponent(); //グラフィックス下ごしらえ String url = "http://akihiko.shirai.as/projects/WiiRemote/finger.bmp"; using (System.Net.WebClient wc = new System.Net.WebClient()) using (System.IO.Stream st = wc.OpenRead(url)) bmp = new Bitmap(st); // ※もちろんハードディスクから読むことも可能 // bmp = new Bitmap("c:¥¥WiiRemote¥¥yubi.png"); bmp.MakeTransparent(bmp.GetPixel(0, 0)); //抜き色の指定 <中略> #region フォーム描画関数Finger版 public void DrawForms(WiimoteState ws) { <中略> double radians, angle = 0.0f; //赤外線が2つ見えたらその中間をとる if (ws.IRState.IRSensors[1].Found) { pos.X = (int)(ws.IRState.IRSensors[0].Position.X * 256 + ws.IRState.IRSensors[1].Position.X * 256) / 2; pos.Y = (int)(ws.IRState.IRSensors[0].Position.Y * 128 + ws.IRState.IRSensors[1].Position.Y * 128) / 2; radians = Math.Atan2(ws.IRState.IRSensors[0].Position.Y - \ ws.IRState.IRSensors[1].Position.Y, ws.IRState.IRSensors[0].Position.X - ws.IRState.IRSensors[1].Position.X); angle = radians * (180 / Math.PI); //ラジアン→角度変換 } else { //赤外線が1つなら、1つめの値を採用する pos.X = (int)(ws.IRState.IRSensors[0].Position.X * 256); pos.Y = (int)(ws.IRState.IRSensors[0].Position.Y * 128); } double d = angle / (180 / Math.PI); //角度→ラジアン変換 //2D回転変換 float x = pos.X; float y = pos.Y; float x1 = x + bmp.Width * (float)Math.Cos(d); float y1 = y + bmp.Width * (float)Math.Sin(d); float x2 = x - bmp.Height * (float)Math.Sin(d); float y2 = y + bmp.Height * (float)Math.Cos(d); //新しい描画位置 System.Drawing.PointF[] destinationPoints = {new System.Drawing.PointF(x , y ), new System.Drawing.PointF(x1, y1), new System.Drawing.PointF(x2, y2)}; //画像を表示 g.DrawImage(bmp, destinationPoints); //<終了時はbitmapオブジェクト等の廃棄などを忘れずに> <以下略>
プログラム実行時、DrawForms()は秒間数10回はまわってきますので、毎回の描画時にURLから読み込むと、動作がとても遅くなってしまいますので、初期化時にロードしています。このとき、インターネットに接続されており、正しくURLから画像ファイルが取得できないとエラーになります。「bmp.MakeTransparent()」で「抜き色」として、画像の一番左上の色を指定しています(これを指定しないと「背景が灰色の指」が表示されます)。今回はURLからBMP画像を読み込んでいますが、ハードディスク内のPNG画像を読み込む場合のソースコードもコメントに記述しておきました。
赤外線2点から傾きをとる方法は「Math.Atan2()」を使います。これはアークタンジェントという三角関数で、tan(余弦)の定義から与えた直角三角形のなす角を得る関数です。180度を超えなければ問題なく安定して角度が取得できます。
Atan2で取得した角度はラジアン(変数radians)で渡されますので、理解しやすいよう角度(変数angle、単位としてはdegree)に変換する数式も用意しておきました。最後に「2D回転変換」というコードで三角関数による変換処理を実施して、DrawImage()で読み込んだ画像を描画しています。
数学っぽいことが沢山出てきて頭を抱えている人もいるかもしれませんが、内容的には高校1〜2年程度の数学の教科書に載っていることを応用しているだけです。コンピューターグラフィックスプログラミングは数学を楽しく活用できる珍しい例ともいえます。「数学なんて嫌いだ」という読者の方は、良い機会ですからアレルギーを出さずに「楽しんで」取り組んでみてください。
「指カーソル」が見事に回転します。画像ファイルも存在しないのに、インターネット経由でBMPファイルを読み込んでいるところにも注目です。指の傾きは、一見何に使うのかわかりませんが「WiiRemoteを使っている」という感じがしますし、もしかしたら角度を積極的に使ったコマンドや、ベクトルを利用した物理的なインタラクションなどに活用できるかもしれませんね。
以上で、装飾要素に関する解説を終わります。「装飾要素」は事務系アプリケーションでは文字通りデコレーションでしかないのですが、インタラクションが重要になるプログラムでは非常に重要な要素になります。また、プログラミングの実装の仕方によっては、装飾要素がプログラム全体のパフォーマンスを低下させたり、ユーザーインタラクションを向上させたりと、奥深いプログラミング要素になることが体感できたのであれば幸いです。
以上で、「WiiRemoteMouse」の開発は終わります。この章での開発を通して、単にプログラミングだけではなく、多くのことを学ぶことができたのではないでしょうか?
ここで完成とはいえないかもしれません、まだまだ実装したい機能やチューニングしたい要素がたくさんあると思います。例えばこの「WiiRemoteMouse」を身体にハンディをもった人に使ってもらうのか、自分がソファーに寝そべりながらネットを楽しむために使うのか。そのためにどのような改善ができるのか。このような課題に対して、万能かつ確実な回答はありませんが、今まで学んだことを応用し、想像力を働かせればすれば、必ずゴールにたどり着けるでしょう。つまり、作った本人が納得できて、かつユーザーは「さわっていて楽しいプログラム」になるということです。
この実践的なプログラミング体験を通して、皆さんの可能性は確実に広がったはずです。ぜひ、繰り返し時間をかけてWiiRemoteをつかったインタラクティブ技術プログラミングの醍醐味を楽しんでください。次章「演習問題」にはWiiRemoteを活用したイマジネーションを爆発させるための刺激的なスパイスを沢山紹介しています。それぞれの課題に必ずしも「模範回答」が用意されているわけではありませんが、今のあなたであれば、自分自身で楽しみながら学習し、前に進めていくことができるはずです。
この章は「アイディアストック・演習問題集」として、いままで学んだ技術を応用することで、WiiRemoteをつかって実現できる様々なプロジェクトの実例を紹介します。
アイディアをためておく"棚"のようなものをイメージして「アイディア・ストック」と名付けました。イメージしやすいようにプログラミング編、モノ編、ゲーム応用編、サービス編、作品編、研究編に分けていますが、このアイディアの活用は読者のみなさん次第で様々な方向に混ざったり、生み出されたりしていくことでしょう。
本書をここまで読み進めてきた読書であれば、「不可能なほど難しい」という内容ではないはずです。本章では細かいステップバイステップの解説をあえて割愛し、少ない紙面で幅広くWiiRemoteの可能性を伝えることに力点を置きます。
各セクションの終わりに「演習問題」として、そのテーマの研究や作品作りに役立つ問題集を用意しておきました。難易度が5段階の☆で表現されていますので、難易度に合わせて授業や課題の制作、論文のリファレンスなどにご活用ください。
8章までの知識を一歩進めて、WiiRemoteを3Dグラフィックスプログラミングの世界で利用できるようになりましょう。マイクロソフトの本格的なゲーム用3DCGプログラム開発環境「XNA」と「WiimoteLib」を使います。
ここでは「XNA Game Studio 3.1」とWiimoteLibを使って、C#.NETによるゲーム開発環境をベースにしたリアルタイム3DCGによるプログラミングを解説します。
XNAとは、マイクロソフトが推進している「DirectX」の流れをくむ最新の.NETによるゲーム開発統合環境です。XNAのコーディングスタイルは、旧来のリアルタイム3DCG開発環境の本流であったDirectXやManaged DirectXとは異なり、XNA FrameworkにおけるC#言語による開発になります。DirectX時代よりもさらにゲーム開発に便利なツールやAPIが統合されており、簡単に効率よくゲームプログラムを作成できるようになっています。
プロのゲーム開発者に限らず、学生などにも親しみやすい環境でもあります。WindowsPC用のゲーム開発に加え、最新のコンシューマー(家庭用)ゲーム機である「Xbox 360」の両方のプラットフォームで、非常に効率的かつ先進的な開発ができるため、プロのゲームスタジオだけでなく、今後ホビープログラマを中心に大きな流れを作り出す可能性があるでしょう。
このセクションでは、WiiRemoteの加速度センサーの傾きによって、3Dで描画されたWiiRemoteがリアルタイムで変化するプログラム「WiiRemoteXNA」を作成します。なかなか派手な感じがするデモですが、XNA Game Studio 3.1を使って、驚くほど短いコードで作成することができます。
まずは、開発環境のセットアップを行いましょう。最新のMicrosoft XNA Game Studioをダウンロードしてインストールします。
Microsoft XNA Game Studio 3.1も無償で入手可能です。
http://www.microsoft.com/downloads/details.aspx? familyid=80782277-D584-42D2-8024-893FCD9D3E82
XNAは無料で開発環境を手に入れることができます。PCをターゲットプラットフォームとして利用する上ではライセンスに従い無料で利用することができますが、Xboxプラットフォームで開発するためには年間ライセンス料(9,800円※)を払う必要があります。本書ではXboxプラットフォームについては扱いませんが、Xbox 360のような、高価なゲーム用PCに比べて「安定して安価で入手できるコンシューマゲーム機」の開発環境が、それほど高価ではないライセンス料で入手できるのは大変な魅力です。開発したゲームプログラムを世界中に1200万人以上いるXbox 360のユーザーに遊んでもらえることも、モチベーションになるでしょう(Xbox360でWiiRemoteが公式に使える、という話は聞きませんが...)。
本書とは直接関係ありませんが、Xbox 360用ゲームを実行するためには年額9,800円の「XNAクリエイターズクラブ」に入会する必要があります。XNAクリエイターズクラブはXbox 360用ネットワーク・サービス「Xbox Live」から加入できます。
[URL] http://www.xbox.com/ja-JP/live/
[URL] http://creators.xna.com/en-US/tour_detail
Microsoft XNA Game Studio 3.1のインストールは、ダウンロードしたインストーラーのウィザードに従うだけで問題なく行えるでしょう。ウィザードの最後に「Xbox 360用のサービスを起動するか?」という質問がありますが、これはXbox 360用のプログラムを開発したときに、ローカルネットワーク経由でXbox 360に送信するためで、特に利用する予定がなければチェックは入れなくても問題ありません。
さて次はゲームプロジェクトの作成です。Microsoft Visual C# 2008を起動してください(無料の「Express Edition」でも問題なく利用できます)。「新しいプロジェクト」を選ぶと、いつも見慣れた新規プロジェクト作成のダイアログに「XNA Game Studio 3.1」という項目が現れているはずです(ない場合はGame Studioのインストールを再度確認してください)。「テンプレート」から「Windows Game (3.1)」をクリックして、プロジェクト名に「WiiRemoteXNA」という名前をつけて、場所を「C:\WiiRemote」として「OK」をクリックします。
数秒間待つと、新しいプロジェクトが作成されます。[F5]キーを押して、試しにプロジェクトを実行してみましょう。水色の背景に、何かウィンドウが表示されれば成功です。
次はWiimoteLibを組み込みます。XNA環境でも4章や8章での.NET環境におけるWiimoteLibの組み込み作業の流れと同じです。
ソリューションエクスプローラの「参照設定」を右クリックし、「参照の追加」を選択します。参照の追加から「参照」もしくは「最近使用したファイル」から「WiimoteLib.dll」を選択します。
いままでのプロジェクトと同様、プログラム冒頭のusingにWiimoteLibを追加し、クラスの初期化時にWiimoteオブジェクトの新規作成「Wiimote wm = new Wiimote();」を挿入します。
using WiimoteLib; <略> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Wiimote wm = new Wiimote(); //Wiimoteオブジェクトの作成 <略>
初期化時にWiiRemoteに接続しましょう。XNAフレームワークでは「Initialize()」という関数がすでに用意されていますので、そこにいつものWiiRemote接続処理を追加します。
protected override void Initialize() { base.Initialize(); wm.Connect(); //WiiRemote接続 wm.SetReportType(InputReport.ButtonsAccel, false);//ボタンと加速度 wm.SetLEDs(15); //LED全点灯 }
「SetReportType()」の第2引数に「false」を設定して非連続データ取得モードにしています(ここでいつものようにtrueにして、コールバック関数を設定してもよいのですが、今回のサンプルでは簡単に加速度の値を取れればよいので、値のばたつきが少ない、よりシンプルな方法をとります)。
この状態でもビルド処理を試すことはできますが、ButtonStateが「あいまいな参照」というエラーを出し停止するはずです(XNAとWiimoteLibに同じ名前のプロパティがあるため)。エラーの出る行をコメントアウトすれば良いのですが、オリジナルのソースでは、ここで「ゲームパッドのボタンが押されたら終了」となっているようですので、ここを「WiiRemoteの[Home]ボタンが押されたら終了」と変更してみましょう。
protected override void Update(GameTime gameTime) { // Allows the game to exit ↓もともとのコードをコメントアウト // if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) if(wm.WiimoteState.ButtonState.Home) { this.Exit(); } base.Update(gameTime); }
この段階でWiiRemoteをBluetooth接続し、[F6]で実行してみてください。先ほどと同様、水色の画面が表示されますが、WiiRemoteのLEDが4つとも点灯し、[Home]ボタンを押すことでプログラムを終了できるはずです。
次に読み込む3Dモデルファイルを準備しましょう。3Dモデルデータをゼロから作ると時間がかかってしまいますので、ここでは小坂研究室のホームページからWiiRemoteによく似た「.x形式」のモデルファイル「wiimodoki.x」と、その表面を飾るテクスチャファイル「texture_wii.jpg」をダウンロードで入手します。
■小坂研究室「XNAとWiimoteLibで3Dオブジェクトを操作」
[URL] http://www.kosaka-lab.com/tips/2009/06/xnawiimotelib3d.php
■特製テクスチャファイル(texture_wii.jpg)
ファイルをダウンロードしたら、XNAのプロジェクトのコンテンツフォルダ「C:\WiiRemote\WiiRemoteXNA\WiiRemoteXNA\Content」に置きます。
ファイルを置いた後に、Visual Studio内に取り込みます。ソリューションエクスプローラーの「Content」を右クリックして「追加」→「既存の項目」として、先ほど置いた「wiimodoki.x」と「texture_wii.jpg」を読み込んでください。
なお、モデルデータである「wiimodoki.x」と「texture_wii.jpg」は3DCGコンテンツ制作ソフトウェア「Maya2008」を使って作成したのち「cvXporter」を使ってコンバートしています。
[URL] http://www.chadvernon.com/blog/downloads/cvxporter/
Maya2008のような比較的高価な3DCGソフトウェアが無ければ、日本人が開発している歴史ある3Dポリゴンモデラー「Metasequoia(メタセコイヤ)」でも可能でしょう。無料版と有料版(シェアウェア)があり、価格は1ライセンスにつき5,000円です。「Metasequoia LE R2.4」では.xファイルを直接書き出せますので、XNAやDirectX環境で簡単に利用することができます。作者のO.Mizno氏に感謝です。
[URL] http://www.metaseq.net/
さて、コーディングに戻りましょう。モデルファイルの読み込みと表示について、「LoadContent()」と「Draw()」に加筆をします。
<略> Wiimote wm = new Wiimote(); //Wiimoteオブジェクトの作成 private Model xfile; //Xファイル読み込み用 <略> protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); this.xfile = this.Content.Load<Model>("wii"); //.xファイルの読み込み foreach (ModelMesh mesh in this.xfile.Meshes) { foreach (BasicEffect effect in mesh.Effects) { //ビュー行列 カメラの視点を設定(0.0f,0.0f,10.0f)の位置から原点を見る effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10.0f), Vector3.Zero, Vector3.Up); //プロジェクション行列 視野角などの設定 effect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(45.0f), (float)this.GraphicsDevice.Viewport.Width / (float)this.GraphicsDevice.Viewport.Height, 1.0f, 50.0f ); } } } <略> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); //画面に描画する foreach (ModelMesh mesh in this.xfile.Meshes) { foreach (BasicEffect effect in mesh.Effects) { //WiiRemoteの加速度に合わせて回転角度を設定 effect.World = Matrix.CreateFromYawPitchRoll(0, -wm.WiimoteState.AccelState.Values.Y, -wm.WiimoteState.AccelState.Values.X); } mesh.Draw();//meshを描画 } base.Draw(gameTime); } <略>
ここでは個々のAPIについて解説はしませんが、いずれも3DCGにおけるお作法的な手続きと「見え方」を設定しているものです。興味がある人は、MSDNなどのマニュアルをしらべたり、パラメーターを変更してみたりして、探求してみてください。
実行すると、WiiRemoteの動きに合わせて回転する「WiiRemoteもどき」が表示されます。[Home]で終了します。
ファイルの読み込みなどでエラーが起きるときは、ソリューションエクスプローラーで正しくContentにファイルが取り込まれているか確認してください。なお、テクスチャファイルは「wiimodoki.x」の中で記述されていますので、実はVisual Studioに取り込まなくても自動で読み込まれます。また「wiimodoki.x」ファイルはテキストで記述されていますので、テキストエディタで編集することで、マテリアル(表面材質の特性)やテクスチャファイル名を書き換えたりすることもできます。
以上で「WiiRemoteXNA」は完成です。usingの整理など行った完成版のコードを紹介します。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WiimoteLib; namespace WiiRemoteXNA { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Wiimote wm = new Wiimote(); //Wiimoteオブジェクトの作成 private Model xfile; //Xファイル読み込み用 public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { base.Initialize(); wm.Connect(); //WiiRemote接続 wm.SetReportType(InputReport.ButtonsAccel, false); wm.SetLEDs(15); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); xfile = Content.Load<Model>("wii"); foreach (ModelMesh mesh in this.xfile.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10.0f), Vector3.Zero, Vector3.Up); effect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(45.0f), (float)this.GraphicsDevice.Viewport.Width / (float)this.GraphicsDevice.Viewport.Height, 1.0f, 50.0f ); } } } protected override void UnloadContent() { ; } protected override void Update(GameTime gameTime) { if(wm.WiimoteState.ButtonState.Home) { this.Exit(); } base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); foreach (ModelMesh mesh in this.xfile.Meshes) { foreach (BasicEffect effect in mesh.Effects) { //WiiRemoteの加速度に合わせて回転角度を設定 effect.World = Matrix.CreateFromYawPitchRoll(0, -wm.WiimoteState.AccelState.Values.Y, -wm.WiimoteState.AccelState.Values.X); } mesh.Draw();//meshを描画 } base.Draw(gameTime); } } }
リアルタイム3DCGという見た目の派手さに対して、とてもコードが短いことに驚かれたのではないでしょうか(XNAのおかげです)。
なお今回はコールバックを使わずに、描画ループで直接WiiRemoteの加速度センサーの値をそのまま回転の角度に利用するという、ちょっと荒っぽい方法をとっています。実際のゲームに使う場合には、このような使い方をすることは稀で、コールバックと動作認識関数などを作るべきでしょう。
ここから先は内容によって、今までのようなステップバイステップの解説手法をとりません。アイディアのみをかいつまんで、自分のプロジェクトに利用していきましょう。
このセクションでは筆者が開発に協力した反重力レーシングゲーム製品「AceSpeeder2」をWiiRemoteでプレイできるように実験した例を紹介します。
WiiRemoteは任天堂自身「マリオカート」などを発売していることもあり、レースゲームへの利用は想定されているようで、親和性高く利用できます。
「AceSpeeder2」は2007年に発表された「RAINGRAPHスタジオ」ナカタニタカヒロ氏によるゲーム作品で、シェアウェアとして非常に人気が高かった初代「AceSpeeder」(2000年)の続編となる超高速SFレーシングゲームです。
[URL] http://www.raingraph.com/
ゲームシステムはDirectX8ベースで開発されており、DirectXのジョイスティック機能であるDirectInputをベースに自機の制御を行っていました。
筆者はナカタニタカヒロ氏の協力により、既にGPUを使った高速な全身画像認識「GPUVision」や、「OpenCV」を使った顔画像入力による、マルチモーダルなレーシングゲームコントロールの研究をAceSpeeder2のソースコードを用いて行っていました。その流れで、WiiRemoteによる新しい操作方法を実験してみました。
ゲームのソースコードに関わる部分ですので、プログラミングの詳細を解説しませんが、プレイヤーインタラクションとしては「左右は傾き、前後はアクセル/ブレーキ、2回振るとブースト発動」という操作体系にしてあります。
すべて加速度センサーによる重力検出だけで実現しており、ボタンはメニュー選択を含め、全く使用しません。またLEDを「自機シールドの残量」として表示したり、バイブレーターを演出に使ったりと、WiiRemoteを最大限に活用しています。
AceSpeeder2では「ブースト」という機能があり、エネルギーをためて大きな加速力を得ることができます。またコースアウト時にもブーストで復旧できる技があるので、いつでもプレイヤーの意思で『ブースト発動!』できることが爽快感につながります。このブーストを「2回振る」というアクション、つまり加速度センサーのマグニチュードを取ることで実現しています。
[URL] http://www.youtube.com/watch?v=KowXAXdfO8E
WiiRemoteのおかげで、ボタンを全く使わない操作体系になり、小さな子供でも体験することができました。
実際の展示を通したユーザーテストでの観察によると面白いことがわかりました。「ブースト」は開発者の意図通り、振って発動する場合のほかに、コースの切れ目を避けるために無意識に「ジャンプ」したときに発動したり、コースアウトして「うわっ!落ちる!!」とのけぞった瞬間、無意識で振ったきっかけで発動したりと、より全身で直感的に楽しめるゲームになりました。
残念ながら当時のWiiRemoteのBluetooth接続はそれほど安定した環境ではありませんでしたし、本書のような解説書を出版するということも考えていませんでしたので、このバージョンは製品としては公開されていませんが、それほど難しいことをしたわけではありません。これからは、もっと多くのPCレーシングゲームでWiiRemoteを活用してほしいと思います。
筆者は2007年に、この実験とWiiRemoteの赤外線センサーの特性実験などを「WiiMedia: motion analysis methods and applications using a consumer video game controller」という論文にまとめています。この論文は反響が高く、アメリカで毎年開催されるCGとインタラクティブ技術の世界最高の国際会議「SIGGRAPH」におけるビデオゲームシンポジウム「Sandbox」において、最優秀論文賞をいただきました。 ソースコードは公開し、研究は発表しておくものだなと、つくづく思います。
[URL] http://sandbox.siggraph.org/about.html
加速度センサーによる傾きの検出はAtan2()を使えば簡単に求めることができます。マグニチュードの算出なども既に7章、8章などで扱っていますので参照してください。さっそく課題の設定に入りましょう。
誰もが「AceSpeeder2」のようなすばらしいゲーム作品のソースコードにアクセスできるわけではありません。しかしフリーでソースコードを公開されているゲームプロジェクトはSDL関係では意外に多く、有名なところでは「Tux Racer」というLinuxペンギンのレースゲームなどもソースが公開されているプロジェクトです。
ソースコードがどうしても手に入らない、ということきは逆転の発想で3章で学んだ「GlovePIE」を使いましょう。プログラミングが不要ということはソースコードの入手も不要なのです。うまくジョイパッドをエミュレーションするコードを書きましょう。アナログ→デジタル入力でも、うまく作ると自然な体験を作ることもできます。
以下、既存のレースゲームプロジェクトをWiiRemote対応させる上で、難しかった点をメモしておきます。
多くのゲームの場合はDirectXのジョイスティックAPIであるDirectInputに従った仕様になっているはずです(便利なので)。WiiRemote専用のゲームならいいのですが、PCゲーム開発としては、ゲームバランスや他のコントローラーでの操作感を壊さずに、他のゲームコントローラーと同様の感覚で、うまくなじませることが必要になります。
「AceSpeeder2」の場合も、元のゲームが細かい操作のためにデジタルジョイパッドを想定して設計されていたので、WiiRemoteの傾きをアナログジョイスティックとして当てはめると、大きく動きすぎたり、細かい動きができなかったりとより難易度が上がってしまいました。
難易度が上がるだけなら調整すれば良いのですが、「傾ける」というプレイヤーの物理的な行動には制約がありませんので、ゲーム内に働く物理(速度や舵など)といかに親和性を保ちながら、インタラクションを向上させるかといった点も大きな課題となりました。
ちょっとしたアイディアとしては、「ゲーム内の物理」と「実際のユーザーの姿勢」をインターフェースさせるための「理想の姿勢」というものを考えて、係数や式といった数値で扱う方法があります。ここをチューニングしていくことで、元のゲームプログラムを壊さずに、より操作感の向上に注力できるはずです。
任天堂からリリースされた最新のレースゲームでの例としては「Wii Sports Resort」の「ウェイクボード」が良くできています。物理シミュレーションを使った美しいCGもさることながら、SFレーシングとは異なり『上に振ってジャンプ』という動作に加えて、『着地のときはWiiRemoteを水平に保つ』というルールを加えることで、WiiRemoteを使った操作とゲーム性を爽快感とともに見事に昇華しています。
■Wii Sports Resort「ウェイクボード」(動画あり)
[URL] http://www.nintendo.co.jp/wii/rztj/resort/02_sports/index.html
ゲームでのWiiRemote応用(...といっても、もともとゲーム用コントローラーですが!)を考える上で、レースゲームのようなダイレクトな方向入力として使う以外、もっとも期待される使用方法が「認識」ではないでしょうか。
このセクションでは筆者が実際に開発した剣術アクションゲーム「JaWii's Virtual Fencing」(ジャウィのバーチャルフェンシング)の開発をベースにWiiRemoteをつかったモーション認識の基本テクニックと、剣術、特にフェンシングゲームへの応用を簡単に紹介します。
赤外線センサーと全身を使ったゲーム製品は、任天堂Wiiが世界初、というわけではありません。「剣神ドラゴンクエスト甦りし伝説の剣」(スクウェア・エニックス・2003年)で利用されていたのが国内メジャー作品では最初といえるかもしれません。
フォトダイオードと赤外線LED光源が受光部(ロトの証)に組み込まれ、「ロトの剣」には再帰性反射剤(反射板や銀スプレー)が処理されています。この「電池の要らない剣」を振り回して、8種類の切る方向に加え、正面に構えて魔法を使うモーションを入力することができました。
この製品は滋賀県草津市にあるインタラクティブ技術の研究開発企業「新世代株式会社」のに技術よって実現しています。この会社のホームページ(http://www.xavix.jp/)にいくと、技術の高さと様々な産業へのインタラクティブ技術のインパクトがうかがい知れます。
この作品「ジャウィのバーチャルフェンシング」は筆者がフランスの西部ラヴァル市(Laval)にて、テーマパーク開発のためのエンタテイメントシステムの開発に従事してたころに、市の観光振興企画として開発したものです。
ラヴァル市は人口10万人程度、世界遺産モンサンミッシェルから車で2時間程度の場所にある、中世の雰囲気を残す美しい中規模都市です。バーチャルリアリティ応用で有名な学術・産業研究都市でもあります。年に一度ヨーロッパでもっとも大規模なバーチャルリアリティのイベント「Laval Virtual」(ラヴァル・バーチャル)が開催されます。
Laval Mayenne (Wikipedia英語) [URL] http://en.wikipedia.org/wiki/Laval,_Mayenne
Laval Virtual (日本語ページあり) [URL] http://laval-virtual.org/
フェンシングゲーム「JaWii's Virtual Fencing」はそのLaval Virtualでラヴァル市のブースで展示されるゲーム企画でした。ちょうどラヴァル市は2007年に、同市出身の画家アンリ・ルソー(Henri Rousseau, 1844〜1910)と同じ時代を生きた、ラヴァル市出身の劇作家アルフレッド・ジャリィ(Alfred JARRY, 1873〜1907)の没後100年祭を祝っていました。
ジャリィは作家としては「ウビュ王(Ubu Roi)」シリーズが有名ですが、古式フェンシングの名手でもありました。そこで当時発売されたばかりで話題だったWiiRemoteを使って「ジャリィに古式フェンシングの指南を受ける」というゲームアイディアが、市民にジャリィの人物を伝える良い企画として持ち上がったのです。
まずは解説に入る前に、完成版の動画をYouTubeにアップロードしてありますので、ご参照下さい。
WiiMedia:Sword Fighting "JaWii's Virtual Fencing"
[URL] http://www.youtube.com/watch?v=Kl_-KoVLtx4
フェンシングの基本として、「突き」「正面切りつけ」「右切りつけ」「左切りつけ」といった攻撃、それから各攻撃に対応した防御法があります。古式のフェンシングはより複雑ですが、現代のように電気を使った接触検出はありませんし、防具もつけません。
このような複雑で形式ばった古式フェンシングのモーションを、子供も交えた一般のお客さんに伝えるのは企画の趣旨ではありませんが、ジャリィがたしなんだという古式フェンシングはちゃんと表現したいところですので、フランスフェンシング協会に依頼して、Gypsy社製の機械式モーションキャプチャーで古式演舞を収録しました。
3D Studio MaxとVirtoolsを使って、ジャリィを模した3Dキャラクター「JaWii」にこのモーションを元したアニメーション割り当てます。
ゲームは背景投影の大スクリーンに表示され、プレイヤー自己視点で遊びます。プレイヤーの3Dモデルは必要ありませんが、フェンシングのサーベルを振るアニメーションだけは事前に複数通り作成しておきます。
さてここからがWiiRemoteプログラミングの課題です。
プロジェクター背面投影という設営の構成上、赤外線マーカーは利用できそうにありません。加速度センサーだけでさまざまなプレイヤーの複数の動作を認識する必要があります。
「振る」を認識するだけであれば、加速度センサー各軸の強度を算出するマグニチュードで十分でしょう。しかし、大人や子供など人によってマグニチュードの強さは異なりますし、「突き」だけならともかく、フェンシングらしく切りつける方向もある程度は検出したいと思います。
ここで複数のプレイヤーの「切りつける」という動作について、加速度センサーの値をテキストファイルに保存し、作図して観察してみると、いろいろな問題や解決方法が見えてきます。以下、ポイントをいくつかまとめてみます。
特にさまざまな問題の根底に感じられる原因は、WiiRemoteの検出分解能と回転速度が得られない点でしょう。図9-14は加速度センサーの加速度データひとつひとつを3次元ベクトルにして積分し、得た速度をもとに、さらにその積分を取って3次元的な位置に配置した図です。「同じ場所で数回、自然に振る」というアクションで、長い矢印ほど大きいマグニチュードを表していますが、なぜか右から左に移動しているように見えます。
加速度センサーの分解能により、3次元座標が再構築できないことは7章でWiiRemoteを「そーっとうごかすと検出されない」という実験行ったので理解できるでしょう。そして、図中の○の部分に注目するとより面白いことが見えてきます。この部分は「振り」モーションの最後で、伸ばした腕を引いて、後ろに振りかぶった瞬間です。明らかに振りの最高速に比べて直線的な動きではなく、小さい力で回転しているように見えます。
実際にはこの瞬間、WiiRemoteは頭の後ろで手首をつかって回転しています。つまり回転のエネルギーをWiiRemoteが取得できていないため、このような図になるようです。しかし逆転の発想で、この瞬間のマグニチュードは、振りの最大速のマグニチュードとは異なる性質をもっているので、弱いマグニチュードが入力されたときに「処理ウィンドウの最初」として評価を開始することができます。
以後、このような「評価関数」を作りこんでいくことで目的のモーションを発見する関数を作っていきます。あとは個々のモーションに対応する評価関数それぞれを作っていきます。
しかし、加速度センサーだけの値だけでは観察できるデータが少なすぎます。そこで別の測定方法を使って振る舞いを観察します。図9-15は光学式のモーションキャプチャーを装着した状態で、WiiRemoteを持って{正面、右から、左から}の攻撃モーションを繰り返した様子です。
モーションキャプチャは高価な機材なので気軽に使うことはできませんが、このような実験と可視化を一度行っておくことで目的のモーションを検出するために、どのようなベクトルに注目すべきか、またどれぐらいのサンプル時間(図の矢印の個数)が必要かを見極めることができます。理想とされる方向との近いものを1.0とすればよいのです(ベクトルの内積を使いましょう)。
また評価関数化することで複雑なモーションも簡単に設計できるようになります。評価関数の足し算や掛け算をつかって、複数の評価関数が同時に成立する条件を判断させたり、マイナスの評価を使って、誤検出されやすい条件から逆の条件を浮き立たせます。
例えば図9-15では「突き」と評価されるベクトル(つまりY軸十字ボタン方向への加速)に対して着色していますが、このベクトルから遠いものが「右切りつけ」や「左切りつけ」に該当します(「振り」モーション中は重力の影響はほとんど無いことも読み取れます)。「突き」の最高速モーションは5サンプル程度で認識できていますので、他の評価関数では、より多くのサンプルを使って「突きではないモーション」に注目させればよいのです。
この方法を使って「ジャウィのバーチャルフェンシング」では、さまざまな年齢層のプレイヤーでも3方向の攻撃モーションと防御モーションを認識させ、適切なアニメーション再生に割り当て、ゲーム作品を完成させることができました。
WiiMotionPlusは本書執筆の最終段階で発売されましたので、上記のようなグラフを描くことはできませんでした(発刊が遅れてしまいます!)。演習ではWiiYourself!によるC++を意識していますが、グラフを描画やファイル入出力もきっちり実装するならば、C#.NETによるフォームアプリケーションのほうが便利かもしれませんね(小坂研究室にCSVファイルを保存するサンプルがあります)。
このような「モーション評価関数デザイン」は今、ゲーム開発の世界ではとても需要がある技術です。HMMやSVMなどの機械学習を用いた方法も研究としては面白味がありますが、最後はこの評価関数のデザインセンスが、ゲームの面白さを決定付けることもあります。IF文のカタマリで開発し、ゲームプログラマーの誰かに特化されたインタラクションではなく、エレガントでエキサイティングな評価関数を設計できるよう、探求してみてください。
「JaWii's Virtual Fencing」の開発で使ったVirtoolsは高価な産業向け製品であることもあり、日本ではそれほど有名ではありませんが、欧米では最も利用されている可視化プラットフォームです。モデルや画像などのリソースに対して部品化されたGUIプログラミングを施すだけでほとんどのインタラクティブデザイン関係が作れてしまう画期的なツールです。
カスタマイズ性も高く、レンダラーやプラグインなどほとんどのソースは公開されています。ゲームの流れや面白さの根幹に関わる部分の設計をプランナーがGUIで作成し、最終工程である最適化やプラットフォームの独自部分などをプログラミングで行う、といったゲーム開発手法です。
ソニーPSP用や任天堂Wii用のVirtoolsも存在します。任天堂Wiiの開発ライセンスを持っているゲーム開発企業であれば、WiiRemote関係のプラグインを入手することもできるそうです。またVirtoolsのフリーな開発者コミュニティは活発で、スワップミート(http://www.theswapmeet-forum.com/)で様々な情報やソース、プラグインが共有されており、PC上で利用できるオープンソースのWiiRemoteのプラグインも多数あります。将来ゲーム制作を目指す学生さんや、フリーのゲーム企画者にとって、これは協力なソリューションです。Virtoolsを使ってPC上でゲームのプロトタイプを制作すれば、すばやくアイディアを形にできますし、資金や技術的なリスクを回避できるからです。
日本ではクレッセントと三徳商事という会社が中心に代理店を行っています。コンテンツの制作サポートや技術サポート、教育支援、学校向けライセンス販売などリセラー各社の得意分野がありますから、興味があったらまず問い合わせてみてください。お試しライセンスを発行してくれるかもしれません。
■株式会社クレッセント http://www.crescentvideo.co.jp/virtools/
■三徳商事 http://virtools.jp/
次のテーマは「バランスWiiボード」を扱います(本書では「WiiBoard」と標記しています)。
第1.4章で紹介した学生作品『人間椅子』でもWiiBoardを2つ使い、Windows上の独自APIにより開発を行っていました。
このセクションではMac上のProcessingを使った学生の卒業制作作品『オーラ診断』の開発プロセスを、実際に本作品を開発した東京工科大学コムメディアデザイン研究室・電王隊(小笠原明日美、平塚宏、平野実花、渕上伸吾)の皆さんのご協力により学生視点で開発資料を紹介することで、WiiBoardによる作品作りに親しんでみたいと思います。
「オーラ診断」は東京工科大学メディア学部の卒業制作展「メディアコンテンツ展2009」の「ライフエンタテインメント」として発表された作品です。
まずは「オーラ診断」の動画とブログから紹介します(以下、渕上伸吾氏のBlogより文体も含めできる限りそのまま引用しています)。
YouTube動画「オーラ診断」
[URL] http://www.youtube.com/watch?v=3pL3ObUwoA8
プログラミング初心者4人が作った「オーラ診断」という作品
[URL] http://gryng.blog87.fc2.com/blog-entry-15.html
「オーラ診断」は体験者のオーラを診断する作品です。自分のオーラの色や形を見て、脳内メーカーのように楽しんでもらうことが狙いです。
こんなかんじです。
いわゆる脳内メーカー系サービスは、名前や誕生日を入力させる事で結果を算出しています。そうしないと、分析する手がかりがないので当然です。
しかし、オーラを診断するにあたって、体験者に“入力”をさせたくありませんでした。厳密には、入力した事を気がつかせない。ここがこの作品のキモになっています。
そのために使ったデバイスが、バランスWiiボードです。
NINTENDO Wii用の周辺機器であり、Wii FitでおなじみのバランスWiiボード。Wii Fitではバランスゲームや筋トレ、ヨガなどを楽しめます。
このバランスWiiボードは、乗った人の重心を求めることができます。具体的には、左右の足の前後、合計4カ所にかかる重さを得ており、それぞれを比べる事で重心を調べています。
このバランスWiiボードなら、体験者に意識させずに情報を入力させることができる!と思ったわけです。なぜなら、そこに立たせるだけで重心の情報を得る事ができちゃうんですから。
バランスWiiボードとMacを接続するために「BBOSC」というソフトを利用させていただきました。
[URL] http://456.im/wp/download/
立ち上げて、Wiiボードの電池ボックスの中にある[Sync]ボタンを押すだけ。びっくりするほど簡単に接続できました。
そうしてMacに送られてくる4カ所の体重の情報を、Processingに渡すわけです。このあたりは、Web Designing 3月号(2008年)の記事[Beyond the Browser]を参考にさせていただきました。
大雑把に言えば、BBOSCが体重の情報をOSCという規格で送信し続けてくれるので、Proecssing側では「oscP5」というライブラリを使ってキャッチする、という感じです。
Processingはビジュアル表現が容易なオブジェクト指向のプログラミング言語、らしい。はっきり言って名前すら知りませんでした。
Processingでは体重の情報を元にオーラを描画していきます。
人の重心は絶えず動いているもの。それだとちょっと扱いにくいので、その人の平均的な重心位置を求めるために、数秒の判定時間を設けました。その間、体験者には画面に集中しておいてもらいます。そうして見つかった重心の位置を使って、その人の基準となる1色を設定します。
基準の1色だけだと画面が寂しいので、重心の移動に合わせてある程度、色が変化するようにしました。この変化の幅も重心の位置から設定しています。より“その人だけのオーラ”が診断できるようになりました。
書いてないこともまだまだたくさんあるんですが、作品のおおまかな仕組みを紹介してみました。
以上の渕上伸吾氏のブログエントリーだけですと、情報が足りませんので以下補足します。
「BBOSC」は石橋素(いしばしもとい)氏が雑誌「ウェブデザインニング」連載記事のために開発したもので、WiiRemoteでMacを操作できる「DarwiinRemote」などを開発したHiroaki Kimura氏による、MacOSにおけるWiiRemoteプログラミングAPI「WiiRemote Framework」を参考して開発されたそうです。
「DarwiinRemote」WiiRemoteでMacを操作できる
[URL] http://blog.hiroaki.jp/2006/12/000433.html
「WiiRemote Framework」
[URL] http://blog.hiroaki.jp/2007/05/000456.html
OSCとはOpen Sound Controlの略で、電子楽器やコンピュータの音楽演奏データをネットワーク経由でリアルタイムに共有するための通信プロトコル、つまりMIDIの代替となることを意図してつくられたネットワークプロトコルです。カリフォルニア大学バークレー校にあるCNMAT(The Center for New Music and Audio Technologies)を中心にオープンソースで開発されています(http://opensoundcontrol.org/)。
「BBOSC」実行時に、ターゲットとなるホストのIPアドレスとポートを指定します。4カ所にかかる体重を0-10000にスケールしてOSCで送信します。右上、右下、左上、左下と、WiiBoardに内蔵された4つのひずみセンサーの値が得られますが、そのままでは使いにくいので、「オーラ診断」では{ X, Y}の値に変換し、平均を利用しています。
「オーラ診断」を実現するために、BBOSCのほかに、カメラの利用と処理に「JMyron」、人と背景を分けるために背景差分法を用い、人の輪郭をとるために「blobDetection」というProcessingのライブラリを使っています。
「JMyron」
[URL] http://webcamxtra.sourceforge.net/download.shtml
ちなみに「Myron」とはアメリカのコンピュータアーティストミロン・クルーガー(Myron Krueger, 1942)。に由来する名前と思われます。1980年代にインタラクティブ・アートやバーチャルリアリティーを使った作品を作った人です。
「Myron Krueger」(YouTube動画)
[URL] http://www.youtube.com/watch?v=A6ZYsX_dxzs
「blobDetection」[URL] http://www.v3ga.net/processing/BlobDetection/
Processingの画像処理ライブラリです。「Blob」とは「もやもやしたカタマリ」のことで、人物などの検出をするには向いています。なおこのホームページには画像処理を利用したさまざまなアートプロジェクトへのリンクがあります。
なおこの作品が発表された東京工科大学「メディアコンテンツ展2009」のホームページには「オーラ診断」の他にも面白い作品が数多く発表されています。
「ライフエンタテインメント」
[URL] http://www.teu.ac.jp/mce/2009/work/lifeenter.html
このセクションにご協力いただいた渕上伸吾氏も「Earth Surfer」というWiiBoardを使った別の「バックトゥザフューチャー感覚で写真を見るプロジェクト」を発表しています。
Windows環境では「WiimoteLib」でWiiBoardを利用することができます。特にC#.NETでWiiBoardを利用したい方は、小坂研修室でサンプルが公開されていますので活用すると良いでしょう。
[URL] http://www.kosaka-lab.com/tips/2009/02/wiiwii-fit.html
このセクションではWiiBoardとMacOSでの学生プロジェクトを扱いました。本書ではMacOSでのWiiRemoteプログラミングをProcsssingとActionScript以外は扱ってきませんでしたが、Bluetooth接続の安定感もあり「WiiRemote Framework」など、WindowsXP環境よりも先に日本人開発者によってプログラミング環境が開拓されてきた時期もありました。
またこのセクションで扱ったようなOSCのような「ネットワーク経由の楽器として扱う」という方法は、VJやアーティスト系に親しまれているインタラクティブなサウンドプログラム環境「Max/MSP」などでよく使われる方法です。実際にMax/MSPとWiiRemoteをつかったVJ活動などもよくききます。Windows環境だけにとらわれる必要は無いのです。
「オーラ診断」で使ったような、カメラ画像処理、画像エフェクトを組み合わせ、今後さらに幅広い層でバーチャルリアリティアート、ビデオアート、インタラクションアート作品が生まれることを期待します。
世界的に有名なバーチャルワールドサービス「SecondLife」でWiiRemoteを使えるようにしてみましょう。SecondLifeは無料で利用できるバーチャルリアリティ空間共有サービスです。リンデンラボという会社が運営しており、リンデンドルという実社会に似た通貨を買ったり、土地の売買や建築、キャラクターの装飾やプログラミングといったユーザーによるコンテンツ作成が行えるのが特徴です。よくわからない人は「ゲームが目的ではない3Dネットゲームのようなもの」を想像すると良いでしょう。
SecondLifeのクライアントソフトのソースコードが公開されているわけではありませんが、第3章で学んだ「GlovePIE」を使えば、プログラミングや改造することなくSecondLifeをWiiRemoteで操作することができるようになります。SecondLifeを使ったバーチャルリアリティ空間の建築作品作りで有名な首都大学東京の渡邉英徳先生が、インタラクティブ技術のイベントのための写真アーカイブ作品「Laval VRchive」の展示用スクリプトを作成していますので紹介します。
まず、Google Earth用のPIEスクリプトをベースに、複数回のユーザビリティ検討を行った結果、まずSecondLifeにもとからある前進/後進機能をオフにすることにしました。「バーチャルリアリティ空間内で等身大のサイズで過去の体験型イベントの写真を共有する」というコンテンツの設計上、前後に移動することがそれほど重要ではないと判断したのです。このような機能の刈り込みは、ユーザーインターフェースデザインを向上させる上で重要な機能制限といえますし、GlovePIEで入力させなければ良いので、比較的簡単に検討することができました。
しかし再検討を重ねていく上で、最終的には「十字キーで前後移動+左右転回,『[B]ボタンを押しながら↑↓』もしくは『+−ボタン』で上昇下降」という仕様に落ち着きました。赤外線センサーの値はマウスポインタに割り当ててあります。USB給電できるセンサーバーをプロジェクタースクリーンの下に設置し、SecondLife内の「指さし」と同じ感覚で、作品中のオブジェクトにWiiRemoteを向けて、[A]ボタンを押すことで操作することができます。
SecondLifeではちょっとしたことでカメラアングルがずれてしまうので、[Home]ボタンでリセットできるようになっています。またSecond Lifeのコンテンツを展示する場合、メニューバーが邪魔になるため、ディベロッパーモード[Ctrl+Shift+D]に切り替え、「インターフェイスをoff」[Ctrl+Shift+1]というモードにしています。この場合、画面上部のメニューバーは不可視にはなっていますが、メニューバーそのものは存在しているため、画面の上下端で[A]をクリックすると誤動作する恐れがあります。今回のスクリプトでは実装していませんが、画面の上下端にマウス移動のリミッターを付けることが望ましいかもしれません。
Mouse.LeftButton = Wiimote.A Keyboard.ESC = Wiimote.Home Keyboard.Up = Wiimote.Up Keyboard.Down = Wiimote.Down Keyboard.Left = Wiimote.Left Keyboard.Right = Wiimote.Right Keyboard.E = Wiimote.Plus Keyboard.C = Wiimote.Minus if Wiimote.B & Wiimote.Up Then KeyBoard.Up = False Keyboard.E = True Wait 600ms Keyboard.E = False endif if Wiimote.B & Wiimote.Down Then KeyBoard.Down = False Keyboard.C = True Wait 600ms Keyboard.C = False endif <以下、赤外線センサーの利用や安定感向上のためのスクリプト>
WiiRemoteの更新が長時間なにもないと、いつの間にか切断されてしまいます。赤外線を見せるなどして、切断されないリポートモードを使うと確かに切断はされないのですが、今度は電池が切れてしまいます。実験するには長い時間がかかりますが、時々LED出力などの信号を送ってあげるとよいのかもしれません。なおWii本体では、同様にWiiRemoteが長時間操作しないとスリープモードに入るのですが、何かWiiRemoteのボタンを押すと、本体側から再度Bluetoothのペアリングを要求するらしく、接続が復旧するようになっています。
Windows環境においてはまだBluetooth自動接続に成功したソフトウェアはありませんが、試している人がいないわけではありません。原理的にはDDKがあるので不可能ではないはずです(専用のHIDドライバを作った方が早いのかもしれませんが...)。そのうちこういった高度なWiiRemote管理もオープンソースのAPIで可能になるかもしれませんね。
WiiRemoteの赤外線センサーは非常に多機能で高速で高機能ですが、このままの状態では、センサーバーをWii本体に接続していなければ使えません。せっかくPCでWiiRemoteが使えるので、Wii本体がなくてもよいように、センサーバーの仕組みを知り、自作に挑戦してみましょう。
WiiRemoteの加速度センサーだけ使う予定の読者の方や、センサーバーをWii本体に接続して利用する方は、このセクションは読み飛ばしていただいてもかまいません。
センサーバーは、名前だけ聞くと『中にセンサーが入っている』ように聞こえますが、実際には赤外線センサーはWiiRemote内に実装されており、センサーバー内部にセンサーは存在しません。
センサーバー内部には、左右にそれぞれ5つの赤外線LEDが実装されています。Wii本体と接続しているケーブルは、ただの電源ケーブルで、赤外線LEDはプラグを差している間、常に点灯しているようです。つまりLEDは信号を送って同期したり、変調(周波数を変えて明度や速度を調整すること)したり、といった凝ったことはせず、単純に直流電流を使って、同じ明るさで点灯しています。ちなみに「テレビの友チャンネルGガイドfor Wii」でリモコンとして使うときだけは、リモコン信号の規格に合わせて高速に点滅しています。
同期や変調といった複雑な電子回路の場合は、自分で作るのは少々大変ですが、LED点灯回路ぐらいであればそれほど難しくはありません(中学生レベルの電子回路です)。このLED点灯回路を赤外線LEDを用いて自作すれば、オリジナルの赤外線マーカーのできあがりです。センサーバーは必要なくなります。PCでWiiRemoteを利用するのに、いちいちWii本体を起動してセンサーバーを点灯させる必要はありませんし、赤外線センサーを使った自作の作品を利用する上での自由度も広がるでしょう。
さて、ここでは赤外線について学んでおきましょう。まず、世の中の光にはすべて「波長」があります。波長が変わると色が変わって見えます。虹やプリズムを通して太陽の光を分解してみると「赤橙黄緑青藍紫」という順番に並んで見えます。赤色に近くなればなるほど長い波長、紫色に近くなればなるほど短い波長です。人間が肉眼で見ることができる波長「可視光」には限りがあり、実際にはもっと多くの波長が存在します。
『赤外線』と一言で言っても、本書で扱う赤外線は波長700nm〜2500nm近辺の「近赤外線」と呼ばれる赤外線です。他にも2500nm〜4000nmの「中赤外線」や、波長4μm〜1000μmの「遠赤外線」(熱線)があります。いずれも人の目では見えない光で、「電波」よりも波長の短い「電磁波」のことです。遠赤外線以上に波長が長くなると「マイクロ波」「メートル波」といった「電波」と呼ばれます。
人間が見える「可視光」は、せいぜい赤の750nmから紫の380nm程度で、それよりも短い波長になると「紫外線」となり、さらに波長が短くなると「X線」や「ガンマ線」と呼ばれ、性質が異なってきます。人体に吸収されたり、山や建物を通り抜けたり、お湯が沸いたり、無線通信できたり……と波長ごとにいろんな利用上の特性がありますが、特にWiiRemoteを使う上では波長1000〜800nmの「近赤外線」の光を使います。この近赤外光は、人間の目に見えづらいという以外は、普段我々が目にする光とほとんど変わらない性質を持っています。
近赤外線は目に見ることができませんが、可視光に近いため、目に見える光に似た拡散や反射が観察できます。目に見えないという理由から、自動ドアの接触センサー(フォトインタラプタ)や、テレビのリモコン、携帯電話同士の赤外線通信「IrDA」などに使われています。こうしてみると、街の中は赤外線センサーだらけなのです!なおインフルエンザで発熱している人を見分けるときなどにも使われる「熱画像カメラ」や「赤外線サーモグラフィー」とよばれる温度に応じて色をわりあてるカメラがありますが、これは黒体放射による7.5〜13μmの波長、つまり遠赤外線です。
文部科学省が2008年の科学技術週間で配布した「一家に1枚光マップ」が非常に良くできています。波長毎、ありとあらゆる光について、実際に使われている例が写真入りで紹介されているポスターです。PDF版が理化学研究所のホームページからダウンロードできます。
■「一家に1枚光マップ」
製作著作:文部科学省/監修:河田聡(独立行政法人理化学研究所)
http://www.riken.go.jp/r-world/topics/080404_2/lightmap.pdf
白熱電球なども目に見える光(可視光線)とともに、熱線と近赤外線を発光しています。「センシング用途」つまりWiiRemoteのようなセンサーとして光を使う場合には、可視光や熱は必要でなく、効率が良くないので、マーカー用光源として赤外線LEDの発光を使う場合が多いようです。
センシング用途では、赤外線LED光源にあわせて、フォトダイオード(PD)やフォトトランジスタといった特定の波長の光に対して反応する半導体とセットで利用されます。TVのリモコンや携帯電話やPCの近距離通信に使うIrDA(Infrared Data Association)規格、自動ドアもこの赤外線LEDと半導体素子のセットで構成されています。
目には見えない赤外線ですが、デジカメや携帯、Webカメラなどの画像センサーを使うことで、赤外線を画像として見ることができます。デジカメに利用されている画像センサーである「CCD」や「CMOS」は、本来、限定された幅の波長の光しか電子に変換できないのですが、赤・緑・青といった、人間が画像として利用するための受光特性以外にほんの少しだけ、可視光の外側の波長に感度があるデバイスもあります。この受光特性を利用して「見えない赤外線を見る」ことができます。この知識は非常に有効で、センサーバーを自作したときや動作確認をする上で、赤外線が見えるカメラを手元においておくと、赤外線LEDの点灯状態が見えて非常に便利です。
なお「ノクトビジョン」や「ナイトスコープ」と呼ばれるカメラは、この仕組みを使って目に見えない赤外線という明かりをつかって、夜中や暗闇でも撮影できるカメラを実現しています。
WiiRemoteの赤外線センサーはいったいどのような仕組みでどんな波長の光を感じることができるのでしょう?
実際には型番が公開されているわけではないのでよくわかりません。実験してみるしかないのですが、WiiRemoteに内蔵されているセンサーは、任天堂が台湾のPixArt Imaging社(http://www.pixart.com.tw/)に特注して開発した、特別な赤外線画像センサーといわれています。推測の範囲を出ませんが、低解像度のCMOSで、デジカメのような「画素値」ではなく赤外線の明かりの「重心の位置」を高速に出力するタイプのデバイスのようです。
一般的なWebカメラなどの画像処理速度が毎秒30-60フレーム程度なのに対して、このPSDは2次元の重心位置を出力するだけなので、高速です。値段と大きさにもよりますが、毎秒400フレームぐらい出せるデバイスもあります。
なお筆者が大学4年の時に書いた論文「光学的3次元位置検出法を用いたリアルタイム人間動作入力デバイス」では、浜松ホトニクス社製のPSD(Position Sensing Device)カメラを使いました。このカメラもWiiRemoteのCMOSに似た半導体デバイスですが、当時100万円以上で研究室が購入したことを記憶しています...!
[!]ここから先は電子回路等の知識がある方、半田ごての扱いなどが可能な方のみ実践に臨んでください。本書を原因とするPCの破損や火傷その他の不利益について、著者や出版社は責任を持ちません。
こちらが自作USBセンサーバーの回路図の例です。
材料としては、赤外線LEDと抵抗、基盤、半田ごて一式、あとは不要なUSBのケーブルを1本、切断して作ります。平型のUSBプラグなら、金属端子面を下にして左から順に1,2,3,4と4つの端子がついています。この1番が電源となるVCC(+5V)で、4番がGND(−)です。台形のUSBの端子の場合は台形の長編を下側にして右上が1番、右下が4番になります。USBのケーブルを適当な長さで切断して、テスターなどで確認しながら、図中のUSB1番を回路の+5Vに、USB4番を回路のGNDにそれぞれ半田をつかってつなぎます。
赤外線LEDは普通のLEDと同じく、秋葉原や通販で入手できる電子部品のお店で買うことができます。
型番:OSIR5113A
VF=1.25V(@20mA)、ピーク波長940nm、半減角15度、推奨電流20mA
http://akizukidenshi.com/catalog/g/gI-00656/
値段は100個入りで700円と、電子部品としては気軽に買える部類に入ります。購入前に、仕様書をよく見てください。重要なのは「ピーク波長」、「VF(DC Forward Current)」、「推奨電流」、それに「半減角(50% Power Angle)」と呼ばれる値です。ピーク波長は保障はできませんが、WiiRemoteには900〜1000nmの間ぐらいがよいようです。半減角は、正面を100%としたときに明るさが半分になる角度です(LEDには広角のものと、正面に指向性の高いものがあります)。なおここで紹介した「OSIR5113A」は小坂研究室でも利用実績があるそうです。
またVF(mA)によって制限抵抗の値が決まりますので、それに合わせた抵抗もいっしょに買ってください。制限抵抗を間に入れないと、無制限に電流が流れてしまい、非常に危険な光源になってしまいます。最悪PC本体を壊すかもしれません。
この警告メッセージはUSBポートの電流がUSBの規格で定められた許容量である500mAを超えたときなどに表示されます。配線が甘くてVbusとGNDがショートしているときなども同様に表示されますのでこのメッセージが表示されたときは、すばやくUSBポートからプラグを抜き、テスターなどでショートがないか確認してください。
またLEDはダイオードという電流を一方向にしか流さない性質を持ちます。アノード(足の長いほう)からカソードへ流れますが、逆には流れません。赤外線が見えるデジカメを傍らにおいて、仮組みしたりテストで駆動してみたりしながらやらないと、足を切った後では極性がわからなくなりますので注意しましょう。
このように非常に小さなUSBセンサーバーも作ることができます。いろいろと応用の幅が出てきます。
半田ごてが自信を持って握れる方のみお勧めします。半田ごてで火傷したりしても、本書は責任を持ちません。
赤外線は目に見えないので、回路図の赤外線LEDに加えて、通電しているかの確認のために可視波長(赤や緑)のLEDを使ってパイロットランプを作るとよいでしょう。
他の課題は自作センサーバーを作らなくても挑戦できます。三角形の認識でちょっと数学パズルがありますが、楽しんで解いてみてください。WiiRemoteは4点まで検出できますので、三角形の向きが拾えたり、面が推定できたりと、いろいろな応用があります。
WiiRemoteを使ってラジコンカーを操作しようというアイディアを実現した人は(世界には)意外にいるようです。
http://gigazine.net/index.php?/news/comments/20061222_wii_rc/
http://www.inside-games.jp/news/329/32904.html
この2006年12月のニュース(Gigazine)で紹介されている例は、WiiRemoteからの信号をBluetooth経由でPCに受信して、それをラジコンカーのコントローラー(プロポ)などに送信する方法です。
「WiiRemote→PC→ラジコンプロポ→ラジコン」
もうひとつの動画も同様で、WiiRemoteに飽きたらず、ヌンチャクやWiiBoard、さらにiPhoneを使ってラジコンカーを操作する動画を公開しています。
人がやったことをただ真似ても面白くありません。ここでは、上のような構成ではなく、
「Wiiremote→PC→WiiRemote→ラジコン」
というプロポすら使わない方法で、ラジコンを操作してみたいと思います。
正確には「WiiRemoteでラジコンを操作」ではなく、WiiRemoteをラジコンと合体、つまり「WiiRemoteをラジコン化」した『WiiRemoteTank』を開発します。
ほとんどロボット兵器です。武器はありません。
原理は簡単です。WiiRemoteあるプレイヤーインジゲーター(4つの青色LED)は、WiimoteLibのSetLEDs関数で信号を送るだけで、ON/OFFの出力ができます。このLEDの電力をモータードライバーに接続することでモーターを制御することができます。
モータードライバーとは、その名の通りモーターを制御する電子部品です。2つの信号の組み合わせによって、モーターの回転、反転、停止を行うことができます。たとえば「[01]で前進」「[10]で反転」「[00]でストップ」といった2bitのデジタル信号で制御できますので、LEDの点灯制御を出力させてやるだけで、モーターの動作をコントロールできるわけです。
ここではロボットの開発を演習している、大阪大学応用理工学科機械系3年生の演習「機械創成工学演習」の教科書を参考にしています。
[URL] http://www.robot.ams.eng.osaka-u.ac.jp/hosoda/enshu/start.html
■Toshibaのモータードライバー「TA7291P」データーシート
[URL] http://www.robot.ams.eng.osaka-u.ac.jp/hosoda/enshu/doc/TA7291F_TA7291SG_ja_datasheet_070613.pdf
ここで紹介されているモータードライバー「TA7291P」を使います。WiiRemoteのLEDは4つあるので、このドライバーを使って2個のモーターを制御することが可能です。モーター2個で操作できる戦車といえば、「タミヤタンク工作基本セット」でしょう。オンラインで1,500円で購入できます。
http://tamiyashop.jp/shop/product_info.php?cPath=17_149&products_id=70108
続いてコントローラー用のWiiRemoteと制御用のWiiRemoteの2台を用意します。まず制御用のWiiRemoteを分解し、LEDの信号を取り出します。
小坂先生は「WiiRemoteを分解」とあっさり書かれていますが、WiiRemoteのネジは特殊なドライバーでなければ回すことすらできません。もちろん全く保証外の行為ですが、そのようなドライバーなど無くても開けようと思えば開けることはできます。
【参考】[URL] http://ameblo.jp/akihiko/entry-10056910390.html
特殊ネジをはずしたら、普通のネジを入れておきましょう。
さて、この先一番難しいのは、「WiiRemoteを分解しLEDに配線し、元通りに収めること」かと思います。かなりの集中力が要求されます。小坂研究室のTipsにWiiRemoteのLEDを換装する記事があるので参考にしてください。
[URL] http://www.kosaka-lab.com/tips/2009/05/wiiledled.php
テスターを使って確認しながら進めてください。LEDのための信号を拾って、モータードライバーに接続します。
コントローラー用のWiiRemoteの傾きをPCが読み取り、その傾きに合わせて、制御用WiiRemoteに信号を送るプログラムを別途作成しておきます。
モーターが2つありますので、加速度センサーの傾きに合わせて2つのLEDに対して[00]〜[11]を出力するようなプログラムで十分でしょう。
完成版の動画は小坂研究室にて見ることができます。
[URL] http://www.kosaka-lab.com/tips/2009/05/wii-2.php
コントローラー用WiiRemoteを傾けると、WiiRemoteTankが進みます。今回はWiiRemoteをプロポにして操作していますがWiiBoardなどで操作しても面白そうです。
LEDの出力はまだ2チャンネル分残っていますから、他にも武器やデコレーションを装備したり、WiiRemoteの赤外線カメラを利用して、赤外線を自動に追尾して動くロボットや、ライントレーサー(黒い線に従って動くロボット)としても展開することができるでしょう。
今回はWiiRemoteをロボットへ内装する例として、LEDから信号をとる方法を紹介しました。LED以外にスピーカーのアナログ出力や、拡張端子のI2Cインターフェースを使方法も可能性がありそうです。ここでの例ではロボット戦車ですが、かわいらしいモンスターのぬいぐるみを着せたり、ゲームと連動させたりすると、ビッグなビジネスチャンスがありそうです(笑)。
このセクションでは、第8章で学んだ技術を応用して、ハンディキャップのある方にWiiRemoteを使って、自由にコンピューターを触れるように、何ができるか?を中心に考えてみたいと思います。
「体が不自由」と一言でいっても、先天的に不自由な方だけでなく、事故や病気で不自由になった人や、一時的に不自由なひとなどそれぞれです。
そんな方々にWiiRemoteだけでブラウザーを操作したりメールを書いたりすることができるインターフェースを開発できれば、半身不随の方などの人生を大きく変えるかもしれません。
演習問題とはいえ、せっかく作ったソフトウェアですから、いいものができたら公開しましょう。なんと言ってもWiiRemoteとソフトウェアだけですから、一般的な医療福祉機器に比べて非常に気軽に利用できます。
ちなみに「ハンディなんて自分には関係ない」と思っていると、損をします。かくいう筆者も、生まれたばかりの自分の赤ん坊に2時間おきに授乳しなければならないときがありました。夜は論文を書いたりしていることが多いので、妻に代わって私が担当なのですが、ミルクをあげている時は両手がふさがっているので何もできません(とても眠くなる)。この時間を使ってメールに返信したり、ブラウザーを触ったりといった簡単な作業ができれば、子育て初期の授乳ストレスはどんなに有意義な時間になったでしょうか!
つまり両手があいている元気なうちに「こういうソフトウェアを作っておけばよかった!!」と何度も後悔した、ということです(笑)。
「幼児に持たせるロボット」は加速度センサーの値をうまく使って、眠ったり歩いたり、スピーカーを使ったり…とアイディアが広がります。フランス語では安心塗りぐるみのことを「Poupee」といいますが、筆者は過去にこのアイディアで幼児向けのラクガキシステム「Papier Poupee Painter」を開発したことがあります。振るだけで塗り絵っぽいことができる作品でした。
幼児もインタラクティブ技術の視点では「ハンディを持つユーザー」とあまり変わりありません。いわゆるペイントブラシのような色を選ぶような機能は限定して、ラクガキの面白さだけを際立たせる、という仕掛けにWiiRemoteの無線機能と安定性能は大変役に立ちました。
WiiMedia:Painting "Papier Poupee Painter" ver.Alpha (YouTube動画)
[URL] http://www.youtube.com/watch?v=S8kYQbfN_9I
WiiRemoteの赤外線センサーは非常に高速で、使い道がたくさんあります。ここでは2点の赤外線LEDの情報だけで、どこまで正確な「奥行きを含めた3次元座標」が取得できるか理論的に突き詰めてみます。
ここでは、幾何的な方法をつかって2点のLED座標からWiiRemoteの3次元奥行きつきの座標を取得する方法を考えます。考え方のトレーニングだと思って読んでみてください。
いま、P(x,y,z)という位置にあるWiiRemoteが、原点O(0,0,0)という場所にあるセンサーバーに向かって赤外線センサーを向けたとき、WiiRemoteで取得できる2つのLED群の位置を(IrX1,IrY1), (IrX2, IrY2)とします。
この2つのLEDのX座標、IrX1、IrX2について、その差の絶対値IrZについて仮説を立ててみます。
IrZ = | IrX1 - IrX2 | ...(9-1)
いまこのWiiRemoteを原点から遠ざかる方向に移動させた場合、このIrZの値は遠近法に従って、遠くに行けば遠くにいくほど2つのLEDの差は小さくなります。WiiRemoteとセンサーバーの距離(以後PosZと標記、単位はmm)は比例関係にあるかもしれません。
式で表せば、
PosZ = K * IrZ ...(9-2)
という関係がつくれる可能性があります。もしこのKが簡単に求まるなら、WiiRemoteの赤外線LEDの値(IrX1, IrX2)から、奥行きPosZが算出できそうです。
なお、この式(9-2)でのKは比例関係を表しているだけで、定数かどうか、つまり1次関数なのかどうかは今のところわかりません。より複雑な2次関数以上かもしれません。実際に測定してみることにいたしましょう。
まず、赤外線の測定値が取得できるプログラムを用意しましょう。新たに開発するのが面倒であれば「WiinRemote」などを使って測定してもかまいません。
いま、原点O(0,0,0)に自作のLEDセンサーバーの中心があり、センサーバー内にある2つのLED光源グループ間が、X軸方向に200mm離れているとして、これをLED2点間距離dと呼びます。2つのLEDの中点がこれから実験する座標系の原点O、{x,y,z}={0,0,0}にあり、測定に使用するWiiRemoteは座標P(x,y,z)にあるとします。WiiRemoteをセンサーバーからまっすぐ遠ざけていく方向を、求めたい「奥行きZ」、左右方向をX、上下方向をYと呼びます。Pの座標ではなく距離を表現するときは「PosZ」(単位はmm)と呼ぶことにします。
測定を始めましょう。測定しやすい床などの安定した場所にセンサーバーを置きます。このとき床面に赤外線が反射していると実験が失敗してしまいますので、WiiRemoteやデジカメを使って、赤外線光の強い反射がないか確認しましょう。床がどうしても反射する場合は紙を使って反射を拡散させると良いでしょう。センサーバーを三脚に乗せるなどしてもよいですが、できればWiiRemoteを同一平面に置いてください。
まずWiiRemoteを原点Oに近い場所に置き(ぶつかってしまいますので)、ゆっくりとセンサーバーから遠ざけていきます。最初、WiiRemoteとLEDの距離があまりに近すぎると測定できません。赤外線の発光強度が強すぎたり、1つのLEDしかセンサーの視界に入らなかったりすることに起因します。徐々に奥行きを広げていくと、PosZ = 300mm程度の距離になると、個々の赤外線測定値IRX1, IRX2を読むことができるので、測定値を測定シート(EXCEL等に直接入力しても良い)にメモしていきます。
今回の実験では奥行き方向の距離をそれぞれPosZ = {330, 660, 990, 1320, 1650, 1980, 2310, 2640, 2970, 3300}(mm)の10種類としました。また左右方向の特性も確認するために、X方向にもそれぞれPosX = {0, 330, 660}(mm)の3種類で測定しています。全ての組み合わせで30通りありますので根気よく、流れをつかんで、ふたりひと組など実験すると良いでしょう。
以下の表のように実測値をまとめます。
[Pos] | X = 0 | X = 0 | X = -330 | X = -330 | X = -660 | X = -660 |
---|---|---|---|---|---|---|
PosZ | IRX1 | IRX2 | IRX1 | IRX2 | IRX1 | IRX2 |
330 | 925 | 140 | ― | ― | ― | ― |
660 | 737 | 348 | ― | ― | ― | ― |
990 | 628 | 368 | 1003 | 755 | ― | ― |
1320 | 645 | 450 | 941 | 746 | ― | ― |
1650 | 598 | 442 | 874 | 717 | ― | ― |
1980 | 572 | 442 | 821 | 690 | ― | ― |
2310 | 583 | 471 | 768 | 656 | 957 | 845 |
2640 | 569 | 471 | 734 | 637 | 923 | 824 |
2970 | 597 | 512 | 709 | 622 | 854 | 768 |
3300 | 576 | 499 | 710 | 632 | 855 | 777 |
「―」となっている箇所は赤外線LEDは常の値が読めなかった場所、つまり赤外線センサーの画角の外側です。実際に測定可能なポイントは22点になりました。このデータをPosZを横軸、赤外線の測定値Ixを縦軸としてプロットすると以下のようになります。
グラフを見たところ、IrX1とIrX2は奥行きPosZに対して、単純な比例関係を持ってはいないようです。しかし左右に対してはほぼ対象といえるでしょう。そしてPosXが中心から外れることで(PosX = -330mm, -660mm)、左右の対象性は崩れていくようです。
PosZ | X=0 | X=330 | X=660 |
330 | 785 | ||
660 | 389 | ||
990 | 260 | 248 | |
1320 | 195 | 195 | |
1650 | 156 | 157 | |
1980 | 130 | 131 | |
2310 | 112 | 112 | 112 |
2640 | 98 | 97 | 99 |
2970 | 85 | 87 | 86 |
3300 | 77 | 78 | 78 |
次に各位置での2つのLEDの差の絶対値IrZについて算出します。PosZを横軸、IrZと縦軸にとったグラフにプロットすると、PosZに対する反比例に見えます。今度はX ={0, 330,660}に対してそれぞれ、PosZを横軸、そして本来定数であるはずのK、すなわち「d / IrZ」を縦軸として図9-32(右)のようにプロットしてみます。
XがそれぞれX = {0, 330,660}と異なるにもかかわらず、見事に1本の直線に乗っています。この直線の傾きをPosZの最大-最小から求めると
K = 1284.64 * PosZ - 0.02 ...(9-3)
という、KとPosZの直線の1次方程式で表現することができます。
このKを用いて実測のPosZと、最大最小など2点程度のIrZを計測すれば、その間の奥行きZを簡単に求めることができます。
図9-33は、この理論を用いて算出した奥行きと、実測の奥行きがほぼ一致することを示しています。
水平方向の画角についてはこのセクションで紹介したデータをもとに、算出することができます(意外と狭いです)。
広範囲化についてはさまざまな方法がありえますが、以下の図をヒントに考えてみると良いのではないでしょうか。
加速度センサーと連携する方法以外にもアイディアはあります。WiiRemoteの赤外線センサーはデジカメのCCDなどの画像センサーと異なり、歪んでもボケても得られる値は同じですので、「斜めから見る」という方法で実質の測定範囲を広くする方法はあるでしょう。
次のセクションで紹介する、ジョニー・リーのサンプルも、赤外線ポインタを「斜めに見る」ことで、広い範囲が測定可能になるコードを含んでいるようです。
アメリカ人のジョニー・リー氏(Johnny Chung Lee, http://johnnylee.net/)は、カーネギーメロン大の学生当時、WiiRemoteを使ったプロジェクト「Head Tracking for Desktop VR Displays using the Wii Remote(WiiRemoteを使ったデスクトップVRディスプレイのための頭部追従)」を2007年12月21日にYouTubeで公開し、今日まで721万回以上再生され世界的に有名になりました。
http://johnnylee.net/projects/wii/
他の2つのWiiRemoteプロジェクトも、それぞれ200万回以上再生されています。
過去のWiiRemote関係は氏のWebサイトにまとめられています。すべてソースコードと実行ファイルが入手できます。まずは動画とともに以下の日本語解説を読んでみてください。
「アイディア一発勝負!」の非常にシンプルなデモですが、動画公開の日付を見てもわかるように、開発のスピードがとても速いことも話題になりました。
またジョニーは研究者としてもしっかりしていて、WiiRemote以外にもローコストな特殊カメラや、プロジェクターを使って好きな場所(例えば、手に持った扇)に好きな映像を投影する研究を行っています(だからこそWiiRemoteの活用も早かったのですね)。他にも写真作品や、巨大なペイントボールパチンコの制作など、いろいろ楽しいプロジェクトを実現されております。
現在、彼はマイクロソフトの研究所で実用科学(Applied Sciences)グループで働いているため、表立ったWiiRemote関係の活動はありませんが、長い空白の後、なんと2009年6月1日のブログエントリーで「Project Natal」に関係していることを告白しました。
[URL] http://procrastineering.blogspot.com/
■Microsoft Project Natal(YouTube動画)
[URL] http://www.youtube.com/profile?user=xboxprojectnatal
「Project Natal」とは、動画を見ていただければわかりますが、任天堂Wiiに対抗するマイクロソフトの全身型ゲームインターフェースです(詳細は次の章で紹介します)。
彼の研究者としての論文も非常に面白いです。関連する面白い論文があればどんどん読んでみましょう。なお海外の論文をすばやく調べるときには、「Google Scholar」、公開されているプログラムコードを検索するときは「Google Code」が役に立ちます。日本語の論文が気になるときは「CiNii」という国立情報学研究所が運営している論文検索エンジンを使うと良いでしょう。
論文検索「Google Scholar」
[URL] http://scholar.google.com/intl/ja/
国立情報学研究所「CiNII」(キーワード"Wii"で検索)
[URL] http://ci.nii.ac.jp/search?q=Wii
ソース検索「Google Code」[URL] http://code.google.com/
本章の最後は、WiiRemoteにアクセスするAPIを自分で作る課題です。DDKを使った、C++のプログラミングです。特に用事がない方は読み飛ばしていただいてかまいませんが、他の言語などにWiiRemoteのAPIを移植するときなど、役に立つかもしれません。
「WiiMedia2」
[URL] http://code.google.com/p/wiimedia/source/browse/#svn/trunk/Wiimedia2/
上記、Google Codeで公開されている、DDKとC++をつかった機能最小限の自作APIです。
筑駒パ研「電脳2007:Wiiリモコンをもう一回見直してみます」by Iketaki
[URL] http://paken.s1.hayasoft.com/files/down/denno2007_wii.pdf
筑波大学附属駒場中・高等学校パーソナルコンピュータ研究部の文化祭で発行された部誌のPDF版です。非常に丁寧に解説されております。
「WiiMedia2」から重要なOpenWiiRemoteHID()近辺を引用しておきます。
HANDLE wm_base::OpenWiiRemoteHID(void) { //LONG iHIDs; DWORD indexHID = 0; BOOL bEndofDeviceList = FALSE; BOOL bDeviceDetected = FALSE; PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceDetail = NULL; DWORD size = 0; DWORD RequiredSize; HIDD_ATTRIBUTES Attributes; HidD_GetHidGuid( &guidHID ); hDeviceInfo = SetupDiGetClassDevs( (LPGUID)&guidHID, NULL, (HWND)NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT ); if ( 0 == hDeviceInfo ) { return INVALID_HANDLE_VALUE; } //失敗 do { bDeviceDetected=FALSE; //HIDインタフェースを列挙 deviceInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if ( SetupDiEnumDeviceInterfaces (hDeviceInfo, NULL, &guidHID, indexHID, &deviceInfoData)!=0 ) { printf("[%d] HID found.\n",indexHID); //列挙したHIDの詳細を取得 SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &deviceInfoData, NULL, 0, &size, NULL) ; DeviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(size); DeviceDetail -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); printf("HID detail size =%d.\n",DeviceDetail->cbSize); SetupDiGetDeviceInterfaceDetail (hDeviceInfo, &deviceInfoData, DeviceDetail, size, &RequiredSize, NULL); hWiiRemoteHID = CreateFile( DeviceDetail->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL); bDeviceDetected = FALSE; Attributes.Size = sizeof(Attributes); if ( HidD_GetAttributes( hWiiRemoteHID, &Attributes ) ) { if ( Attributes.VendorID == 0x057e && Attributes.ProductID == 0x0306 ) { if ( HIDP_STATUS_SUCCESS == GetDeviceCapabilities( hWiiRemoteHID ) ) { printf(" WiiRemote found.[V=0x%04d,P=0x%04d]\n", Attributes.VendorID,Attributes.ProductID); bDeviceDetected = TRUE; } else { printf(" GetDeviceCapabilities() failed.\n "); } } else { printf(" It didn't match with WiiRemote.[V=0x%04d,P=0x%04d]\n", Attributes.VendorID,Attributes.ProductID); CloseHandle( hWiiRemoteHID ); } } else { printf(" HidD_GetAttributes() failed.\n"); CloseHandle( hWiiRemoteHID ); } free(DeviceDetail); } else { bEndofDeviceList = TRUE; } indexHID++; } while ( (bEndofDeviceList == FALSE) && (bDeviceDetected == FALSE) ); if ( bDeviceDetected == FALSE ) { printf("Finally, I couldn't find any WiiRemote.\n"); hWiiRemoteHID = INVALID_HANDLE_VALUE; } else { printf("Yes, I found a WiiRemote.\n"); } SetupDiDestroyDeviceInfoList(hDeviceInfo); return hWiiRemoteHID; }
最初から手探りで作るのは大変ですから、もし自分自身でAPIを作成に挑戦する場合、上のコードのほかに、WiimoteLibやWiiYourself!を参考にすると良いでしょう。
「GetDeviceCapabilities()」近辺が重要で、これが個々のPC環境やBluetoothスタックによって異なります。それに対して高度に完成しているAPIは、WriteFile()などを工夫して、確実に通信が行えるようにしています。その他、レポートタイプの設定などは「Wiili.org」や「WiiBrew」などのWikiサイトで情報をあつめて構築していきます。
このセクションでの演習問題は難易度を特に高く設定しています。特に腕に自信がある方のみ挑戦してみてください。
これで本章は終わりです。いままで通りステップバイステップの解説を行ったところもありますが、ほとんどがアイディアの要点だけ、あとは演習問題という構成で解説よりも、課題設定に力点を置かせていただきました。
読者のスキルを幅広くとった本書において、英語で書いた論文をそのまま落とし込むのは難儀しました。残念ながら割愛したネタも数多くありますが、筆者の経験したWiiRemoteプロジェクトのいくつかを、初めて日本語で解説する機会に恵まれました。
本書でWiiRemoteプログラミングを学んだ皆さんが、本書での「ネタ」をきっかけに、YouTubeなどを通して、より多く世界中の人々と交流されることを祈っております。その際「元ネタはWiiRemote本より」と、本書を引用元に書いていただけるとより励みになります。
またWiiMotionPlusの登場などで、本書の内容も古くなったり「もっといい方法があるよ!」といったご意見もあると思います。上記で紹介した「WiiMedia」プロジェクトや、Google Groupsにてコミュニティを立ち上げておりますので、ご活用いただければ幸いです。
[URL] http://akihiko.shirai.as/projects/WiiRemote本書のポータルとしてここに情報をまとめています。
Google Code「WiiMedia」
[URL] http://code.google.com/p/wiimedia/
本書で紹介したサンプルなど関連のコードをここで共有しています。
Google Groups「WiiRemote」
[URL] http://groups.google.com/group/wiiremote
ご質問や「こんなことできたよ!」という情報をお寄せ下さい。
この章は本書の最終章です。今まで筆者とともに長い長い旅にお付き合いいただき、ありがとうございました。
ここではWiiRemoteでのプログラミングから少し離れて、インタラクティブ技術の未来を読者のみなさんといっしょに考えてみたいと思います。
まずは本書で扱ったWiiRemoteプログラミングに関わっている、世界の貢献者から未来を読み解いてみたいと思います。
WiiRemoteプログラミングにおけるPCでの貢献者といえば、API開発を行ったBrian Peek氏とgl.tter氏です。ふたりの偉大なハッカーに、メールでのインタビューに応じていただきました。
Q:どちらにお住まいですか?
A:アメリカ、ニューヨークのGlenvilleにすんでいます。ニューヨーク市から120マイルほど北にいったところです。
Q:昼間のお仕事は?
A:ソフトウェアコンサルタント、著者、そしてオールアラウンドな.NET屋ですね。
Q:WiimoteLibを開発しようと思ったモチベーションは?
A:私は「Coding4Fun」というウェブサイトの著者の一人なので、プロジェクトにトピックが必要でした(笑)。実際には、そこでは「できるかどうか見た」というだけでした。その後、完全なものを書いた後に、私はWiiRemoteをPCで使うことの巨大なポテンシャルを具現化できた、という感じです。
Q:何か面白いエピソードはありませんか?
A:もっとも面白いことといえば「NEWSWEEK」誌によって取材され、記事として世界中に配信されたことだと思います。エキサイティングだった...。
Q:将来の夢や読者へのメッセージをどうぞ
A:ただもう巨大な「Thank you」をこのライブラリを使って楽しんでくれた皆さんにお伝えしたいですね。これは大変な仕事でしたが、すばらしい(amazing)プロジェクトが、このライブラリを使って登場してくるのを拝見するのは楽しかったです。(小坂研究室の「La Fleche l'odeur」などを見て)私の仕事が、こんなにも多くの異なる使い道に使われていることを知って、非常に満足しています。 ―――ブライアン
Q:どちらにお住まいですか?
A:「イギリスのgl.tter」とだけ名乗らせてくれ。
Q:「WiiYourself!」を開発しようと思ったモチベーションは?
A:もとはといえば、驚くべき(amazing)WiiRemoteに絶好な、C++のゲームプロトタイプに取り組んでいたんだ。僕は当初Brian Peek氏の.NETのコードからライブラリを改造してた。広範に書き直し、自分の好きな方法で動くように最適化し、もちろん複数のWiiRemoteのサポート、よりよいスタックのサポート、自動検出、スピーカー機能など、新しい機能を追加した。取り組みはじめてベータを公開したのが、2007年の6月。バージョン1.0は2008年2月13日公開だね。僕のゲームアイデアは、1対1のモーション割り当てが必要だったんだ。それはWiiRemoteだけでは成しえなかった。カルマン(Kalman)フィルターとか、自分より数学が得意な、それを動くようにしてくれる「誰か」が方法を見つけてくれるに違いない、それがこのライブラリを公開したひとつの理由なんだ。
Q:「どうしてWiiYourself!なんて名前なの?」
A:名前は自己説明の一部「Get your self Wii'ed up」・・・WiiRemoteがキミのアプリを可能にするよ!って意味。イギリスのほかの人が書いた別のC++ライブラリに対する穏やかなジョークなんだ。彼はそのライブラリを公開したらNintendoが首に噛み付くんじゃないかと恐れたので、僕は「WiiYourself!」を書いたんだ。
Q:どうして「!」が入っているの?
A:単なる強調。「やれ!いますぐ!」ってこと(笑)。
Q:Wii本体を持ってる?
A:残念ながら無い。見て楽しむ以外は遊んだこと無いんだよ。WiiYourself!のWebサイトに動画がある、僕のプロジェクトのプロジェクトのひとつである光線銃ゲーム「Q2Gunfrenzy」っていうコンピューター関係じゃない人がプレイできるようなのがいいと思ってる。銃を使うのは自然な行為だからね。
Q:…えーと、最近の興味は?
A:マイクロソフトの「Project Natal」は見た?全身スキャンと音声認識・・・。MLに紹介したら、みんな恍惚ものだったよ。
Q:将来の夢や読者へのメッセージをどうぞ
A:僕らは、1対1のモーションの実現には、WiiMotionPlusのジャイロスコープがないと不可能だって知っている、だからWiiYourself!でサポートしたいと思っている。 ――――"gl.tter in UK"
■
gl.tter氏は典型的なイギリスのハッカーという感じの人物ですが、実直な人物だと感じます。MLでの返答はこまめですし、なにより情熱があります。なおWiiYourself!はv1.13から、発売されたばかりの「WiiMotionPlus」をサポートするようです!
2009年6月、任天堂はWiiRemoteの追加オプション「Wiiモーションプラス」(本書では「WiiMotionPlus」と表記)を発売しました。
WiiMotionPlusとともに発表されたゲームタイトル「Wii Sports Resort」は、その名の通り、リゾート感たっぷりのゲームです。
アメリカで毎年6月頃開催される「E3(http://www.e3expo.com/)」はElectronic Entertainment Expoの略で、ちょうど日本で言えば東京ゲームショウ(TGS)のようなイベントです。
E3に並ぶ、アメリカで開催される、重要なゲーム技術のカンファレンスが「GDC(http://www.gdconf.com/japan/japan.html)」です。Game Developers Conferenceの略で、毎年3月に開催されます。日本には同様の業界カンファレンスとして「CEDEC」というイベントがあります。E3がセールス向け、GDCが技術向け、というすみ分けでしょうか。
実のところ、筆者はこのWiiMotionPlusが、2008年の「E3」で発表されてから、実際に発売直前になるまであまり興味はありませんでした。本書で紹介してきたように、現状のWiiRemoteでもかなりのことができますので、任天堂自身が「WiiRemoteのセンサーが不十分だ」ということを言って回っているようだなあ、と逆に冷めていたぐらいです。しかし発売直前になって、任天堂公式ホームページ『社長が訊く』シリーズで公開された、WiiMotionPlus開発秘話を読んで、衝撃を受けました。
『社長が訊く』「Wiiモーションプラス」+「Wii Sports Resort」■「2種類のセンサーの組み合わせで」
http://wii.com/jp/articles/wii-motion-plus/crv/vol/page2.html
『社長が訊く』シリーズは任天堂岩田社長が直接、新しいプロジェクトに関係した社員をインタビューする形式で構成されています。
このページの電子回路担当の伊藤氏による『そこで1秒間に1600度までセンシングできるようにしました』というやりとりを読んで、私は飲んでいるコーヒーを吹きそうになりました(笑)。
「ヒューマンインターフェースにジャイロスコープを使う」というアイディアはWiiMotionPlusがパイオニア(先駆者)ではありません。アメリカのジャイレーション社(Gyration, http://www.gyration.com/)が既に、動きを検知してPCを操作できる空中ジャイロマウスなどを開発しており、マイクロソフトの「Windows XP Media Center Edition」のリモコン「メディアセンター・リモート」に採用されていました。
メディアセンター・リモートは、左右方向のリモコンの振りだけでポインターを動かすことができます(赤外線センサーは不要です)。しかし実際に試してみるとなんだか「もったりとした動き」で、さらにあまり動作量が大きくないので、目的の位置、たとえば画面の端までポインターを動かすために何度もリモコンを振りなおす必要がありました。
このジャイロスコープ(コリオリの力を利用した回転速度センサーであることが多い)を、Wii用に従来の検出幅を「5倍」に高めるとは…日本のゲーム機器産業、恐るべし、です。
さらにこの『社長が訊く』を読み進めると、もっと面白い情報が書かれています。本書で紹介してきたような、新しいインタラクションを作るためのさまざまな実験を組織的に行っていることも読み取れます。本書をここまで読み解いた読者の皆さんであれば、任天堂側の開発者でもさまざまな苦労をしていることが理解できるのではないでしょうか。
特にSDKを担当した太田氏の活躍は、読んでいてワクワクできるはずです。話の中で紹介されている「人形デモ」の方位角での回転(WiiRemoteを立てたときのY軸回転)のモーションは、一般のゲームファンには「?」かもしれませんが、本書の読者は一見に値します。
その他にも、本書で扱った話題に近い話も出てきます。
『社長が訊く』「Wii Sports Resort」■「Wiiモーションプラスで寸止めも」
[URL] http://wii.com/jp/articles/wii-sports-resort/crv/vol/index.html
このページにある「人形デモ」の動画は必見です!
■「魔法の技術で70人とチャンバラ」
[URL] http://wii.com/jp/articles/wii-sports-resort/crv/vol/page3.html
宮本氏が「魔法の仕組み」と説明する「振れば振るほど正しくなる」というあたり、本書の読者ならどうやって実現しているのか、想像して、試作してみたくなってくるのではないでしょうか。
「寸止め」は第7章や第9章で扱っているとおり、「振り抜く力」と「止める力」の違いで、従来の加速度センサーでも検出できるのですが、その処理には理論上、数ミリ秒ですが、ディレイがうまれてしまいます。4000円以下で入手できるWiiRemoteとソフトウェア技術だけでは解決できない限界がそこにはあります。
この『社長が訊く』で読み取れるメッセージは、単に「従来の加速度センサーにジャイロスコープがつきました」という話ではないのではないでしょうか。複数のセンサーを組み合わせ、そして、本書で解説したようなインタラクションを実現するためのソフトウェア技術があり、さらに綿密なテストの繰り返しによるフィードバック開発によってはじめて、「より自然で直感的で楽しい操作感」を極め、高度なインタラクションがより高度にゲーム体験において実現できるようになっていくのです。その企業姿勢を任天堂が自ら、世界のゲームファンとゲーム産業に向けて発信しているのだと感じます。
さて、日本人の魂がこもった「WiiMotionPlus」の開発ですが、上記『社長が訊く』のページは(多少の遅れを持ってはいますが)英語や各国語に素早く翻訳され「wii.com」で全世界に向けて公開されています。
私の肌で感じた感覚ですが、日本側の反応よりも、英語圏のほうが、WiiMotionPlusに対する開発者の盛り上がりは大きいように感じます。開発者コミュニティでは、6月の日本発売よりも前に、WiiRemote研究者が集まるポータル「WiiBrew」において、WiiMotionPlusに接続するための情報が報告されています。
[URL] http://wiibrew.org/wiki/Wiimote/Extension_Controllers
NOA(任天堂アメリカ)の戦略もあるのでしょう、北米市場では日本の「Wii Sports Resort」の発売日2009年6月25日よりも先に、「EA SPORTS Grand Slam Tennis」とともに2009年6月8日に「Wii Motion Plus」が、市場で入手できるようになりました。アメリカ、ヨーロッパでの「Wii Sports Resort」は7月発売なので、本格的なWiiMotionPlusの衝撃は、このあとのフェーズで広がってくるはずです。
任天堂公式のミドルウェアである「AiLive」も発売前の早い段階で対応製品を出していました。今後、WiiMotionPlusを使った新しい世代のインタラクション開発は、より本格的にさまざまな陣営を巻き込んで展開するものと予測します。個人レベルの研究者、学生プロジェクトといった活動が、企業の需要と接点を持つチャンスでもあります。
■
任天堂が「ReVolution」で起こした「革命」はこの後、どこへ向かうのでしょうか?当初はライバルであるマイクロソフトやソニーも、新しいコントローラー(と新しいインタラクションへのアプローチ)に対して明確な動きは出してきませんでしたし、当のゲームユーザーも「住み分け」という方向に向かったように感じます。
ユーザーの感覚はそうであったとしても、ゲーム産業の研究開発は5年を周期として、先を見越して動いています。プラットフォームの中心になるような技術要素や特許、そして人材の確保については、各社とも大きな動きが明確になってきました。
本書発刊は「新プラットフォーム発売から2年半」という、この節目の時期でもあります。
第9章の最後に紹介したジョニー・リー氏のように、大学の研究者からマイクロソフトで働き始めた人物もいます。ジョニーのBlogでも公式に発表があった「Project Natal」とはいったい何なのでしょう?
http://www.xbox.com/en-US/live/projectnatal/
「Project Natal」は2009年6月1日、マイクロソフトがE3で開催した記者会見で発表されました。
■YouTubeチャンネル「XboxProjectNatal」なおこの記者会見には映画監督スティーブン・スピルバーグ氏も発表者として参加しており、当時の様子は専用のYouTubeチャンネルで見ることができます。
http://www.youtube.com/user/xboxprojectnatal
■Steven Spielberg and Xbox Project Natal
http://www.youtube.com/watch?v=jh9plZmFIP4
「Project Natal」は、画像カメラや深度カメラ、多数配置したマイクロフォンや専用プロセッサを内蔵したゲームインターフェースシステムで、全身の動きを3Dで追跡し、命令や指示は音声を使って、ゲームがプレイできるというものです。
ボールを蹴る、打つ、キャッチするといった操作をコントローラーを使用せずに実現し、手を動かす、腰をひねる、話すなど、日常生活で行なう動作をするだけでキャラクターを動かすことができるということです。「ゲームからコントローラーを不要にする」という明確なコンセプトが打ち出されています。
Xbox 360を使ったデモでは、プレイヤーの48カ所の関節を秒間30フレームでリアルタイムに追跡し、各間接の方向や加速度を分析し、人間の身体がどの方向へ動くのかを予測していたそうです。
なお「深度カメラ」とは「奥行きが撮影できるカメラ」です。夢みたいな話に聞こえるかもしれませんが、実際に赤外線のTOF(Time of Flight、光を投げてから戻ってくるまでの時間)を使って深度カメラは実現できます。実は筆者も2004年頃、研究レベルで取り組んでいたことがありますが、このマイクロソフトはこの「Project Natal」のために、奥行きカメラなど関連技術の会社を複数買収した、とセンシングデバイス業界では噂になっています。
そのうちの1社、イスラエルの「3DV Systems」社は6月2日付けのプレスリリースで「サードパーティ向けの出荷を停止しました」と告知を出しました。
http://www.3dvsystems.com/news/news.html
TGS2007でもSIGGRAPH2008でも発表していたので、多くのゲームメーカーは目をつけていたはずですが…。
■You Are the Interface! ZCam?, 3DV's Depth-Sensing Camera (SIGGRAPH 2008)
http://www.siggraph.org/s2008/attendees/newtech/3.php
ちなみに「natal」とは日本語では「出生」という意味です。新しいエンタテイメントの「出生」になるかどうか、期待が集まります。
もうひとつの国産両雄ゲームプラットフォームと言えばソニーです。同じく2009年のE3において、ソニーはNatalのような大きなシステムではなく、現在のプラットフォームであるPlayStation3向けに2010年春を予定にモーションセンシングコントローラーを投入してくるようです。
■Sony PS3 Motion Sensing Controller E3 2009 (YouTube動画)
http://www.youtube.com/watch?v=gaQsXdKbUw8
■Engadgetでの紹介
http://www.engadget.com/2009/06/02/sony-announces-new-ps3-motion-controller/
Play Station Eyeと連携するこのプロトタイプは、カラーボールの付いた「魔法の杖」といった見た目ですが、ミリメートル以下の精度を持っているとのことです。
「ボタンは完全には廃せない」というコンセプトのようで、「モーションコントローラーで剣を使い、従来のコントローラーであるDualShock3で楯を使う」といった使い道を想定しているそうです。
実はソニーは画像を使った新しいゲームインタフェースとしては老舗でもあります。PlayStation2「Eye Toy Play」も研究開発レベルでは2001年にはプロトタイプが出ていましたし、最近では「THE EYE OF JUDGMENT」などのカードを使った例もあります。
■
まるでゲーム雑誌のように、本書発刊直前の最新のゲームの動きについてレポートしてしまいましたが、プラットフォームが行うべき、次世代エンタテイメント技術の研究開発の方向性は、はっきりしてきたのではないでしょうか。
新しいエンタテイメント、インタラクションを作るためには?各社ともこの部分の研究開発に必死です。ソフトウェアからハードウェア、インタラクションデザインまであらゆるレベルで見直しが進んでいます。
研究レベルのものも、どんどんと積極的に取り込まれていきます。日本はインタラクション技術の研究開発が世界でも特に進んでいる国ですが、見た目は「チンドン屋にしか見えない」と思われていたこの分野の研究が、これからは世界的に「熱い研究分野」になっていくと考えます。
研究だけではなく実際の面白いゲームにたどり着かなければなりません。ゲーム開発者も、そしてゲーム雑誌などのメディアも、そしてプレイヤーも、旧来の「ゲームってのは、こう...」というステレオタイプに固執せず、新しいエンタテイメント産業とインタラクティブ技術の未来を応援していきたいところです。
ここで「予言の書」と題して、エンタテイメント技術の10年先を占ってみたいと思います。
10年先を占うには、まず10年前を見ることです。
昔話になってしまいますが、エンタテイメント技術の研究開発といえば、我らが国産ソニーは「ソニーコンピューターサイエンス研究所(Sony CSL)」という研究所を持っています。前節で紹介した「THE EYE OF JUDGMENT」の技術はCSLの「インタラクションラボラトリー」という研究所で開発されています。このSonyCSLはゲーム技術に限らず、10年以上前から様々なインタラクションシステムを開発していました。
[URL] http://www.sonycsl.co.jp/
テレビでよく見る科学者茂木健一郎氏が所属している研究所でもあります。
1998年のSIGGRAPHにおいて、筆者は、SonyCSLの暦本純一氏(現・東大)「HoloWall」の隣で、スリッパ型インターフェース「Foot Interface: Fantastic Phantom Slipper!」を発表していました。コナミが「Dance Dance Revolution」を発表するより昔の話です。
■SIGGRAPH'98 Enhanced Realities Fact Sheet http://www.siggraph.org/s98/media/realities.html
■HoloWall(SonyCSL)
http://www.sonycsl.co.jp/person/rekimoto/holowall/
その後、暦本氏は、SonyCSLで長年インタラクションラボラトリーの室長を勤められておられました。
ハリウッドなどのCG産業の影響が強く世界的CGのフェスティバルと化している「SIGGRAPH」ですが、本来は「コンピューターグラフィックスとインタラクティブ技術」の学会です。1998年は「Enhanced Realities」という、現在では「Emerging Technologies」として続いている、先進的なインタラクティブ技術を展示するデモセッションがありました。
上記のファクトシートを見ると、暦本氏の「HoloWall: Interactive Digital Surfaces」と筆者の「Foot Interface: Fantastic Phantom Slipper」が隣に並んでいます。実際にはその隣にMITの学生がレーザースキャナーを使ってインタラクティブなホワイトボードを作って展示していたり、小さいロボットを使ってゲーム画面とインタラクションするようなシステムが展示されていました。他にもキヤノンのMR(Mixed Reality、複合現実感)研究所や、タンジブルインターフェースで有名なMIT石井裕先生の「PingPongPlus」や、稲見昌彦先生(慶應大)が世界的に有名になった「光学迷彩」がはじめて発表した場所でもありました。
こうやって10年前を振り返ってみると、なんだか現在のインタラクション技術の基盤になっているコンセプト、技術を数多く発見することができます。そして研究者たちが10年前にデモを伴って提案した技術が、まだ現実の産業になっていないものもあります。
萌芽的な研究が花開くのには、10年ぐらいの時間がかかる、しかも世界の産業を大きく動かすような萌芽的な研究である可能性が高い、この種のインタラクション技術の基盤研究が、実は「いまはチンドン屋にしか見えない」ということが感じられましたでしょうか?
「SIGGRPAH'98」から現代までの10年間の例を振り返ると、いま研究者たちが熱くなっている「ネタ」が10年先の産業の中心になるということが、仮説づけられるのではないでしょうか。
この「研究→産業→お茶の間」という時間進行感覚は、実のところ他のハイテク応用研究分野とあまり変わりありません(基礎科学研究には20年から50年、それ以上という分野もありますが...!)。
以下、キーワードだけでも列挙してみます。
■
最後の最後に、感謝の言葉で終わりたいとおもいます。
まず、国際学生VRコンテスト「IVRC」に関わった皆さんに感謝です。小坂先生との出会いもIVRCでしたし、卒業生も数多くゲーム業界に就職しています。ネットや紙面では紹介できないような温かなFace2Faceのコミュニティを長年IVRCを支えていただいている実行委員長の舘?教授(東大、現慶應大)をはじめボランティア学生の皆さん、作家の皆さんに感謝です。
そして、「任天堂へのみなさん」に感謝です。本書はハッキングを目的とした書籍ではありません。ゲーム業界の明日を底から支える為の書籍になるよう、頑張って書いたつもりです。歴史に残る革命、「真の価値ある開発」を行ったのは紛れもなく任天堂の皆さんです。本書はその技術を歴史の一部を語る立場で解説させていただいただけです。しかしこの技術が「アンダーグラウンドなハッキング」ではなく日本のゲーム文化を技術面から支える基盤研究力になって行くことを私は望んでいます。本書の読者が任天堂やサードパーティの就職面接に来たときは、彼等を温かく見守っていただければ幸いです。
そして最後に「読者の未来に感謝」したいとおもいます。
フランスから3年間、日本をナナメから見ていた筆者は、ゲーム産業やインタラクション技術の最先端を行く日本がうらやましくて仕方がなかったです。これは欧米の学生さんに共通の感覚なのです。
私は現在、日本で世界の皆さんに向けて、科学を伝える仕事をしています。
この本を手にとって、最後まで読破されたあなたはもう、ゲームに使われているインタラクティブ技術を「ただの遊び」とは思わなくなっているでしょう。
そして、その先の未来には、自分の足もとからまっすぐつながった「道」が見えるはずです。その道を一歩一歩自分で進むための、プログラミングと考え方という「最初の武器」を、本書は授けたはずです。
がんばってください、またお逢いしましょう!
2009年7月1日、ホームタウンのマクドナルドのいつもの席で ――しらいあきひこ
http://akihiko.shirai.as/projects/WiiRemote/
Google Groupsで質問を受け付けています。
■任天堂公式『社長が訊く』(wii.com)
世界中が注目しています。広報戦略としても、ダイレクトに思想が伝わる非常によい公式ページだと思います。
■WiiLi.org http://wiili.org/
■WiiBrew http://wiibrew.org/
■Wiimote Project http://www.wiimoteproject.com/
■mixiコミュ「WiiリモコンをPCで使う会」http://mixi.jp/view_community.pl?id=1610444
■本書のGoogle Docs http://code.★
■筆者のBlog★
■井上理.任天堂“驚き”を生む方程式.日本経済新聞出版社, 2009.
就職活動で任天堂を志望する人は『社長が訊く』と共に読んでおいた方が良い本です。
■きたみあきこ. Visual C# 2008逆引きクイックリファレンスWindows Vista/XP対応.毎日コミュニケーションズ, 2009.
その名の通り、C#で逆引きしたいときに便利な本です。
■村上恭子.お絵描きソフト作りで学ぶグラフィックスプログラミング入門.秀和システム, 2007.
Windowsでお絵かきソフトを開発するためのプログラミング入門本です。お絵かきソフトを自分で開発したい人は必読です。著者の村上恭子さんは本書9章でほんの少しだけ扱った「ぱぴぷぺペインター」の手書き風パステル描画エンジンを開発した研究者でもあります。
■米本実.楽しい電子楽器−自作のススメ−.オーム社開発局, 2008.
本書ではテルミンについてあっさり扱ってしまいましたが、奥深い電子楽器の世界がまとまっています。「音楽アート×半田ごて」を志望する人はぜひ。
■浅海智晴. XML SmartDoc公式リファレンスマニュアル.ピアソンエデュケーション, 2002.
本書執筆にはSmartDocを使いました。本書のような巨大な書籍を書くのには向いていないかもしれませんが、テキストエディタで執筆し、SVNでソースを管理し、HTMLとTeXを同時に作成したいときは便利でしょう。
白井暁彦、久米祐一郎、津田元久、畑田豊彦:光学的3次元位置検出法を用いたリアルタイム人間動作入力デバイス、テレビジョン学会技術報告、20巻、7号、pp.21-26、1996.
Akihiko SHIRAI, Erik GESLIN, Simon RICHIR, WiiMedia: motion analysis methods and applications using a consumer video game controller, Sandbox: an ACM SIGGRAPH Video Game Symposium, San-Diego, 2007.
その他、筆者の論文はホームページ(http://akihiko.shirai.as/)や、「CiNii」等の論文検索サイトで入手可能です。
[1] | 二瓶 健次. バーチャルリアリティは子どもに何ができるか : 臨床場面でのVR. 日本バーチャルリアリティ学会誌 = Journal of the Virtual Reality Society of Japan, 2003/09/25. |
[2] | 白井 暁彦. エンタテイメントシステム. 芸術科学会論文誌, 2004. |