iモードJavaがやって来た! 第4章 iモードで動くゲームを作ろう


技術評論社 JavaPress vol.17 に掲載された記事を公開します (無断転載禁止)。

はじめに / 簡単なゲーム / 改良 / その他の機能 / おわりに / 戻る / トップページ


はじめに

前章では高レベル API UI を使った簡単な i アプリのプログラムをしました。 本章では低レベル API UI を中心に i モード対応 Java プロファイルの本格的なプログラムの解説をしていきます。 解説の例題として簡単なゲームを作っていこうと思います。

簡単なゲームを作る

ゲーム内容

早速 i アプリで簡単なゲームを作ってみましょう。 ボールを左右移動・ジャンプさせ敵のボールを地面に落とす ゲームを作っていきたいと思います。
 
このプログラムでは低レベル API UI を使っていきます。 先にほぼ完成したソースを リスト 1, 2, 3 に示して順番に 説明を加えていこうと思います。 少し長めのソースですが、i アプリ特有のコードはそれほど多くありません。 i-jade 上で動かした実行結果を図 1 に示します。
 
(図1 Crash.java の実行結果)
 

低レベル API UI

まずは低レベル API を使ったユーザーインターフェースについて説明します。
 
低レベル API UI は Canvas クラスを中心としています。 ゲームなどを作ることを目的としているのであれば、 自由に絵が描画できる Canvas クラスを使うのが良いでしょう。 Canvas クラスは抽象クラスのため、継承して使います。 Canvas クラスは Frame クラスのサブクラスなので、 Panel オブジェクト同様に Display クラスに設定します。
 
このサンプルでは Canvas クラスのサブクラス CrashCanvas にメインコードを書きました (リスト 2)。 IApplication を継承した Crash クラスは CrashCanvas オブジェクトを呼び出すことのみをしています (リスト 1)。
 

イベント処理

イベント処理は processEvent(int type,int param) メソッドを オーバーライドして処理します (リスト 2)。
 
低レベル API を使った UI、すなわち Canvas クラスを使用している場合、 イベントはその Cansvas オブジェクトの processEvent(int type,int param) メソッドに通知されます。 引数 type にイベントの種類、param にイベントに応じた パラメータ値が渡されます。イベントの種類は Display クラスの定数で 定義されています。
 
最もよく使用するイベントはキーが押された/離された時でしょう。 キーが押された時は type に Display.KEY_PRESSED_EVENT が、 param にキー番号が渡されます。キー番号は Display クラスの定数で 定義されています。キーが離された時は同様に type に Display.RELEASED_EVENT、param にキー番号が渡されます。
 
また、キー入力に関してはイベント方式ではない方法で、 能動的にキー状態を取得できます。ゲームなどのアプリケーション開発では こちらを使用したほうが良いでしょう。
 
Canvas クラスに getKeypadState() メソッドがあり、このメソッドで 現在の全てのキーの押下状態を取得することが出来ます。 戻り値の各ビットの位置が Display クラスで定義されているキーに 対応します。例えば、KEY_0 に対応するビットはビット 0 で、 KEY_1 に対応するビットはビット 1 です。 押された状態になっているキーに対応するビットは 1 に、 押されていない状態になっているキーに対応するビットは 0 になります。 つまり、ある 1 つのキーが押された時の getKeypadState() メソッドの 返り値は 1<<(Display クラスで定義されているキーの値) となります。
 
このサンプルではソフトキー処理を processEvent(int type,int param) メソッドで、キャラクター操作のためのキー処理を controll() メソッドなどで getKeypadState() メソッドを使って行っています。 キーの同時押しを実現するために論理積演算を使ってキー押下状態を チェックしました。
 

短時間タイマー

低レベル API UI を使う場合、短時間タイマークラスが使えます。 Timer クラスに比べてオーバーヘッドが少なく、 短時間のタイマー処理に向いています。 短時間タイマークラスは com.nttdocomo.ui.ShortTimer クラスです。 Timer クラスとはパッケージが異なります。
 
使い方はまず ShortTimer クラスのクラスメソッド getShortTimer(Canvas canvas,int id,int time,boolean repeat) で ShortTimer オブジェクトを生成します。 この時、リスナーとなる Canvas オブジェクト、 タイマーの ID (自由に指定できます)、時間間隔、 インターバルタイマか否かを引数でそれぞれ指示します (リスト 2、start() メソッド)。
 
開始・終了・破棄方法は Timer クラスと同様ですが、 処理する時間になると Canvas オブジェクトにイベントとして通知されます。 このイベントは他のイベントと同様、Canvas オブジェクトの processEvent(int type,int param) メソッドに通知されます (リスト 2)。 引数 type に Display.TIMER_EXPIRED_EVENT が、 param にタイマーの ID が渡されます。
 
短時間タイマーはそのリスナーとなる Canvas オブジェクトが 画面に表示されている時のみ動作します。 タイマーをコンストラクタではなく、 start() メソッドで行ったのはこのためです。 コンストラクタ中では Canvas オブジェクトがまだ画面に 表示されていないため、短時間タイマーは動作しませんのでご注意ください。
 

Graphics

Graphics クラスは Standard Edition と使い方はほぼ同じですが、 色の扱いについては注意が必要です (paint(Graphics g) メソッド)。
 
setColor(int c) メソッドの引数に指定する値は RGB 値ではなく 色番号です。色番号はクラスメソッド getColorOfRGB(int r,int g,int b) に RGB 値を指定して得ます。 色番号は実機に依存する値なので、必ずこのようにした取得した値を設定します。 また、色の名前を表す Graphics クラスの定数を引数にしてクラスメソッド getColorOfName(int name) を呼び出すことでも色番号を得ることが出来ます。
 

ダブルバッファリング

画面への描画手順が表示されてしまい、 表示がちらつくことを防ぐ手段としてダブルバッファリングがあります。 これは画像オブジェクト (オフスクリーン画像) に描画を行い、 その画像を画面に書き込む方法です。 ところが、i モード対応 Java プロファイルには オフスクリーン画像を作るための手段がないため、 そのようなことが出来ません。
 
ではどのようにすれば良いかと言いますと、 i モード対応 Java プロファイルにはダブルバッファリングを するための機能がそのために用意されています。 使い方は至って簡単です。 描画前に Graphics オブジェクトの lock() メソッドを呼び出し、 描画後に unlock() メソッドを呼びます。 実機がダブルバッファリングに対応さえしていれば これでダブルバッファリングをしてくれます。
 

フォント

Font クラスを使うことでフォントの変更やその大きさを知ることも出来ます。 AWT の Font クラスと FontMetrics クラスの合わさったものと 考えれば良いと思います。 Graphics クラスには Font オブジェクトを取得するメソッドが 用意されていないため、標準のフォントを扱うには Font クラスのクラスメソッド getDefaultFont() で Font オブジェクトを取得します (paint(Graphics g) メソッド)。 サンプルでは "Game Over" の文字列を画面中央に表示するために Font クラスのメソッドを使っています。
 

終了方法

i アプリを終了させるには IApplication オブジェクトの terminate() メソッドを呼び出しますが、 terminate() メソッドはクラスメソッドではないために IApplication クラスのサブクラス以外からは呼び出すのが面倒です。 しかし、IApplication クラスには現在の i アプリを取得する getCurrentApp() クラスメソッドが用意されていますので、 i アプリを終了させたい時にはリスト 2 の processEvent(int type,int param) メソッドのように IApplication.getCurrentApp().terminate() とすれば便利です。
 

画像

画像自体は Java2 StandardEdition のように Image クラスによって表現されています。 画像を読み込むには MediaManager クラスを使います (リスト 2、init() メソッド)。
 
まずは MediaManager クラスのクラスメソッド getImage(String location) に画像の置き場所を指定して MediaImage オブジェクトを取得します。 MediaImage クラスは読み込まれた画像を表すクラスです。 画像の置き場所は、Jar ファイルの中のルートにあるファイル image.gif を読み込む場合は resource:///image.gif と指定します。 MediaImage オブジェクトから Image オブジェクトや その大きさを取得することが出来ますが、 前もって use() メソッドを呼ぶ必要があります。
 
Image オブジェクトを使用し終わった後には MediaImage オブジェクトの unuse() メソッドで使用終了を宣言し、 dispose() メソッドでリソースを開放します。
 
このサンプルでは背景画像とボール画像を読み込んでいます (図 2, 3)。 このように透明色を使うことが出来ます。
 
   
(図2 背景画像 back.gif)     (図3 ball1.gif, ball2.gif (黒い部分は透明))

ゲームの改良

これでゲームは完成です。一通り遊ぶことが出来ます。
 
ここからはさらに改造を加えることで、 ネットワークやスクラッチパッドの説明をしていきます。 改良を加えた CrashCanvas.java を リスト 4 に示します (注:CrashCanvas.java として保存して下さい)。 i モード対応 Java プロファイルとは無関係な改良も若干施してありますが、 主な変更点は画像ファイルの読み込み方が違います。
 
実はすでにこのゲームは 10KB の容量ぎりぎりになってしまっています。 これ以上の改良を施すにはこの問題を解決する必要があります。 そこで画像ファイルを jar ファイルの外に置き、 ゲーム初回起動時にネットワーク経由で画像ファイルをダウンロード、 スクラッチパッドに保存します (cacheImage() メソッド)。 そして、画像をスクラッチパッドから読み込みます (init() メソッド)。 スクラッチパッドはプログラムとは別に 5KB 確保できるので、 これを利用するわけです。
 

Generic Connection

 
Java2ME CLDC ではネットワーク (HTTP, Datagram, Socket など)、 ポート、ファイルなど全ての入出力への接続を簡単に、 メモリの消費を抑え、そして汎用的に扱えるようにするため、 Generic Connection フレームワークを採用しています。 Java2ME CLDC に基づいている i モード対応 Java プロファイルも 当然このフレームワークに従います。 i モード対応 Java プロファイルでは HTTP、HTTPS、スクラッチパッド、 リソース (Jar ファイルの中にあるファイル) への接続が出来ます。
 
Generic Connection フレームワークでは接続先を 「プロトコル : 位置 パラメータ」で表現します (パラメータは任意)。 javax.microedition.io.Connector クラスの open(String name,int mode) クラスメソッドで接続先と 入出力モードを指定すると Connection オブジェクトが得られます。 Connection インターフェースは文字通り接続を表すインターフェースで、 実際には接続先に応じたサブインターフェースを実装したクラスの オブジェクトが使われます。入出力モードには読み込み (Connector.READ)、 書き込み (Connector.WRITE)、その両方 (Connector.READ_WRITE) の いずれかを指定します。 そして、Connection オブジェクトからストリームを取得したり、 設定を行ったりします。
 
例えば、Jar ファイル中のファイル test.txt の読み込みであれば、 リスト 5 のようになります。
(リスト5 ファイルの読み込みの例)
	Connection connection=Connector.open("resource:///test.txt",Connector.READ);
	InputConnection inputConnection=(InputConnection)(connection);
	InputStream in=inputConnection.openInputStream();
実は画像の置き場所も Generic Connection フレームワークに基づいていました。 つまり、MediaManager クラスで画像をネットワーク先や スクラッチパッドから読み込むことも出来ます。
 
また、ストリームを取得するだけであれば、 Connector クラスにあるクラスメソッド openInputStream(String name)、 openOutputStream(String name) で接続先を指定して直接ストリームを 取得することもできます。
 

スクラッチパッド

 
i モード対応 Java プロファイルではスクラッチパッドが使用できます。 スクラッチパッドとはデータを永続的に保存できるメモリで、 1 つの i アプリにつき、5KB まで保存可能です。
 
スクラッチパッドも Generic Connection フレームワークに基づいています。 スクラッチパッドの接続先は「scratchpad:///0」と表現されます。 パラメータとして「;pos=アクセス個所」を追加することで、 スクラッチパッド上の読み書きを開始する位置を指定することもできます。 アクセス個所はスクラッチパッドの頭からのバイト数です。 また、スクラッチパッドを使用する i アプリは ADF (jam ファイル) の SPsize キーに使用容量を宣言しておく必要があります (例: SPsize=5120)。
 
リスト 4 の init() メソッドではスクラッチパッドから頭の一文字を取得し、 0 の場合はスクラッチパッドは空だと判断しています。 画像 back.gif はスクラッチパッドから読み込みしている点に注意してください。
 

ネットワーク

 
ネットワーク接続も Generic Connection フレームワークに基づいており、 接続先を URL にすれば接続できます。 接続先はセキュリティー保護のために i アプリのダウンロード元と同じサーバーのみに許されています。 また、ネットワーク接続を使用する i アプリは ADF (jam ファイル) の UseNetwork キーに http を指定する必要があります。
 
リスト 4 の cacheImage() メソッドではネットワーク先の画像ファイルから InputStream オブジェクトを、 スクラッチパッドから OutputStream オブジェクトを取得して ダウンロードを実行しています。
 
InputStream オブジェクトの取得方法は次のとおりです。 まず、Connector クラスの open(String name,int mode) クラスメソッドで 接続先を指定してネットワーク先への接続である HttpConnection オブジェクトを得ます。今回は読み込みのため、 アクセスモードは Connector.READ を指定します。 次に setRequestMethod(String method) メソッドで HTTP リクエストメソッド GET を、 setRequestProperty(String key,String value) メソッドで 画像形式のファイルの要求を指示します。 connect() メソッドで実際に接続を開始し、 最後に HttpConnection オブジェクトから InputStream オブジェクトを取得します。

その他の機能

最後に、サンプルでは使用しなかった i モード対応 Java プロファイル固有の機能について説明します。
 

効果音

 
効果音、音楽 (i メロディ) を読み込むには画像と同様 MediaManager クラスを使います (リスト 6)。 まずは MediaManager クラスのクラスメソッド getSound(String location) に音楽の置き場所を指定して MediaSound オブジェクトを取得します。 MediaSound クラスは読み込まれた音楽を表すクラスです。 音楽の置き場所も Generic Connection フレームワークに基づき、 使用前に use() メソッド、使用後に unuse(), dispose() メソッドを 呼ぶ必要がある点も同じです。
 
音楽を演奏するには AudioPresenter クラスを使用します。 AudioPresenter オブジェクトはそのクラスの getAudioPresenter() クラスメソッドで取得し、 setSound(MediaSound sound) メソッドで音を設定します。 そして、play() メソッドで演奏します。
(リスト6 効果音、音楽を使用する例)
	MediaSound sound=MediaManager.getSound("resource:///sound.mld");
	sound.use();
	AudioPresenter audio=AudioPresenter.getAudioPresenter();
	audio.setSound(sound);
	audio.play();
	sound.unuse();
	sound.dispose();
 

エージェントモード

 
エージェントモードとは i アプリを一定時間間隔ごとに定期的に 自動起動するものです。
 
使い方は至って簡単です。 ADF (jam ファイル) の LaunchAt キーに実行間隔を記述します。 例えば、1 時間おきに自動起動させたいときは「I 1」とします。

おわりに

具体的なミニゲームの作成を通して i モード対応 Java プロファイルの使い方を解説してきましたが、 いかがでしたでしょうか。 この i アプリはスクラッチパッドに画像を保存することで 容量に余裕が出来ました。 まだまだ拡張できるので、このゲームを改良してみてはいかがでしょうか。
 
最後に i モード対応 Java プロファイルの使い方の情報が得られる Web ページを紹介して終わりにしたいと思います。
 
この記事をきっかけに i アプリの開発に興味を持っていただければ幸いです。
 
All about i-mode
i モード対応 Java プロファイルの公式ページです。 仕様のダウンロード、FAQ があります。
 
モバイル:トップページ
ZDNet のモバイル関連の記事です。 i モード対応 Java プロファイルについてもしっかりと書かれているので、 時々見に行かれると良いと思います。
 
Overflow! さかきけいのページ
HelloWorld! を表示するプログラムの作り方を丁寧に解説されています。 掲示板も活発です。
 
SINSEN
i モード対応 Java プロファイルのプログラムの情報を公開されています。 リンク集も充実していますし、掲示板も活発です。

戻る