見出し画像

昔を懐かしみたい人ための携帯プログラミング講座 - ezplus/KDDIプロファイル/KJX作成ツール

【注意】この記事は2001年にホームページに載せてた記事の修正版です。

DoCoMoのJavaアプリ「iアプリ」の開始から遅れること半年、2001年6月にはJ-Phone、7月にはauがJavaアプリサービスを開始しました。J-PhoneのJavaアプリはそのまま「Javaアプリ」、auのJavaアプリは「ezplusアプリ」と呼びます。

◎NTTドコモ
 ・Web閲覧サービス:iモード
 ・Javaアプリ:iアプリ
◎J-PHONE
 ・Web閲覧サービス:J-Sky Web
 ・Javaアプリ:Javaアプリ
◎au
 ・Web閲覧サービス:EZWeb
 ・Javaアプリ:ezplusアプリ

J-Phoneの「Javaアプリ」は、J2ME CLDCとMIDPに独自の「JSCL」というライブラリが追加されています。3Dエンジン「マスコットカプセル」も搭載され、携帯Javaアプリでは世界初の3Dポリゴンが表示できるのが特徴です。2Dのスプライトエンジンも搭載され、セガの人気ゲームも移植されています。

auの「ezplus」は、J2ME CLDCとMIDPに独自の「KDDPプロファイル」というライブラリが追加されています。ファイルサイズ最大50Kバイトという携帯Javaアプリとしては、かなりの大容量が使えることが特徴になります。さらに、ゲーム用のハードウェアコントローラも提供される予定です。

そして2001年8月、auから「KJX作成ツール」が一般公開されました。このツールを使えば、「MIDP」の仕様に基づいて作られた小さなアプリケーション「MIDlet」を、auのJava搭載端末の実行ファイルに変換することができます。

そこで今回は、「KJX作成ツール」を使って、auのJava搭載端末で動くシューティングゲームを作ります。

1. KDD Profile

「KDD Profile」は「ezplusアプリ」独自の機能を利用するためのクラスライブラリです。具体的には以下のような機能が使えます。

・バイブレータの制御
・着信LEDの点灯制御
・サウンドの制御
・Cメール通信(端末間通信)
・移動機状態(電解強度、電池残量、端末型番)の取得

セキュリティのため一般ユーザには使用が許されていない機能もあります。MIDPで規定されているAPIも使えますが、執筆時現在(2001年9月)、HTTP通信のみ使うことができません。

詳しくは、KDDIのサイトで入手できる「ezplusプログラミングガイド」(リンク切れ)を参照してください。

2. 開発環境の準備

今回のゲーム開発に必要なツールは、以下の5つです。ユーザー登録が必要なものもありますが、全て無償で入手できます。

Java 2 SDK, Standard Edition Version 1.3
従来のパソコン上で動くJavaアプリやJavaアプレットを作るための開発キットです。Sun Microsystemsのサイトで入手できます。「J2ME Wireless Toolkit」の実行に必要なので、先にインストールしてください。

Java 2 Platform Micro Edition, Wireless Toolkit 1.0.3 Beta(リンク切れ)
「MIDlet」を作るための開発キットです。ボタン1つでビルド(コンパイル+検証)やエミュレータでの実行ができます。Sun Microsystemsのサイトで入手できます。

KJX作成ツール(リンク切れ)
「MIDlet」を、auのJava搭載端末の実行ファイル「KJXファイル」に変換するツールです。auのサイトで入手できます。
「KJX_tool_Ver1_0.exe」をダウンロードして実行すると、自己解凍して「KJX_tool_kit_Ver1_0」ディレクトリが現れるので、それを好きなディレクトリに配置してください。

ダウンロードCGI(リンク切れ)
「ezplusアプリ」をauのJava搭載端末にダウンロードするには、ダウンロード用のCGIが必要です。
auのサイトで入手できます。auのサイトでPerl版の「ダウンロードCGI」を入手できるので、それをそのまま使います。

チェックサム付加プログラム(リンク切れ)
KJXファイルにダウンロードするファイルの正当性をチェックするための情報を付加するツールです。携帯Javaの情報サイトSINSENで入手できます。
「CRC.zip」をダウンロードして解凍すると、「CRC」ディレクトリが現れるので、それを好きなディレクトリに配置してください。

3. 文字列を表示する

まずはじめに、「Hello World!」という文字列を表示するプログラムを作ります。このプログラムは以下の2つのクラスで構成されています。

・StringExクラス:本体
・StringCanvasクラス:キャンバス

◎プロジェクトの作成
「J2ME Wireless Toolkit」がインストールできたら、Windowsのスタートメニューに「プログラム - J2ME Wireless Toolkit 1.0.3 Beta - KToolbar」が追加されているので、それを選択してください。「J2ME Wireless Toolkit」の主要ツール「KToolbar」が起動します。

「新規作成」ボタンを押すと、新規作成ダイアログが開くので、新規プロジェクト名に"StringEx"、クラス名に"StringEx"と入力します。

クラス名は一番はじめに実行するクラスの名前です。
「プロジェクトの生成」ボタンを押すと、「J2ME Wireless Toolkit」のルートにあるappsディレクトリの下に"StringEx"ディレクトリが生成されます。さらにこの下に以下の3つのディレクトリが生成されています。

・binディレクトリ
実行ファイル(JARファイル、JADファイル、マニフェストファイル)がここに生成されます。

・resディレクトリ
JARファイルに含ませるリソースファイル(画像ファイルや音声ファイル)を置くディレクトリです。

・srcディレクトリ
プログラムファイルを置くディレクトリです。

・libディレクトリ
今回は使用しません。

「アプリケーション属性の設定ダイアログ」が開きますが、属性の編集の必要はないので、そのままOKボタンを押して閉じてください。

◎StringExクラス
StringExクラスは、プログラムの本体となるクラスです。

【StringEx.java】

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

//文字列を表示する(本体)
public final class StringEx extends MIDlet {

   //コンストラクタ
   public StringEx() {
       Display.getDisplay(this).setCurrent(new StringCanvas());
   }

   //アプリの開始
   public void startApp() {
   }

   //アプリの一時停止
   public void pauseApp() {
   }

   //アプリの終了
   public void destroyApp(boolean unconditional) {
   }
}

◎MIDletクラス
MIDletの本体となるクラスはMIDletクラスを継承します。これを継承したクラスは以下の3つのメソッドをオーバーライドする必要があります。

・startApp():アプリの開始時に呼ばれます。
・pauseApp():アプリの一時停止時に呼ばれます。
・destroyApp():アプリの終了時に呼ばれます。

今回は特別な処理はしないので、全て空のままです。

◎Displayクラス
DisplayクラスのgetDisplay()でDisplayオブジェクトを取得し、そのオブジェクトのsetCurrent()メソッドで画面に表示するキャンバスを指定します。

Display.getDisplay(this).setCurrent(new StringCanvas());

今回は次で説明するStringCanvasクラスのオブジェクトをセットしています。

◎StringCanvasクラス
StringCanvasクラスは、キャンバスとなるクラスです。

【StringCanvas.java】

import javax.microedition.lcdui.*;

//文字列を表示する(キャンバス)
final class StringCanvas extends Canvas {

   //描画
   public void paint(Graphics g) {
       g.setColor(0);
       g.drawString("Hello World!",0,20,g.LEFT|g.BOTTOM);
   }
}

・Canvasクラス
StringCanvasクラスはCanvasクラスを継承して、paint()をオーバーライドしています。画面に文字列や絵などを表示するには、このpaint()に渡されるGraphicsオブジェクトを操作します。

・Graphicsクラス
Graphicsクラスには、画面に文字列や絵などの表示を行なうためのメソッド群が用意されています。今回は文字列を表示するのでdrawString()を使います。

void drawString(String str,int x,int y,int align)
   str:表示する文字列
   xX座標
   yY座標
   align:配置
       Graphics.TOP:上
       Graphics.BOTTOM:下
       Graphics.LEFT:左
       Graphics.RIGHT:右
       Graphics.HCENTER:水平中央
       Graphics.VCENTER:垂直中央

Graphics.LEFT|Graphics.UPなら左上、Graphics.RIGHT|Graphics.BOTTOMというように、縦方向と横方向の配置定数を|をつけて渡します。ただし、drawString()では"Graphics.VCENTER"は使えないようです。イメージの配置などで使います。

◎ビルドとエミュレータでの実行
プログラムファイルをsrcディレクトリに置いて、ビルドボタンを押してクラスファイルを生成し、メニューの「プロジェクト-パッケージ」を選んでJARファイルを生成します。デバイスコンボボックスでデバイスを選んで、実行ボタンを押すとエミュレータで実行できます。
DefaultColorPhoneを選べばカラーの携帯電話、RIMJavaHandheldを選べばページャのビューになります。

◎エミュレータのプロパティの変更
「C451H」の画面サイズは「120x113」です。「C452CA」の画面サイズは標準の状態では「120x108」でが、アプリ起動中にezplusキーを押すことで「120x120」まで使えるようになります。

「J2ME Wireless Toolkit」に付属するエミュレータの画面サイズは、auのJava搭載端末の画面サイズとは違います。同じにするには、wtklib¥devicesディレクトリ下にある、プロパティファイルを編集する必要があります。

「DefaultColorPhone」の画面サイズを96x100から120x120に変更するには、DefaultColorPhone.propertiesを次のように変更してください。

## screen properties ##

# Screen location, relative to the top-left corner of the
# telephone's image
###############
screen.x=38
screen.y=60

# Screen size in pixels
###############
screen.width=120
screen.height=148

# The region of the scren available to graphics commands
# This section is optional. It defines the drawable region
# of the screen to be a subregion of the whole screen area.
###############
screenPaintableRegion.x=0
screenPaintableRegion.y=10
screenPaintableRegion.width=120
screenPaintableRegion.height=120

…中略…

# Softbuttons support:
# softbutton.<number>=<x location>,<y location>,<width>,<height>,<font>
#
# Coordinates are relative to the origin of the screen area.

softbutton.0=1,132,40,16, softButton, left
softbutton.1=80,132,40,16, softButton, right

変更後、KToolbarを再起動すると設定が反映されます。

◎KJXファイルの作成
StringExプロジェクトのbinディレクトリに、実行ファイル「StringEx.jar」とアプリケーション属性ファイル「StringEx.jad」ができています。「KJX作成ツール」を使って、この2つのファイルをauのJava搭載端末の実行ファイル「KJXファイル」(拡張子はkjx)に変換します。KJX作成の流れは以下のようになります。

◎JADファイルの編集
まずはじめに、「StringEx.jad」をテキストエディタで開いて以下の2行を追加します。

MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-1.0

auでMIDletを動作させるために必須な属性です。「J2ME Wireless Toolkit」のGUIにもこの項目を設定する場所はあるのですが、かファイルに反映されないので、直接追加します。

【StringEx.jad】

MIDlet-1: StringEx, , StringEx
MIDlet-Jar-Size: 1434
MIDlet-Jar-URL: StringEx.jar
MIDlet-Name: StringEx
MIDlet-Vendor: Sun Microsystems
MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-1.0

◎KJXファイルの作成
KJXファイルを作成するには、「KJX作成ツール」に含まれている「KJXArchiver.jar」を使います。
コマンドの書式は以下の通りです。

java -jar C:¥KJX_tool_kit_Ver1_0ToolsDir1KJXArchiver.jar -c <JADファイル名> <JARファイル名> <KJXファイル名>

今回は、JADファイル名が「StringEx.jad」、JARファイル名が「StringEx.jar」で、生成するKJXのファイル名が「StringEx.kjx」なので、コマンドは以下のようになります。

java -jar C:¥KJX_tool_kit_Ver1_0ToolsDir1KJXArchiver.jar -c StringEx.jad StringEx.jar StringEx.kjx

◎チェックサムを付ける
ダウンロードするファイルの正当性をチェックするために、作成したKJXファイルにチェックサムと呼ばれるデータに誤りがないかを確認する情報を付加します。チェックサムを付加したKJXファイルでないと、実機にダウンロードした時にエラーになります。
「CRCチェックサム付加プログラム」でチェックサムを付加するコマンドの書式は以下の通りです。

java -classpath c:¥CRC CRC <KJXファイル名>

今回は、KJXファイル名が「StringEx.kjx」なので、コマンドは以下のようになります。

java -classpath c:¥CRC CRC StringEx.kjx

ファイルサイズを調べてみると、2バイト増えているのがわかります。

◎ダウンロードサイトの作成
「ezplusアプリ」を公開するサーバは、「HDML」と「CGI」が使えるサーバでなければなりません。

「HDML」は携帯端末向けに作られたコンテンツ記述言語です。iアプリの公開ページをHTMLで記述したように、ezplusアプリの公開ページはHDMLで記述します。サーバがHDMLに対応していない場合は.htaccessファイルによるMIME設定を行う必要があります。

iアプリは公開ページでダウンロードボタンを押すと、直接ダウンロードできましたが、ezplusアプリは「ダウンロードCGI」を介さないとダウンロードできません。

EZWebのサイトからダウンロードCGIを入手し、サーバーで動かしてください。今回の「StringEx」をダウンロードさせるには、以下のようなHDMLが必要になります。

【StringEx.hdml】

<hdml version="3.0" markable="true">
<choice key="url" title="ezplus">
<action  type="accept" task="gosub" dest="device:data/dnld?url=$url" label="OK">
<br>
<center>文字列の表示
<ce value="http://server/download.cgi&name=StringEx.kjx&size=1694&disposition=devkdjx&title=StringEx">StringEx
</choice>
</hdml>

「ceタグ」の「value属性」は以下のように指定します。

・http://server/download.cgi
download.cgiまでの絶対パスを指定します。ダウンロードCGIを配置したURLに応じて変更してください。

・name=StringEx.kjx
KJXファイルのファイル名を指定します。

・size=1694
チェックサム付きKJXファイルのファイルサイズ(単位はバイト)

・disposition=devkdjx
ezplusアプリ用の属性値「devkdjx」を指定します。

・title=StringEx
端末で表示される名前を指定します。

◎実機で実行する
「チェックサム付きKJXファイル」、「ダウンロード用HDML」が用意できたら、HDML対応サーバへアップロードします。実機でダウンロード用HDMLにアクセスして、ezplusアプリをダウンロードできるかどうか試してください。

4. イベントを処理する

次に、ボタンが押された時に発生するイベントをキャンバスに表示するプログラムを作ります。発生したイベントを新しい順に6つ表示します。
このプログラムは以下の2つのクラスで構成されています。

・KeyEventExクラス:本体
・KeyEventCanvasクラス:キャンバス

新規プロジェクト名"KeyEvent"、クラス名"KeyEvent"でプロジェクトを作成してください。

◎KeyEventExクラス
KeyEventExクラスは、プログラムの本体となるクラスです。コンストラクタでキャンバスの生成だけを行っています。

【KeyEventEx.java】

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

//キーイベントの処理(本体)
public final class KeyEventEx extends MIDlet {

   //コンストラクタ
   public KeyEventEx() {
       Display.getDisplay(this).setCurrent(
           new KeyEventCanvas());
   }

   //アプリの開始
   public void startApp() {
   }

   //アプリの一時停止
   public void pauseApp() {
   }

   //アプリの終了
   public void destroyApp(boolean unconditional) {
   }
}

◎KeyEventCanvasクラス
KeyEventCanvasクラスは、キャンバスとなるクラスです。

【KeyEventCanvas.java】

import javax.microedition.lcdui.*;

//キーイベントの処理(キャンバス)
final class KeyEventCanvas extends Canvas
   implements CommandListener {
   private String[] output;//出力データ

   //コンストラクタ
   KeyEventCanvas() {
       //出力データ
       output=new String[6];
       for (int i=0;i<6;i++) {
           output[i]="";
       }

       //コマンド
       addCommand(new Command("CMD0",Command.SCREEN,0));
       addCommand(new Command("CMD1",Command.SCREEN,1));
       setCommandListener(this);
   }

   //出力データの表示
   public void paint(Graphics g) {
       g.setColor((255<<16)+(255<<8)+255);
       g.fillRect(0,0,getWidth(),getHeight());
       g.setColor(255<<16);
       for (int i=0;i<6;i++) {
           g.drawString(output[i],0,18*(i+1),g.LEFT|g.BOTTOM);
       }
   }

   //キープレスイベント
   protected synchronized void keyPressed(int keyCode) {
       addOutput("PRESSED ",keyCode);
   }

   //キーリリース
   protected synchronized void keyReleased(int keyCode) {
       addOutput("RELEASED ",keyCode);
   }

   //出力データを追加
   private void addOutput(String data,int keyCode) {
       //出力データを生成
       int action=getGameAction(keyCode);
       if (action==UP) {
           data+="UP";
       } else if (action==DOWN) {
           data+="DOWN";
       }
       if (keyCode==KEY_NUM0) {
           data+="NUM0";
       } else if (keyCode==KEY_NUM1) {
           data+="NUM1";
       }

       //出力データを追加
       System.arraycopy(output,0,output,1,5);
       output[0]=data;
       repaint();
   }

   //コマンドアクションイベント
   public void commandAction(Command c,Displayable s) {
       System.arraycopy(output,0,output,1,5);
       output[0]="PRESSED "+c.getLabel();
       repaint();
   }
}

・出力データ
String型配列outputには、キャンバスに表示する出力データを保持します。
イベントが発生するたびに、この配列の中身を1つ後ろにずらし、先頭に新しい情報をセットしています。

System.arraycopy(output,0,output,1,5);
output[0]=data;

・色を指定する
文字列や図形の色を指定するには、その処理を行う前に、GraphicsクラスのsetColor()を使います。setColor()には以下の2種類があります。

void setColor(int red,int green,int blue)
    red:赤
    green:緑
    blue:青
void setColor(int rgb)
    rgb:色

引数rgbは3色の値を1つの変数で表したもので、「(red<<16)+(green<<8)+blue」で求めることができます。引数が1つのほうがバイトコードが少なくてすむので、そっちを使った方がよいでしょう。

画面全体を白色で塗り潰すプログラムは以下のようになります。

g.setColor((255<<16)+(255<<8)+255);
g.fillRect(0,0,getWidth(),getHeight());

・キーイベント
キーイベントは、ハードウェアボタンを操作した時に発生するイベントです。ボタンを押したときにkeyPressed呼ばれ、離したときにkeyReleased()が呼ばれます。

protected void keyPressed(int keyCode)
    keyCode:キーコード
protected void keyReleased(int keyCode)
    keyCode:キーコード

方向キーは、キーコードをgetGameAction()メソッドに渡して得られる数値が何かによって、どのハードウェアボタンが押されたかを判断します。

int action=getGameAction(keyCode);
if (action==UP) {
   data+="UP";
} else if (action==DOWN) {
   data+="DOWN";
}

数字キーは、getGameAction()は通さず、直接比較します。

if (keyCode==KEY_NUM0) {
   data+="NUM0";
} else if (keyCode==KEY_NUM1) {
   data+="NUM1";
}

・コマンドイベント
コマンドイベントは、画面下端に配置されるメニューボタンを押した時に発生するイベントです。

Command(String label, int commandType, int priority)
    label:メニューボタンに表示する文字列
    commandType:コマンド種別
        Command.BACK:バックコマンド
        Command.CANCEL:キャンセルコマンド
        Command.HELP:ヘルプコマンド
        Command.ITEM:アイテムコマンド
        Command.SCREEN:スクリーンコマンド
        Command.STOP:ストップコマンド
    priority:優先順位

commandType引数は、通常Command.SCREENで問題ないでしょう。priority引数はメニューボタンの並び順に影響します。
このプログラムではcmd0とcmd1の2つのCommandオブジェクトを生成しています。

addCommand(new Command("CMD0",Command.SCREEN,0));
addCommand(new Command("CMD1",Command.SCREEN,1));

これらのメニューボタンを押したときの処理を受け取るには、CanvasオブジェクトのaddCommandListener()で、通知先を指定する必要があります。

setCommandListener(this);

通知先のcommandAction()が呼ばれるようになります。

public void commandAction(Command c,Displayable s)
   cCommandオブジェクト
   s:Commandイベントの発生元となるDisplayableオブジェクト

◎ビルドと実行
プログラムファイルをsrcディレクトリに置いて、ビルドボタンを押してクラスファイルを生成し、メニューの「プロジェクト-パッケージ」を選んでJARファイルを生成します。実行ボタンを押してエミュレータで実行してください。

5. ゲームを作る

それでは、本題のゲーム作りに入ります。今回作るのはシューティングゲームです。

スタートボタンを押すとゲームが開始します。星が右から左に向かって次々飛んでくるので、プレイヤーを操作して、魔法で星を撃ち落してください。2キーで上段に、5キーで中段に、8キーで下段に魔法を撃ちます。星がプレイヤーより後ろに行くとミスとなり、ゲーム終了です。ゲーム終了までに、いくつ星を撃ち落せたかがスコアになります。スコアは画面の左上に表示されます。
このゲームは以下の2つのクラスで構成されています。

・StarTorugerクラス:本体
・StarCanvasクラス:キャンバス

新規プロジェクト名"StarToruger"、クラス名"StarToruger"でプロジェクトを作成してください。

◎画像ファイルの用意
このゲームでは以下の2枚の画像ファイルを使います。この画像ファイルをStarTorugerディレクトリの下のresディレクトリに置いてください。

・girl.png

・star.png

◎StarTorugerクラス
StarTorugerクラスは、プログラムの本体となるクラスです。コンストラクタで、キャンバスの生成とスレッドの生成だけを行っています。

【StarToruger.java】

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

//スタートルジャー(本体)
public final class StarToruger extends MIDlet {
   //コンストラクタ
   public StarToruger() {
       //キャンバス
       StarCanvas canvas=new StarCanvas();
       Display.getDisplay(this).setCurrent(canvas);

       //スレッド
       Thread thread=new Thread(canvas);
       thread.start();
   }

   //アプリの開始
   public void startApp() {
   }

   //アプリの一時停止
   public void pauseApp() {
   }

   //アプリの終了
   public void destroyApp(boolean unconditional) {
   }
}

◎StarCanvasクラス
StarCanvasクラスは、キャンバスとなるクラスです。

【StarCanvas.java】

import javax.microedition.lcdui.*;
import java.util.Random;

//スタートルジャー(キャンバス)
final class StarCanvas extends Canvas
   implements Runnable,CommandListener {
   //システム
   private static Random rand =new Random();//乱数
   private static char   scene='R';         //シーン
   private static int    score;             //スコア

   //プレイヤー
   private static Image imgGirl;  //イメージ
   private static int   girlY=70; //Y座標
   private static int   girlMagic;//魔法

   //星
   private static Image imgStar;         //イメージ
   private static int[] starX=new int[8];//X座標
   private static int[] starY=new int[8];//Y座標
   private static int[] starV=new int[8];//速さ

   //ダブルバッファリング
   private static Image    imgOff;//オフイメージ
   private static Graphics graOff;//オフグラフィクス

   //実行
   public void run() {
       try {
           //ダブルバッファリング
           imgOff=Image.createImage(120,120);
           graOff=imgOff.getGraphics();

           //イメージの読み込み
           imgGirl=Image.createImage("/girl.png");
           imgStar=Image.createImage("/star.png");

           //コマンド
           addCommand(new Command("START",Command.SCREEN,0));
           setCommandListener(this);
           scene='T';

           //メインループ
           while (true) {
               repaint();
               Thread.sleep(100);
           }
       } catch (Exception e) {}
   }

   //描画
   public synchronized void paint(Graphics g) {
       int i;
       if (scene!='R') {
           //背景の描画
           graOff.setColor((255<<8)+255);
           graOff.fillRect(0,0,120,90);
           graOff.setColor(255<<8);
           graOff.fillRect(0,90,120,30);

           //プレイ時の処理
           if (scene=='P') {
               int idx=-1;
               for (i=0;i<8;i++) {
                   //星の移動
                   if (starV[i]>0) {
                       starX[i]-=starV[i];
                       if (starX[i]<25) {
                           scene='M';
                       } else if (girlY==starY[i] &&
                           (idx<0 || starX[i]>>1)%50==0) {
                       starX[i]=140;
                       starY[i]=30+(rand.nextInt()>>>1)%3*20;
                       starV[i]=3+(rand.nextInt()>>>1)%3*2;
                   }
               }

               //魔法
               if (girlMagic>0) {
                   girlMagic+=30;
                   graOff.setColor((255<<16)+(255<<8));
                   if (idx>=0 && girlMagic>starX[idx]-20) {
                       graOff.fillRect(20,girlY-2,starX[idx]-20,4);
                       starV[idx]=0;
                       girlMagic=0;
                       score++;
                   } else if (girlMagic<100) {
                       graOff.fillRect(20,girlY-2,girlMagic,4);
                   } else {
                       girlMagic=0;
                   }
                }
           }

           //キャラの描画
           graOff.drawImage(imgGirl,20-13,girlY-12,g.LEFT|g.TOP);
           for (i=0;i<8;i++) {
               if (starV[i]>0) {
                   graOff.drawImage(imgStar,
                       starX[i]-9,starY[i]-8,g.LEFT|g.TOP);
               }
           }

           //文字列の描画
           graOff.setColor(255);
           graOff.drawString("SCORE "+score,2,2,g.LEFT|g.TOP);
           if (scene=='T' || scene=='M') {
               String str="スタートルジャー";
               if (scene=='M') str="ミス!";
               graOff.setColor((255<<16)+(150<<8)+150);
               graOff.drawString(str,61,25,g.HCENTER|g.TOP);
               graOff.drawString(str,59,25,g.HCENTER|g.TOP);
               graOff.drawString(str,60,26,g.HCENTER|g.TOP);
               graOff.drawString(str,60,24,g.HCENTER|g.TOP);
               graOff.setColor(255<<16);
               graOff.drawString(str,60,25,g.HCENTER|g.TOP);
           }

           //オフイメージの描画
           g.drawImage(imgOff,0,0,g.LEFT|g.TOP);
       }
   }

   //キープレスイベント
   protected synchronized void keyPressed(int keyCode) {
       if (scene=='P' && girlMagic==0) {
           if (keyCode==KEY_NUM2) {
               girlY=30;
           } else if (keyCode==KEY_NUM5) {
               girlY=50;
           } else if (keyCode==KEY_NUM8) {
               girlY=70;
           }
           girlMagic=1;
       }
   }

   //コマンドイベント
   public synchronized void commandAction(Command c,Displayable s) {
       if (scene!='P') {
           score    =0;
           girlY    =70;
           girlMagic=0;
           for (int i=0;i<8;i++) starV[i]=0;
           scene='P';
       }
   }
}

・シーン
スタートルジャーには以下の4つのシーンがあります。

・準備
ゲーム起動直後にイメージの読み込みを行うシーンです。処理が終わったら「タイトル」に移ります。
・タイトル
"スタートルジャー"というタイトルを表示するシーンです。スタートボタンを押すとゲームが開始し、シーンは「プレイ」に移ります。
・プレイ
実際にゲームをプレイするシーンです。星が女の子より後ろに行った時、「ミス」に移ります。
・ミス
"ミス!"という文字列を表示するシーンです。スタートボタンを押すとゲームが開始し、シーンは「プレイ」に移ります。

char変数sceneには、準備の時は'R'、タイトルの時は'T'、プレイの時は'P'、ミスの時は'M'が指定されます。

・ダブルバッファリング
画面の表示と同じイメージを用意し、画面に必要なものをこのオフイメージに描画してから、実際の画面に一変に表示する処理をダブルバッファリングと呼びます。これによって、画面のチラツキを抑えることができます。

今回のプログラムでは、画面の表示と同じ内容をもつオフイメージ(imgOff)とオフグラフィックス(graOff)をグローバル変数として用意し、run()メソッドで生成しています。そして、paint()メソッド内で、オフグラフィックスに必要な描画処理を行い、最後に画面にオブイメージを描画しています。

・run()
StarTorugerクラスで生成したスレッドが処理するメソッドです。はじめに、ダブルバッファリングで使うオフイメージとオフグラフィックスの生成し、2枚のイメージの読み込み、STARTコマンドを設定します。その後、メインループに入り、定期的にrepaint()を呼ぶ処理を行っています。

・paint()
ゲームの核となる処理を行うメソッドです。定期的にrun()内のメインループからrepaint()を介して呼ばれ、以下の5つの処理を繰り返し行っています。

(1)背景の描画
背景をオフイメージの描画します。
(2)プレイ時の処理
星の速さ(starV)が0の時、星が存在しないことを表します。星の速さ(starV)が0でないものは左へ移動し、0のものは一定の確率で画面右端に出現させます。魔法(girlMagic)が1以上の時は、魔法の長さを徐々に増やし、敵と衝突したら敵と魔法の両方を消去します。魔法が画面右端まで行った時も、魔法を消去します。魔法の描画もここで行っています。
(3)キャラの描画
キャラをオフイメージに描画します。
(4)文字列の描画
"スタートルジャー"や"ミス!"といった文字列をオフイメージに描画します。
(5)オフイメージの描画
オフイメージに描画された内容を、実際の画面に描画します。

・イベント処理
keyPressed()で、キーイベントの処理を行います。シーンがプレイかつ魔法(girlMagic)が0の時、2キーが押されたら30、5キーが押されたら50、8キーが押されたら70にプレイヤーのY座標を変更し、魔法(girlMagic)の値を1にします。

commandAction()で、コマンドイベントの処理を行います。プレイ中でない時、STARTボタンを押すと、変数を初期化して、シーンをプレイに移します。

◎ビルドとエミュレータでの実行
プログラムファイルをsrcディレクトリに置いて、ビルドボタンを押してクラスファイルを生成し、メニューの「プロジェクト-パッケージ」を選んでJARファイルを生成します。実行ボタンを押してエミュレータで実行してください。

この記事が気に入ったらサポートをしてみませんか?