- 2009-01-27 (火) 19:53
- ActionScript
LobbyFormクラスはJava版から移植したわけですが、Web向けのクライアントプログラムならJavaもActionScriptも大差ないので、対応するクラスを作ってから、メソッド単位でEclipseからFlashDevelopにコピペして、
変数宣言を「int index = 0;」→「var index:int = 0;」に、
メソッド定義を「int myfunc( int i, String s)」→「function myfunc(i:var, s:string):int」という感じに、
機械的に修正していきます。一回ビルドしてコンパイラにエラーを指摘させると楽です。
APIも、SocketChannelクラスをSocketクラスに置き換えるとか、StringBuilderやCharBufferは全部Stringに置き換えるとか、たいていは置き換えで済みます。
しかし、「対応するものがまったくない」場合はちょっと困ったことになります。今回のケースではJOptionPaneクラスがないのと、floatToIntBitsメソッドがなく、そこでかなりもたつきました。JOptionPaneクラスはメッセージボックスとかインプットボックスなどの小物ダイアログボックスを表示するクラスです。仕方がないのでそれっぽいものを自作したのですが……。

作成したクラスを使うところで、問題に気づきました。
JOptionPaneクラスの場合、次のようにサクッとスタティックメソッドを使ってダイアログボックスを表示し、すぐに結果を得ることができます。
case GameCommunicator.COM_OFFERPLAY:
// 対戦を申し込まれた
int result = JOptionPane.showConfirmDialog(this, body
+ "が対戦を申し込んできました", "ゲームロビー", JOptionPane.YES_NO_OPTION);
boolean accept = true;
if (result == JOptionPane.NO_OPTION) accept = false;
// 返事を送る(trueなら対戦了解、falseなら断り
this.communicator.sendToServer(-GameCommunicator.COM_OFFERPLAY,
endpt, startpt, Code64.encodeBoolean(accept).toString());
// 申し込みを受け入れた場合はログインフォームを閉じる
if (accept) {
this.oppname = body;
this.oppID = startpt;
this.setSucceed();
}
break;
ダイアログボックス側の処理が終わるまで呼び出し側の処理が待機するわけですから、いわゆる「モーダルダイアログボックス」で「同期処理」ですね。ところがActionScriptにはスレッドがないので、同期処理を行うメソッドを書きにくいのです(こちらが知らないだけで、実は簡単に書けたらすいません)。こういうケースだと、呼び出したメソッドとは別に結果を受け取るイベントハンドラメソッドを書いて、非同期で結果を受け取るのが普通です。
こんな感じですね(注:ConfirmBoxは自作した確認ダイアログボックス用クラス、dialogsheatはダイアログボックス代わりの半透明グレーで塗ったSpriteです)。
case GameCommunicator.COM_OFFERPLAY:
// 対戦を申し込まれた
//ConfirmBoxの表示
var confirm:ConfirmBox = new ConfirmBox( body + "が対戦を申し込んできました" );
confirm.addEventListener( ConfirmBoxEvent.CONFIRM, this.confirmHandler );
this.oppname = body;
this.oppID = startpt;
this.dialogsheat.visible = true;
this.dialogsheat.addChild( confirm );
break;
……中略……
//ConfirmBoxのYesまたはNoボタンが押されたときに呼び出されるイベントハンドラ
private function confirmHandler( e:ConfirmBoxEvent ):void{
//ConfirmBoxを隠す
this.dialogsheat.removeChild( DisplayObject( e.target ) );
this.dialogsheat.visible = false;
DisplayObject(e.target).
removeEventListener( ConfirmBoxEvent.CONFIRM, this.confirmHandler );
//resultがyesなら了解、falseなら断り
var accept:Boolean = false;
if (e.result == ConfirmBox.OPTION_YES ) accept = true;
// 返事を送る
this.communicator.sendToServer(-GameCommunicator.COM_OFFERPLAY,
thislobby.userID, thislobby.oppID, Code64.encodeBoolean(accept) );
// 申し込みを受け入れた場合はログインフォームを閉じる
if(accept) this.setSucceed();
}
これでもいいのですが、Java版では1メソッド内の一連の流れとして書いていたものを分けて書くのは気に入りません(はい、いいえを確認するだけだと思うとなおさら)。結局、イベントハンドラメソッドを無名関数にして気持ちJava版に近づけることにしました。
case GameCommunicator.COM_OFFERPLAY:
// 対戦を申し込まれた
var confirm:ConfirmBox = new ConfirmBox( body + "が対戦を申し込んできました" );
confirm.addEventListener( ConfirmBoxEvent.CONFIRM,
function(e:ConfirmBoxEvent):void{
//LobbyForm自身のインスタンスを取得
var thislobby:LobbyForm = LobbyForm(
DisplayObject( e.target ).parent.parent );
//ConfirmBoxを隠す
this.dialogsheat.removeChild( DisplayObject( e.target ) );
this.dialogsheat.visible = false;
DisplayObject(e.target).removeEventListener(
ConfirmBoxEvent.CONFIRM, arguments.callee );
//resultがyesなら了解、falseなら断り
var accept:Boolean = false;
if (e.result == ConfirmBox.OPTION_YES ) accept = true;
// 返事を送る
thislobby.communicator.sendToServer(-GameCommunicator.COM_OFFERPLAY,
thislobby.userID, thislobby.oppID, Code64.encodeBoolean(accept) );
// 申し込みを受け入れた場合はログインフォームを閉じる
if(accept) thislobby.setSucceed();
} );
this.oppname = body;
this.oppID = startpt;
this.dialogsheat.visible = true;
this.dialogsheat.addChild( confirm );
break;
これならだいたいJava版に近い感じです。無名関数を使うときの注意点は、スコープがクラスやメソッドの外に出てしまうため、呼び出し元メソッドのローカル変数やthisを使ったインスタンス参照が利用できなくなることです。「function(……):void{~}」のブロック内は特殊な異空間なのです。
そこでイベントの送出元を表す「e.target」から、parentプロパティで階層を登ってLobbyFormクラスのインスタンスを取得して、そのメンバにアクセスしています。また、関数自身への参照は、arguments.callee(アーギュメンツ・コーリィ?)で取得します。argumentsは関数の引数を表すオブジェクトなのだとか……。変わったものがあるもんですね。普通はどういうときに使うのかイメージが湧きません。
※追加:Java版に比べて長すぎるので、ConfirmBoxの表示非表示処理(イベントハンドラの追加、dialogsheatの表示/非表示、子の追加/削除処理)をshow、hideメソッドにまとめました。「OK」ボタンのみのConfirmBoxを表示するだけなら、次のように簡潔に書けます。
var confirm:ConfirmBox = new ConfirmBox( "サーバ接続またはログインに失敗しました。\n"
+ "ID・パスワード・ホストネーム・ポートのいずれかが間違っているか、\n"
+ "サーバが落ちている可能性があります。", ConfirmBox.CONFIRM_OK);
confirm.show( this.dialogsheat, function(e:ConfirmBoxEvent):void {
ConfirmBox( e.target ).hide( arguments.callee );
} );
ConfirmBoxやInputBoxのソースコード(LobbyFormは含まれていません)
Comments:0
Trackbacks:0
- Trackback URL for this entry
- http://i-libro.net/wpmu/blog/archives/299/trackback
- Listed below are links to weblogs that reference
- [ActionScript]JOptionPaneクラスがない from わくわくプログラミング自習室 Blogs
