|
|
第5回の今回は、NTTドコモのiアプリ、ソフトバンクのS!アプリ、WillcomのJavaアプリをオープンアプリ(Java)に移植する際のポイントについて解説します。
▼APIの違い
コンフィギュレーションは全キャリア共通で「CLDC」、プロファイルもNTTドコモがDoJaを採用している以外は、MIDPが採用されています。つまり、仕様的にはMIDPの基本APIのみを使っているアプリであれば、ソフトバンク端末やWillcom端末向けのアプリをそのままauのオープンアプリプレーヤー対応端末でも実行することができます。
しかし、オープンアプリ(Java)にはオプションAPIはありません。WillcomのJavaアプリもオプションAPIがほとんどないため移植が容易ですが、S!アプリには「JSCL/VSCL/MEXA」といった便利な拡張APIが多数用意されているので、利用しているアプリはそれを基本APIに置き換える作業を行い、できない場合はどのような仕様変更を行うかを考える必要があります。
|
コンフィギュレーション |
プロファイル |
オプションAPI |
| オープンアプリ(Java) |
CLDC |
MIDP |
なし |
| iアプリ |
CLDC |
DoJa |
- |
| S!アプリ |
CLDC |
MIDP |
JSCL
VSCL
MEXA |
| Javaアプリ(Willcom) |
CLDC |
MIDP |
SDバインド |
▼実行ファイルサイズ制限
実行ファイルのサイズ制限(JARファイルのサイズ+データ保存領域のサイズ)はキャリアごとに異なります。オープンアプリ(Java)は一番小さく300KBなので注意して下さい。
|
実行ファイルサイズ+データ保存領域サイズ |
| オープンアプリ(Java) |
300KB+32KB |
| iアプリ |
合計で1MB(903)
100KB+400KB(902以前)
30KB+200KB(70X) |
| S!アプリ |
合計で1MB(3GC)
合計で256KB(P5型,P6型) |
| Javaアプリ(Willcom) |
1MB+512KB |
▼画面・フォントのサイズ制限
オープンアプリ(Java)には携帯アプリでは標準的な、12/16/20ピクセルが揃っていまが、iアプリで標準的な24ドットが使えないので多少調整は必要です。
|
画面サイズ |
フォントサイズ |
| オープンアプリ(Java) |
240x268 |
12,16,20ドット |
| iアプリ |
240x240程度(まれにVGA) |
12,16(まれに20),24ドット |
| S!アプリ |
240x240程度(まれにVGA) |
12,20ドット |
| Javaアプリ(Willcom) |
240x240程度(まれにVGA) |
12,16,20ドット(まれに12のみ) |
▼リソースの制限
画像リソースをiアプリから移植するには、GIFをPNGに変換する必要があります。サウンドリソースをiアプリとS!アプリから移植するには、MLDとSMAF・SMAF/PhraseをMIDI・WAVEに変換する必要があります。また、サウンドの同時再生可能数はMIDIが4音、WAVEが4音です。
|
画像 |
サウンド |
| オープンアプリ(Java) |
PNG,JPEG |
MIDI,WAVE,Tone Sequence |
| iアプリ |
GIF,JPEG |
MLD |
| S!アプリ |
PNG,JPEG |
SMAF,SMAF/Phrase |
| Javaアプリ(Willcom) |
PNG,JPEG |
MIDI |
▼通信量の制限
オープンアプリ(Java)には、「1接続32KB」「1日3MB」という厳しい通信制限があります。そのためネットリソースを大量に活用するようなツール(WEBブラウザアプリなど)の作成には向きません。
|
通信量の制限 |
| オープンアプリ(Java) |
1接続32KB
1日3MB。通信規制は午後1時にリセット |
| iアプリ |
1接続150KB
1日の通信量制限はない |
| S!アプリ |
1日の通信量制限はない |
| Javaアプリ(Willcom) |
1日の通信量制限はない |
▼iアプリからオープンアプリ(Java)への移植
◎アプリ本体となる親クラス
アプリ本体となる親クラスはiアプリでは「com.nttdocomo.ui.IApplication」クラス、オープンアプリ(Java)では「javax.microedition.midlet.MIDlet」クラスです。
|
iアプリ |
オープンアプリ(Java) |
| アプリの親クラス |
com.nttdocomo.ui.IApplication |
javax.microedition.midlet.MIDlet |
| アプリの開始 |
start() |
コンストラクタ,startApp() |
| アプリの一時停止 |
なし |
pauseApp() |
| アプリの再開 |
resume() |
startApp() |
| アプリの終了 |
なし |
destroyApp(boolean) |
iアプリではこれらメソッド内でメインループを作っても大丈夫ですが、オープンアプリ(Java)では不具合が発生する端末もあるので、処理し続けたい時には、新たにスレッドを作った方が良いです。
[iアプリ]本体の例
import com.nttdocomo.ui.*;
//本体
public class A extends IApplication {
static A a;
static B b;
//アプリの開始
public void start() {
a=this;
b=new B();
Display.setCurrent(b);
b.run();
}
//アプリの再開
public void resume() {
}
}
|
[オープンアプリ(Java)]本体の例
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
//本体
public class A extends MIDlet {
static A a;
static B b;
//アプリの開始
public A() {
a=this;
b=new B();
Display.getDisplay(this).setCurrent(b);
(new Thread(b)).start();
}
//アプリの復帰
public void startApp() {
}
//アプリの一時停止
public void pauseApp() {
}
//アプリの終了
public void destroyApp(boolean uncond) {
}
}
|
◎キーイベント
キャンバスとなるクラスの親クラスはiアプリでは「com.nttdocomo.ui.Canvas」クラス、オープンアプリ(Java)では「javax.microedition.ui.Canvas」クラスです。iアプリにはキープレスイベントとキーリリースイベントを取得するメソッドが、オープンアプリ(Java)にはそれに加えてキーリピートイベントを取得するメソッドがあります。
|
iアプリ |
オープンアプリ(Java) |
| キャンバスの親クラス |
com.nttdocomo.ui.Canvas |
javax.microedition.ui.Canvas |
| キープレス |
processEvent(int type,int param) |
keyPressed() |
| キーリリース |
processEvent(int type,int param) |
keyReleased() |
| キーリピート |
なし |
keyRepeated() |
| キー状態の取得 |
getKeypadState() |
keyPressed(),keyReleased() |
| ソフトラベル指定 |
setSoftLabel(int key,String label) |
addCommand(Command) |
| ソフトキープレス |
processEvent(int type,int param) |
commandAction() |
次のようなキーイベント用のラッパーを作ると便利です。
[オープンアプリ(Java)]キーイベント用のラッパー
private int keypadState;//キー状態
private String[] softLabel=new String[2];//ソフトラベル
private javax.microedition.lcdui.Command[] softKey=//ソフトキー
new javax.microedition.lcdui.Command[2];
//ソフトラベル
protected void setSoftLabel(int key,String label) {
//初期化
if (softKey[0]==null) {
softLabel[0]="";
softLabel[1]="";
softKey[0]=new javax.microedition.lcdui.Command(
"",javax.microedition.lcdui.Command.SCREEN,0);
softKey[1]=new javax.microedition.lcdui.Command(
"",javax.microedition.lcdui.Command.SCREEN,1);
addCommand(softKey[0]);
addCommand(softKey[1]);
}
//ソフトキー1・2変更
if (key==Canvas.SOFT_KEY_1) {
if (softLabel[0].equals(label)) return;
removeCommand(softKey[1]);
removeCommand(softKey[0]);
softLabel[0]=label;
softKey[0]=new javax.microedition.lcdui.Command(
label,javax.microedition.lcdui.Command.SCREEN,0);
addCommand(softKey[0]);
addCommand(softKey[1]);
}
//ソフトキー2変更
else {
if (softLabel[1].equals(label)) return;
removeCommand(softKey[1]);
softLabel[1]=label;
softKey[1]=new javax.microedition.lcdui.Command(
label,javax.microedition.lcdui.Command.SCREEN,1);
addCommand(softKey[1]);
}
}
//キープレスイベント
public void keyPressed(int keyCode) {
int key=getKey(keyCode);
keypadState|=(1<<key);
canvas.processEvent(
Display.KEY_PRESSED_EVENT,key);
}
//キーリリースイベント
public void keyReleased(int keyCode) {
int key=getKey(keyCode);
keypadState^=(1<<key);
canvas.processEvent(
Display.KEY_RELEASED_EVENT,key);
}
//キーの取得
private int getKey(int keyCode) {
switch(keyCode) {
case 0:return Display.KEY_CLEAR;
case KEY_NUM0:return Display.KEY_0;
case KEY_NUM1:return Display.KEY_1;
case KEY_NUM2:return Display.KEY_2;
case KEY_NUM3:return Display.KEY_3;
case KEY_NUM4:return Display.KEY_4;
case KEY_NUM5:return Display.KEY_5;
case KEY_NUM6:return Display.KEY_6;
case KEY_NUM7:return Display.KEY_7;
case KEY_NUM8:return Display.KEY_8;
case KEY_NUM9:return Display.KEY_9;
case KEY_STAR:return Display.KEY_ASTERISK;
case KEY_POUND:return Display.KEY_POUND;
default:
switch(getGameAction(keyCode)) {
case UP:return Display.KEY_UP;
case DOWN:return Display.KEY_DOWN;
case LEFT:return Display.KEY_LEFT;
case RIGHT:return Display.KEY_RIGHT;
case FIRE:return Display.KEY_SELECT;
case GAME_A:return Display.KEY_GAME_A;
case GAME_B:return Display.KEY_GAME_B;
case GAME_C:return Display.KEY_GAME_C;
case GAME_D:return Display.KEY_GAME_D;
}
}
return keyCode;
}
//コマンドイベント
public void commandAction(javax.microedition.lcdui.Command c,
javax.microedition.lcdui.Displayable s) {
if (c==softKey[0]) {
canvas.processEvent(Display.KEY_PRESSED_EVENT, Display.KEY_SOFT1);
} else {
canvas.processEvent(Display.KEY_PRESSED_EVENT, Display.KEY_SOFT2);
}
}
//キー状態の取得
protected int getKeypadState() {
return keypadState;
}
|
◎グラフィックス
グラフィックスはプログラムでかなりの割合を占める部分です。
|
iアプリ |
オープンアプリ(Java) |
| Graphicsクラス |
com.nttdocomo.ui.Graphics |
javax.microedition.ui.Graphics |
| 色の指定 |
|g.setColor
(g.getColorOfRGB(255,255,255));
g.setColor
(g.getColorOfName(g.WHITE)); |
g.setColor(255,255,255); |
| 文字列の描画 |
g.drawString("test",0,12); |
g.drawString
("test",0,12,g.LEFT|g.BASELINE); |
| イメージの描画 |
g.drawImage(image,0,0); |
g.drawImage
(image,0,0,g.LEFT|g.TOP); |
| ロック |
g.lock(); |
なし |
| アンロック |
g.unlock(true); |
flushGraphics(); |
グラフィックス用のラッパーを作ることによって、移植作業を軽減させることができます。
[オープンアプリ(Java)]グラフィックス用のラッパー
import javax.microedition.lcdui.*;
//グラフィックス用のラッパー
class G {
//色定数
static int AQUA =( 0<<16)+(255<<8)+255;
static int BLACK =( 0<<16)+( 0<<8)+ 0;
static int BLUE =( 0<<16)+( 0<<8)+255;
static int FUCHSIA=(255<<16)+( 0<<8)+255;
static int GRAY =(128<<16)+(128<<8)+128;
static int GREEN =( 0<<16)+(128<<8)+ 0;
static int LIME =( 0<<16)+(255<<8)+ 0;
static int MAROON =(128<<16)+( 0<<8)+ 0;
static int NAVY =( 0<<16)+( 0<<8)+128;
static int OLIVE =(128<<16)+(128<<8)+ 0;
static int PURPLE =(128<<16)+( 0<<8)+128;
static int RED =(255<<16)+( 0<<8)+ 0;
static int SILVER =(192<<16)+(192<<8)+192;
static int TEAL =( 0<<16)+(128<<8)+128;
static int WHITE =(255<<16)+(255<<8)+255;
static int YELLOW =(255<<16)+(255<<8)+ 0;
//変数
Graphics g;//グラフィクス
//コンストラクタ
G(Graphics g) {
this.g=g;
}
//文字列の描画
void drawString(String str,int x, int y) {
g.drawString(str,x,y+2,Graphics.LEFT|Graphics.BOTTOM);
}
//イメージの描画
void drawImage(Image image,int x,int y) {
g.drawImage(image,x,y,
Graphics.LEFT|Graphics.TOP);
}
//ラインの描画
void drawLine(int x1,int y1,int x2,int y2) {
g.drawLine(x1,y1,x2,y2);
}
//矩形の描画
void drawRect(int x,int y,int width,int height) {
g.drawRect(x,y,width,height);
}
//矩形の塗り潰し
void fillRect(int x,int y,int width,int height) {
g.fillRect(x,y,width,height);
}
//円弧の塗り潰し
void fillArc(int x,int y,int width,int height,int startAngle, int arcAngle) {
g.fillArc(x,y,width,height,startAngle,arcAngle);
}
//フォントの設定
void setFont(Font font) {
g.setFont(font);
}
//色の取得
static int getColorOfName(int name) {
return name;
}
//色の取得
static int getColorOfRGB(int red,int green,int blue) {
return (red<<16)+(green<<8)+blue;
}
//色の設定
void setColor(int color) {
g.setColor(color);
}
//ロック
void lock() {
}
//アンロック
void unlock(boolean forced) {
A.b.flushGraphics();
}
}
|
▼おわりに
今回はこれでおしまいです。次回は「ゲームの作成」について解説します。 |