★はじめに★
第3話 Zaurus SL-C700でJ2ME CDCのゲームを作る
[JAVA PRESS Vol.30]

ゲーム:へにへにたたき
対応端末:Zaurus SL-C700


 テキスト 布留川 英一 (ん・ぱか工房)
 イラスト つきみの せひら (せひらねっと)



へにへに、ただいま〜。Zaurus SL-C700を買ってきたよ〜!
「買ってきたよ〜!」じゃないでしょうに…、そらみちゃんたら、また高価なものを衝動買いして。
Linux搭載で640×480の高精細液晶に、臨機応変に活用できる2WAYスタイル。…だけど電池の持ちが悪くて、携帯端末だってのに携帯できやしないところがキュンってしちゃうよね。
…しょうがないなぁ、そらみちゃんは。そんなんだから高2にもなって彼氏の一人もできないんだよ。まあ、せっかくだし、Javaゲームでも作って動かしてみようか?
………Zaurusで使えるJava実行環境は、確かPersonalJavaだよね。JDK1.1相当のJavaアプリケーションが動くんだっけ?
実はZaurus SL-C700用J2ME CDCの実行環境が公開されてたりするんだ。これを使えばJava2相当のゲームを作ることもできるんだよ。
あ、それは面白そうかも。PDAもついにJava2仕様に移る時がきたんだねぇ〜。…ってことは、いろいろとパワーアップしてるの?
PDAの実行環境としてはあまり変わってないかもしれないけど、カーナビやセットトップ・ボックスとか、いろんな機器に柔軟に対応できるようになったんだ。
う、う〜ん、自由度は広がったみたいだけど、やっぱりあたしはゲームで遊んでみたいかな?まずは手始めに、ペン入力ができるZaurusの特徴を生かして、へにへにたたきでも作ってみよう〜♪
…へにへにたたき?(汗)


★Zaurus SL-C700★


Zaurus SL-C700」は、シャープが2002年12月に発売した、PDA(携帯情報端末)です。世界初の高精細システム液晶を採用した端末で、640x480の高解像度でパソコン画面と同じようにホームページや表計算データの閲覧ができます。また、OSにLinuxが採用されており、Javaの実行環境「Jeode」も搭載されています。

JeodeはPersonalJavaの仕様を採用している組込み機器用Java仮想マシンです。PersonalJavaは、JDK1.1をベースとしている仕様で、執筆時現在(2003年3月)、多くのPDAで採用されています。今回は、最新のJ2ME CDCの実行環境を使うので、Jeodeは使いません。



★J2ME CDCとPersonalJava★


J2ME
Java言語は、パソコンはもちろん、サーバー、組み込み機器など、様々な用途で使われるようになり、APIの数も膨大になってきました。そこで、Java言語のAPIは3つのエディションに分けられました。 J2MEはPDAや携帯電話など組み込み機器で使われるJava実行環境です。J2EEやJ2SEにはサン・マイクロシステムズからSDK(Software Development Kit)が提供されていますが、J2MEにはありません。これはJ2MEが対象とする組み込み機器は、メモリやCPUの処理能力はもちろん、インターフェースも多種多様なため、1つの開発キットとして提供することができないからです。J2MEでは機器に応じて「コンフィギュレーション」と「プロファイル」を組み合わせて使うことになります。


コンフィギュレーション
J2MEでは処理能力の低い機器でもプログラムが動くように、従来のJ2SEから機能を絞りこんだサブセットを定義します。この定義のことを「コンフィギュレーション」と呼びます。執筆時現在(2003年3月)、以下の2つのコンフィギュレーションがあります。 「J2ME CDC」はハイエンドのPDAや、カーナビ、セットトップ・ボックスなど中程度の処理能力を持つ端末を対象としているコンフィギュレーションです。Java仮想マシンとしてCVMを利用します。CVMは携帯端末向けに最適化されたJava仮想マシンで、Java2の仮想マシン仕様を完全サポートしています。

CDCで規定されているAPIは、基本的にJ2SEから組み込み機器で使用する必要最小限のものが抽出されています。ただし、J2SEの仕様でdeprecated(推奨しない)とマークされたAPIは排除されています。さらに、CDCにはJ2SEにはない、javax.microedition.ioパッケージが存在します。これはCLDCでjava.io、java.netパッケージの代替APIとして導入されたものです。CDCはCLDCのスーパーセットであるという要求仕様に沿って、CDCにも含まれています。 具体的にどのようなAPIが使用できるかは、「CDC Specification, V1.0, Final」を参照してください。

「J2ME CLDC」はPDAや携帯電話などの、処理能力やメモリに制限のあるデバイスを対象としているコンフィギュレーションです。iモード、J-PHONEやauの携帯電話、Palm等はこのプロファイルを採用しています。


プロファイル
組み込み機器は、PDAならペン入力、携帯電話ならバイブレーション機能というように、それぞれ特有の機能を持つため、それに対応できるように用途に応じた特殊なAPIが必要になります。このAPIのことを「プロファイル」と呼びます。J2ME CDCの主なプロファイルとしては、以下の4つが挙げられます。
「Foundation Profile」は、CDCに含まれなかったJ2SEの基本的なAPIを補うプロファイルです。CDCとFoundation Profileを組み合わせると、java.io、java.lang、java.math、java.net、java.security、java.text、java.utilパッケージについてはJ2SEと同じになります。「Personal Profile」は、グラフィックス関連の機能を利用するためのプロファイルです。「Personal Basis Profile」は、グラフィックス関連の機能を利用するためのプロファイルで、Personal Profileのサブセットです。組み込み機器によっては、グラフィックスAPIのほどんどが不要なものも存在するため、このようなものが用意されています。「RMI Profile」は、RMI(Remote Method Invocation)をCDCで利用するためのプロファイルです。

具体的にどのようなAPIが使用できるかは、各プロファイルのサイトで入手できるドキュメントを参照してください。


J2ME CDCとPersonalJava
執筆時現在(2003年3月)、多くのPDAではJavaの仕様としてPersonalJavaが採用されています。

PersonalJavaでは、「Write once Run any where(一度プログラムを書けばどこでも動く)」にこだわりすぎたため、パソコンより性能が劣る組み込み機器にまで、同等のアプリが動くことを要求しました。性能も低く、インタフェースも多種多用な組み込み機器では、そのようなことはできるはずもなく、結果として一部の高性能なPDAでのみ使われるようになりました。

そこでJ2ME CDCでは、J2SEのAPIを分類ごとにプロファイルとして分け、組み込み機器の処理能力と機能に応じてどのプロファイルを使うかを選択する形になりました。

J2ME CDCがPDAのJava実行環境としてまだ採用されていないのは、J2ME CDCの標準仕様の整備が大幅に遅れていたためです。今回紹介するJ2ME CDCの実行環境も、サン・マイクロシステムズが提供している参照実装となります。将来的にはPersonal Javaの役割はJ2ME CDCに移行することになるでしょう。


★開発環境を整える★


開発ツールのインストール
今回のゲームを作るのに必要な開発ツールは次の3つです。どれも無償で入手できます。

Java 2 SDK, Standard Edition Version 1.3(JDK1.3以降)
パソコン上で動くJavaアプリケーションを作るための開発キットです。サン・マイクロシステムズのサイトで入手できます。インストーラの指示に従ってパソコンにインストールしてください。

libfloat
浮動小数を扱うためのライブラリです。J2ME Personal Profile for Zaurusより先にZaurus本体にインストールしてください。ZIPファイルをダウンロードして解凍すると、「libfloat_1.0_arm.ipk」が出現します。拡張子がipkのファイルは、ザウルスへのインストール・パッケージです。ザウルスドライブ(パソコン-Zaurus間でファイルのやりとりを行うアプリケーション)の「本体メモリー/Install_Files」ディレクトリにlibfloat_1.0_arm.ipkを置き、Zaurus SL-C700の設定ホーム画面にある「ソフトウェアの追加/削除」で追加します。ソフトウェアの追加方法の詳細については、Zaurusのマニュアルを参照してください。

 

J2ME Personal Profile for Zaurus
J2ME CDCの仕様を採用しているZaurus SL-C700用のJava実行環境です。ダウンロードするには、Java Developer Connectionへのメンバー登録が必要です。ZIPファイルをダウンロードして解凍すると、「personal-profile-for-zaurus_arm.ipk」が出現します。ザウルスドライブの「本体メモリー/Install_Files」ディレクトリにpersonal-profile-for-zaurus_arm.ipkを置き、Zaurusの設定ホーム画面にある「ソフトウェアの追加/削除」で追加します。


開発環境を整える
JDKのbinディレクトリにパスを通す
パソコン上でjavacコマンド(コンパイル)やjavaコマンド(Javaアプリケーションの実行)を使うには、JDKのbinディレクトリにパスを通す必要があります。JDKのbinディレクトリが「C:\jdk1.3.1_04\bin\」の時、パスを通すコマンドは以下のようになります。
set path=C:\jdk1.3.1_04\bin\;%path%

cvmにパスを通す
Zaurus上でcvmコマンド(Javaアプリケーションの実行)を使うには、cvmの置いてあるディレクトリ(/home/QtPalmtop/j2me/bin)にパスを通す必要があります。

Zaurusのアプリケーションホーム画面にある「ターミナル」を起動してください。これは、LinuxのOSにアクセスするためのアプリケーションで、Windowsのコマンドプロンプトに相当します。現在設定されているパスを知るには、echoコマンドを使います。
echo $PATH

デフォルト状態では、以下の情報が出力されます。
/home/QtPalmtop/bin:/root/bin:/bin:/sbin:/usr/sbin:/usr/local/bin

cvmの置いてあるディレクトリは、このパスには含まれていないことがわかります。そこで、cvmのシンボリックリンクを作り、パスが通っている/home/QtPalmtop/binディレクトリ置くことにします。シンボリックリンクは、ファイルに別名をつけるようなもので、Windowsのショートカットに相当します。コマンドは以下の通りです。
su -
ln -s /home/QtPalmtop/j2me/bin/cvm /home/QtPalmtop/bin/cvm
exit

シンボリックリンクを作成する権限を得るために、suコマンドでスーパーユーザーになります。スーパーユーザーとは、何でもできる特権を持ったユーザーでのことです。その後、lnコマンドで/home/QtPalmtop/bin/cvmに/home/QtPalmtop/j2me/bin/cvmのシンボリックリンクを作り、exitコマンドでスーパーユーザーを終了しています。

シンボリックリンクの作成が完了したら、引数なしでcvmコマンドを入力してください。
cvm

パスが通っていれば、cvmの使い方(ヘルプ)が出力されます。


Linuxのコマンドの詳細については、Linuxのマニュアルを参照してください。


★文字列の表示★


それでは、「Hello World!」という文字列を表示するアプリケーションを作ります。J2ME CDCのアプリケーションの開発方法は、J2SEのアプリケーションの開発方法と基本的に同じです。エディタでJavaのプログラムファイルを作成し、JDKのjavacコマンドでクラスファイルを作成します。

今回のプログラムは、以下の1つのクラスで構成されています。


HelloWorldクラス
HelloWorldクラスは、プログラムの本体となるクラスです。
HelloWorld.java
import java.awt.*;
import java.awt.event.*;

//文字列の表示
public class HelloWorld extends Frame {

    //コンストラクタ
    public HelloWorld() {
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);
        setSize(240,240);
        setVisible(true);
    }

    //描画
    public void paint(Graphics g) {
        g.drawString("Hello World!",10,40);
    }

    //ウィンドウを閉じる
    protected void processWindowEvent(WindowEvent evt) {
        super.processWindowEvent(evt);
        if (evt.getID()==WindowEvent.WINDOW_CLOSING) {
            System.exit(0);
        }
    }

    //メイン
    public static void main(String[] args) {
        new HelloWorld();
    }
}


import
はじめのimportでは、java.awtパッケージとjava.awt.eventパッケージのクラスを使うことを宣言しています。次で説明するFrameクラスやAWTEventクラスがjava.awtパッケージに、WindowEventクラスがjava.awt.eventパッケージに含まれています。
HelloWorld.javaの一部
import java.awt.*;
import java.awt.event.*;

Frameクラス
Javaアプリケーションの本体となるクラスは、Frameクラスを継承します。Frameを継承したクラスでは、paint()メソッドをオーバーライドしています。paint()メソッドは、Javaアプリケーション起動時や、再描画が必要な時に呼ばれます。このpaint()メソッドに渡されるGraphicsクラスを操作することにより、画面に文字や絵を表示します。
void paint(Graphics g)
g:グラフィックス

Graphicsクラス
文字列を表示するにはGraphicsクラスのdrawString()メソッドを使います。
void drawString(String msg,int x,int y)
text:テキスト
x:X座標
y:Y座標
指定するXY座標は、文字列の左上でなくベースライン左隅の参照点の座標となります。


アプリケーションの終了処理
コンストラクタでは、enableEvents()メソッドによって、ウィンドウのイベントが発生した時、そのコンポーネントに通知するように指定しています。具体的には、processWindowEvent()メソッドが呼ばれます。
HelloWorld.javaの一部
enableEvents(AWTEvent.WINDOW_EVENT_MASK);

processWindowEvent()メソッドに渡されたWindowEventオブジェクトのgetID()メソッドによって、イベント種別を取得できます。
イベント種別定数
WindowEvent.WINDOW_ACTIVATED ウィンドウがアクティブになった
WindowEvent.WINDOW_CLOSED ウィンドウがクローズされた
WindowEvent.WINDOW_CLOSING ウィンドウをクローズしている
WindowEvent.WINDOW_DEACTIVATED ウィンドウがアクティブではなくなった
WindowEvent.WINDOW_DEICONIFIED ウィンドウが非アイコン化された
WindowEvent.WINDOW_ICONIFIED ウィンドウがアイコン化された
WindowEvent.WINDOW_OPENED ウィンドウがオープンされた

アプリケーションを終了させるには、Systemクラスのexit()メソッドを使います。引数には終了ステータスを指定します。通常0でOKです。
void exit(int status)
status:終了ステータス

今回はウィンドウをクローズしている時、アプリケーションを終了させるので、以下のようになります。
HelloWorld.javaの一部
protected void processWindowEvent(WindowEvent evt) {
    super.processWindowEvent(evt);
    if (evt.getID()==WindowEvent.WINDOW_CLOSING) {
        System.exit(0);
    }
}


コンパイルとパソコンでの実行
javacコマンドでHelloWorld.javaをコンパイルします。
javac -g:none *.java

引数「-g:none」の指定して、デバッグオプション(デバッグのための情報)を付けないようにしています。これにより、ファイルサイズの小さくなり、端末への負荷も軽くできます。

成功すればクラスファイル「HelloWorld.class」が生成されます。試しにパソコン上で実行してみてください。
java HelloWorld




実機での実行
ザウルスドライブの「本体メモリー」ディレクトリにHelloWorld.classを置いてください。この場所は、Zaurusの/home/zaurus/Documentsディレクトリに相当します。ターミナルを開いて、/home/zaurus/Documentsディレクトリまで移動してください。ターミナルを起動し時のカレントディレクトリは/home/zaurusなので、以下のコマンドで移動できます。
cd Documents

pwdコマンドでカレントディレクトリを表示して、現在の位置を確かめることができます。
pwd

最後に、cvmコマンドでHelloWorldを実行します。
cvm HelloWorld


★イメージの表示★


次に、イメージを表示するアプリケーションを作ります。今回のプログラムは、以下の1つのクラスで構成されています。


画像ファイルの用意

このプログラムで使う画像ファイルは1つです。クラスファイルと同じディレクトリに置いてください。
・そらみ
-sorami.gif
-240x240ドット


ImageExクラス
ImageExクラスは、プログラムの本体となるクラスです。
ImageEx.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;

//イメージの表示
public class ImageEx extends Frame {
    private Image image=null;//イメージ

    //コンストラクタ
    public ImageEx(){
        //画面
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);
        setSize(240,240);
        setVisible(true);

        //MediaTrackerオブジェクトの生成
        MediaTracker med=new MediaTracker(this);

        //イメージの読み込み
        Toolkit tk=Toolkit.getDefaultToolkit();
        image=tk.getImage(
            this.getClass().getResource("sorami.gif"));

        //MediaTrackerにイメージを登録
        med.addImage(image,0);

        //イメージ読み込み待ち
        try {
            med.waitForAll(0);
        } catch (Exception e) {
        }
        repaint();
    }

    //描画
    public void paint(Graphics g) {
        if (image!=null) g.drawImage(image,0,0,this);
    }

    //ウィンドウを閉じる
    protected void processWindowEvent(WindowEvent evt) {


        super.processWindowEvent(evt);
        if (evt.getID()==WindowEvent.WINDOW_CLOSING) {
            System.exit(0);
        }
    }

    //メイン
    public static void main(String[] args) {
        new ImageEx();
    }
}


イメージの読み込み
Javaアプリケーションでは、ToolkitクラスのgetDefaultToolkit()メソッドでデフォルトのToolkitオブジェクトを取得し、getImage()メソッドで画像ファイル読み込みます。getImage()メソッドの引数として渡すURLは、getClass()メソッドで取得したClassオブジェクトのgetResource()メソッドによって取得します。今回は、sorami.gifを読み込むので、以下のようになります。
ImageEx.javaの一部
Toolkit tk=Toolkit.getDefaultToolkit();
image=tk.getImage(
this.getClass().getResource("0.gif"));


イメージの読み込み待ち
ToolkitオブジェクトのgetImage()メソッドによるイメージの取得の処理は、メソッドから処理が戻ってきても、イメージの読み込みが完了しているわけではありません。別スレッドで読み込み処理を行っています。イメージの読み込みが完了するまで待機したい時は、MediaTrackerクラスを使います。

MediaTrackerクラスのオブジェクトを生成し、addImage()メソッドで読み込み待ちを行うイメージを登録し、waitForAll()メソッドで読み込み完了するまで待ちます。addImage()メソッドやwaitForAll()メソッドで引数として指定するインデックスは、待機するまとまりをいくつかに分けたい時に使うグループIDです。
ImageEx.javaの一部
//MediaTrackerオブジェクトの生成
MediaTracker med=new MediaTracker(this);
//MediaTrackerにイメージを登録
med.addImage(image,0);

//イメージ読み込み待ち
try {
  med.waitForAll(0);
} catch (Exception e) {
}


★へにへにたたきを作る★


それでは、本題の「へにへにたたき」を作ります。画面をタップするとゲームがスタートします。へにへにが穴から顔を出すので、ペンでたたいてください。1回たたくと30点です。30秒間にどれだけたたけるかを競います。このゲームは、以下の2つのクラスで構成されています。

画像ファイルの用意
このゲームでは以下の3枚の画像ファイルを使います。クラスファイルと同じディレクトリに置いてください。
・穴
-0.gif
-80x80


・へにへに(出現)
-1.gif
-80x80


・へにへに(ヒット)
-2.gif
-80x80



HeniStrikeクラス
HeniStrikeクラスは、プログラムの本体となるクラスです。
HeniStrike.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;

//へにへにたたき(本体)
public class HeniStrike extends Frame {
    public static HeniStrike current;

    //コンストラクタ
    public HeniStrike(){
        //カレント
        current=this;

        //画面
        setTitle("へにへにたたき");
        setLayout(new BorderLayout());
        HeniCanvas canvas=new HeniCanvas();
        add(canvas,BorderLayout.CENTER);
        pack();
        show();
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);

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

    //ウィンドウイベント
    protected void processWindowEvent(WindowEvent evt) {
        super.processWindowEvent(evt);
        if (evt.getID()==WindowEvent.WINDOW_CLOSING) {
            System.exit(0);
        }
    }

    //メイン
    public static void main(String[] args) {
        new HeniStrike();
    }
}


タイトルバーの文字列
フレームのタイトルバーの文字列を指定するには、FrameクラスのsetTitle()メソッドを使います。
void setTitle(String title)
title:タイトル

今回は"へにへにたたき"と表示させるので、以下のようになります。
HeniStrike.javaの一部
setTitle("へにへにたたき");


キャンバスの追加
キャンバスなどのコンポーネントをフレームに追加する時は、その配置方法を指定します。今回は、BorderLayoutという上下左右および中央にコンポーネントを配置する方法を指定しました。
HeniStrike.javaの一部
setLayout(new BorderLayout());
HeniCanvas canvas=new HeniCanvas();
add(canvas,BorderLayout.CENTER);
pack();

レイアウトの指定には、setLayout()メソッドを使います。
void setLayout(LayoutManager mgr)
mgr:レイアウトマネージャ

BorderLayoutを指定した時は、add()メソッドの第2引数に配置位置を指定します。
void add(Component comp,int index)
comp:コンポーネント
index:配置位置

配置位置
BorderLayout.CENTER 中央
BorderLayout.WEST
BorderLayout.EAST
BorderLayout.NORTH
BorderLayout.SOUTH

最後に、pack()メソッドによって、キャンバにフィットするようにフレームサイズを変更しています。


HeniCanvasクラス
HeniCanvasクラスは、キャンバスとなるクラスです。
HeniCanvas.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;

//へにへにたたき(キャンバス)
public class HeniCanvas extends Canvas
    implements Runnable,MouseListener {
    //システム
    private int   init=0;//初期化(0:タイトル,1:プレイ,2:終了)
    private int   scene; //シーン
    private int   time;  //時間
    private int   score; //スコア
    private int[] state=new int[9];//状態(0:無,1:出,2:当)

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

    //コンストラクタ
    public HeniCanvas(){
        setSize(240,240);
        addMouseListener(this);
    }

    //更新
    public void update(Graphics g) {
        if (imgOff!=null) g.drawImage(imgOff,0,0,this);
    }

    //実行
    public void run() {
        int i;

        //システム
        Image[] image=new Image[3];//イメージ
        Random  rand =new Random();//乱数

        //ダブルバッファ
        imgOff=HeniStrike.current.createImage(240,240);
        graOff=imgOff.getGraphics();
        Font font=new Font("SansSerif",Font.PLAIN,16);
        FontMetrics fm=getFontMetrics(font);
        graOff.setFont(font);

        //イメージ読み込み
        MediaTracker med=new MediaTracker(this);
        Toolkit tk=Toolkit.getDefaultToolkit();
        for (i=2;i>=0;i--) {
            image[i]=tk.getImage(
                this.getClass().getResource(i+".gif"));
            med.addImage(image[i],0);
        }
        try {
            med.waitForAll(0);
        } catch (Exception e) {
        }
        while (true) {
            //初期化
            if (init>=0) {
                scene=init;
                init =-1;
                if (scene==1) {
                    time =300;
                    score=0;
                    for (i=8;i>=0;i--) state[i]=0;
                }
            }

            //へにへに
            for (i=8;i>=0;i--) {
                //描画
                graOff.drawImage(image[state[i]],
                    (i%3)*80,(i/3)*80,this);

                //状態遷移
                if (scene==1 && time%10==0) {
                    if (state[i]==0 && (rand.nextInt()>>>1)%10==0) {
                        state[i]=1;
                    } else if (state[i]==1 || state[i]==2) {
                        state[i]=0;
                    }
                }
            }

            //ステータス
            graOff.setColor(Color.blue);
            graOff.drawString("時間:"+time,10,20);
            graOff.drawString("スコア:"+score,
                230-fm.stringWidth("スコア:"+score),20);

            //メッセージ
            if (scene==0) {
                graOff.setColor(Color.red);
                graOff.drawString("へにへにたたき",
                    (240-fm.stringWidth("へにへにたたき"))/2,120);
            } else if (scene==1) {
                if (--time==0) init=2;
            } else if (scene==2) {
                graOff.setColor(Color.red);
                graOff.drawString("タイムオーバー",
                    (240-fm.stringWidth("タイムオーバー"))/2,120);
            }

            //再描画
            repaint();

            //スリープ
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }
    }

    //マウスプレスイベント
    public void mousePressed(MouseEvent evt) {
        //タイトル
        if (scene==0) {
            init=1;
        }
        //プレイ
        else if (scene==1 &&
            0<evt.getX() && evt.getX()<240 &&
            0<evt.getY() && evt.getY()<240) {
            int index=evt.getX()/80+(evt.getY()/80)*3;
            if (state[index]==1) {
                state[index]=2;
                score+=30;
            }
        }
        //終了
        else if (scene==2) {
            init=0;
        }
    }

    public void mouseClicked(MouseEvent evt) {}
    public void mouseEntered(MouseEvent evt) {}
    public void mouseExited(MouseEvent evt) {}
    public void mouseReleased(MouseEvent evt) {}
}


シーンの遷移
今回のゲームには、以下の3つのシーンがあります。現在のシーンはint型変数sceneで保持しています。次に遷移するシーンはint型変数initで保持しています。遷移しない時はinitは-1を保持しています。


へにへに状態の遷移
へにへにには、以下の3つの状態があります。現在の状態はint型配列stateで保持しています。穴は9つあるので、配列の長さも9となります。


マウスリスナー
マウスリスナーは、マウスの移動やクリックなどのイベント情報を受信するためのリスナーです。今回は、HeniCanvasクラス自身にMouseListenerインタフェースを実装します。Java言語では、インタフェースを実装していることを示すために、implementsキーワードの後にインタフェース名を記述します。
HeniCanvas.javaの一部
public class HeniCanvas extends Canvas
implements Runnable,MouseListener {

次に、CanvasクラスのaddMouseListener()メソッドで、イベントが発生した時、そのことをどこへ送信すればよいかを指定します。今回はHeniCanvasクラス自身がリスナーになるのでthisをセットします。
HeniCanvas.javaの一部
addMouseListener(this);

最後に、MouseListenerインタフェースが持つ5つのメソッドを実装します。 今回は、mousePress()メソッドのみシーンの遷移やへにへにたたきの処理を行い、他のメソッドは空となります。


ダブルバッファリング
ダブルバッファリングとは、画面と同じサイズのイメージを用意し、必要なものをこのイメージに描画してから、実画面に一変に反映させるという処理です。描画するところ1つ1つが見えて、画面がちらついてしまうことを防ぎます。イメージを生成するには、FrameクラスのcreateImage()メソッドを使います。また、イメージに描画するためのGraphicsオブジェクトを取得するには、getGraphics()メソッドを使います。
HeniCanvas.javaの一部
imgOff=HeniStrike.current.createImage(240,240);
graOff=imgOff.getGraphics();

Javaアプリケーション起動時や、再描画が必要な時、実機はまずupdate()メソッドを呼び、update()メソッドは画面クリア(デフォルト色での塗り潰し)を行ってからpaint()メソッドを呼びます。そのため、そのままではこの画面クリアもちらつきの原因となってしまいます。そこで、update()メソッドをオーバーライドし、画面クリアを行わず、ダブルバッファリング用のイメージを描画するようにしました。
HeniCanvas.javaの一部
public void update(Graphics g) {
  if (imgOff!=null) g.drawImage(imgOff,0,0,this);
}


フォント
フォントの幅を取得するには、FontMetricsクラスを使います。Fontクラスを引数に、FontMetricsクラスを生成し、
stringWidth()メソッドで文字列の幅を取得します。
int stringWidth(String string)
string:文字列
戻り値:文字列の幅(ドット)

今回は、フォントに16ドットのSansSerifを指定して、文字列を画面中央に表示するので、以下のようになります。
HeniCanvas.javaの一部
Font font=new Font("SansSerif",Font.PLAIN,16);
FontMetrics fm=getFontMetrics(font);
graOff.setFont(font);
graOff.drawString("へにへにたたき",
(240-fm.stringWidth("へにへにたたき"))/2,120);


★おわりに★


次回は、iモードの505iシリーズの新機能を使ったゲームを作る予定です。お楽しみに。




−戻る−


(C)Npaka/Sehira, 2003-2004