Flashでジョイスティックを使う実験

Flashはブラウザゲームのすぐれたプラットフォームですが、ジョイスティックが使えないのが欠点のひとつです。一方、新興のゲームプラットフォームにUnityがあります。こちらのプラグインはまだほとんど普及していませんが、ジョイスティックに対応しています。

FlashにもUnityにも、それぞれブラウザのJavaScriptとの連携手段が用意されています。それなら、Unityからブラウザを介してFlashにジョイスティックの状態を送れば、Flashでもジョイスティックが使えるんじゃ? と思いついたので、実際にやってみました。

ジョイスティックの状態を送る一連のプロセスについて。まずUnity側からです。Unityでは、物理デバイスの入力を整理・抽象化して、それぞれ名前をつけて扱うようになっています。メニューの Edit > Project Settings > Input にその設定があります。今回はひとまず生データを送りたいので、レバーの軸とボタンを一通り登録します。レバーのデッドゾーン(Dead)もゼロにしておきます。

気分の問題ですが、遅延を少なくするために、Unityの最大フレームレートを上げておきます。

function Awake()
{
	Application.targetFrameRate = 300;
}

次に、ジョイスティックの状態の変化をブラウザに毎フレーム送信します。ブラウザとの通信についてはUnity Web Player and browser communicationに説明があります。Application.ExternalCall()でHTML内のJavaScriptを呼び出すことができます。実際のスクリプトとしては以下のようになります。

var axes = new float[9];

function Update()
{
	for (var i = 1; i <9; i++) {
		var position = Input.GetAxis("Axis" + i);
		if (axes[i] != position) {
			axes[i] = position;
			turnOnLed();
			Application.ExternalCall("axisHandler", i, position);
		}
	}

	for (i = 0; i < 16; i++) {
		var buttonName = "Button" + i;
		if (Input.GetButtonDown(buttonName)) {
			turnOnLed();
			Application.ExternalCall("buttonHandler", i, 1);
		}
		if (Input.GetButtonUp(buttonName)) {
			turnOnLed();
			Application.ExternalCall("buttonHandler", i, 0);
		}
	}
}

ちょっとしたテスト兼演出として、関数を呼び出したときにLEDが点灯するようにしました。

function turnOnLed()
{
	GameObject.Find("Led").SendMessage("TurnOn");
}

次にHTMLです。Flashの埋め込みにはSWFObject 2.2を使用しています。Unityから呼び出されたJavaScriptの関数axisHandlerとbuttonHandlerが、ExternalInterfaceで登録したActionScriptの関数を呼び出します。

var attributes = {
	id:"Flash"
};
swfobject.embedSWF("Game.swf", ...);
...
<script type="text/javascript">
	function axisHandler(no, position) {
		document.getElementById("Flash").axisHandler(no, position);
	}

	function buttonHandler(no, state) {
		document.getElementById("Flash").buttonHandler(no, state);
	}
</script>

ExternalInterfaceは、ローカルファイルシステムではセキュリティのため動作しません。ローカルにApacheを立ててそこでテストするのが簡単です。また、ExternalInterfaceはいろいろ罠があって、ちょっとしたことで動かなくなることが多いようです。今回もそれでなぜかIEでだけ動かず、しばらくはまったんですが、教訓としては、SWFObjectのtest suiteから始めるのがいいと思います。

ここの”Browser communication test page”がブラウザとの通信のサンプルです。test suiteというだけあって動くことが保証されていますので、このサンプルがサーバ上できちんと動くことを確かめてから少しずつ書き換えていくのがいいんじゃないでしょうか。

Flash側では、以下のようなクラスを作ってコールバック関数を登録し、メッセージを受け付けます。

package
{
	import flash.external.ExternalInterface;

	public class Joystick
	{
		public static const AXIS_MAX:int = 9;
		public static const BUTTON_MAX:int = 16;

		private var axes:Vector.<Number> = new Vector.<Number>(AXIS_MAX);
		private var buttons:Vector.<int> = new Vector.<int>(BUTTON_MAX);

		function Joystick()
		{
			if (ExternalInterface.available) {
				ExternalInterface.addCallback("axisHandler", axisHandler);
				ExternalInterface.addCallback("buttonHandler", buttonHandler);
			}
		}

		public function axisHandler(no:int, position:Number):void
		{
			axes[no] = position;
		}

		public function buttonHandler(no:int, state:int):void
		{
			buttons[no] = state;
		}

		public function getAxis(no:int):Number
		{
			return axes[no];
		}

		public function isButtonPressed(no:int):Boolean
		{
			return (buttons[no] == 1);
		}
	}
}

これでUnityからFlashまでジョイスティックの状態が伝わるようになりました。

ちなみに、いったんここまで作った後で、逆の方法に思い当たりました。つまり、UnityからFlashにジョイスティックの状態を流し込むのではなく、FlashからUnityにジョイスティックの状態を問い合わせるやり方です。もしこちらができれば、通信量も間違いも少なく好ましいでしょう。が、ブラウザからUnityの関数をSendMessageで呼び出したときに、戻り値が取得できないので断念しました。

さて、これで、「Unityのプラグインがインストールされていればジョイスティックでも操作できるFlash」ができました。アクロバティックなやり方ですが、信じがたいことにIE8、Firefox 3.6、Google Chrome 4それぞれでXbox360コントローラなどを使って普通に操作できるようです。特にアナログレバーを動かすとかなり頻繁にメッセージが送られるのでもつだろうかと心配だったんですが、レスポンスは悪くないし、CPU使用率もたいして上がりません。作る前はデータの間引きなども考えていたんですが、必要なさそうなのでやっていません。

で、実用性はどうかというと、微妙かなと思ってます。まず、ブラウザゲームでジョイスティックを使う文化がないですよね。それに、ジョイスティックを使いたい人はすでにJoyToKeyなどを使っているはずです。アナログレバーが使えるという違いはありますが、Flashゲームはまずマウスとキーボードで遊べるように作りますから、ゲームにアナログレバーの必要な操作を持ち込むわけにはいかないでしょう。そもそも、ジョイスティックを使うゲームなら最初からFlashではなくUnityで作れよという気もします。

あと、1つのウェブページに2つの標準でない技術を使うことになるので、それなりにリスクがありそうです。Unityの出力するHTMLファイルも結構複雑ですし(ちなみにコメントで説明が書かれているので読んでおくのがおすすめです)、環境によっては不具合が発生するかもしれません。導入には結構ためらうものがあります。

もしUnityのプラグインがある程度普及すれば(20%くらい?)メリットのほうが大きくなるかもしれません。また、いずれにせよFlashでジョイスティックを使う方法ができたということで、ウェブではなく、どこかへの展示用のFlashなどでしたら役に立つかもしれません。

……ということで、どうもひたすら微妙な感じ。こんな変態的なことをさせる前に、そもそもAdobeがFlashでジョイスティックをサポートしてくれたらいいんですけどね……。ゲーム志向にするとか考えてるならその辺検討してほしいなあ。

2010年2月10日

N700系男子

タイトルに意味はありません(笑)。このところXNAやiPhone周辺をずっと調べてたんですが、だいぶ落ちついてきたので、久しぶりのwonderfl新作です。ぼーっと眺めるのにいいんじゃないでしょうか。

架線柱のティアリングがちょっとひどいです。入れたとたんに一気に作る気をなくしかけたくらい。たぶんワーストケースに近いんじゃないかと……。

2009年9月24日

Google MapsにPapervision3Dでオーバーレイ表示

Google Maps API for FlashにMap3Dクラスが新設されて、マップを傾斜・回転できるようになりましたが、Papervision3Dを使用すると簡単に3Dオブジェクトをオーバーレイ表示することができます。サンプルを作ってみました(ソースコードつき)。

pv3doverlay1

通常のマウスドラッグだけでなく、Shift+ドラッグやCtrl+ドラッグで回転操作ができるので試してみてください。Google Earthとだいたい同じ操作になっているようです。

短いコードですが、コアになるのはGoogleMapsCamera3Dクラスで、PV3DのCamera3Dクラスを拡張して、マップに同期する投影行列を与えています。Googleマップのワールド座標系は、東方向がX軸プラスで0?256、南方向がY軸プラスで0?256、上方向がZ軸プラスの左手座標系になっていますが、この座標系上に直接PV3Dのオブジェクトを描画することができます。

せっかくなので、もっと面白いものを表示してみましょう。

pv3doverlay2

東京駅前にワイバーン。3Dモデルは26℃さん制作のをお借りしました。かっこいいですねー。

なにかいる

pv3doverlay3

pv3doverlay4

ズサさんのねぎミクモデルをrectさんのところからお借りしました。ありがとうございます。こんな感じで、小さなものから大きなものまで大丈夫です。ちなみに拙作でも同じ方法で車体を描画しています。

あとは平面投影シャドウあたりが欲しいですね。PV3Dでどうするのがいいのか調査中。

2009年7月31日

2D自動車シミュレーター on Google Mapsを3D化

Google Maps API for Flashが3D表示、というかマップを傾斜表示できるようになっています。

Google Maps APIの担当者さまのご好意でAPIを公開前に触らせていただきましたので、2D自動車シミュレーターを3D化してみました。まだ2車種しかないし、モデリングも適当だし、ホイールも回らないし、ブレーキランプや方向指示器も点灯しないしでやることが山積みなんですが、とりあえず仮公開。

2ddrivingsimulator1

右のCamera Controlで視点を操作できます。ドロップダウンメニューで、車体前方を上にするか、北を上にするかを切り替えることができます。

Googleマップ版でも画面が回転するようにしてほしいという要望は結構いただいてたんですが、これまでAPIの制約でできませんでした。今回のアップデートで、回転できるようになっただけでなく、傾斜で遠方まで見えるようになったので、だいぶ走りやすくなったんじゃないかと思います。

2ddrivingsimulator2

実のところ、もっと視点を下げて3Dレースゲーのようにしたいんですが、手前のタイルが欠けたり、遠方のタイルの読み込みと描画でパフォーマンスが極端に悪くなってしまうので難しいようです。まあ、そこまで極端な視点を使いたいのはこのアプリくらいでしょうからね(笑)。

あと、困ったことがひとつあって、「2D自動車シミュレーター」なのにうっかり3D表示になってしまったという……。2Dを取っても3Dに変えてもつまらない名前になってしまうし、内部的には2D処理のままだし、そのままでいいかみたいな(……いいのか?)。

2D自動車シミュレーター自体根本的にどうにかしないとと思ってるんですが、他にやることがいろいろあって進展がないです。iPhone版とかどうでしょうね?

2009年7月31日

やわらかボール

剛体を動かすにはもうBox2Dのような物理エンジンを使えばいいので、何か自分で動かすならやわらかいものかな、でも布とかメタボールとかもう皆やってるしなーとあれこれ考えていたところ、ローテクなやわらかボールを思いついたので作ってみました(というか自分ローテクしか無理)。たぶん意外となかったタイプ。

ばね法則でボールをやわらかく反射させつつ、ボールと壁、またはボール同士の距離によって見た目を変形させてます。ここまで単純化すると2点以上に同時に接触した場合に対処できないので、どうなるか心配だったんですが、ぎりぎり許容範囲? ボールの数が多いとさすがにエラーが目立つ感じです。

2009年6月4日

トップページ
プロフィール

はてなブックマーク
wonderfl