オープンアプリ・ゲームコンテスト
コンテスト概要 応募方法 応募作で遊ぼう アプリ開発講座 Special Special

アプリ開発講座

講座スケジュール
第1回 オープンアプリ(Java)とJava実行環境
line
第2回 はじめてのオープンアプリ(Java)の作成
line
第3回 イメージの描画
line
第4回 キー入力の処理
line
第5回 オープンアプリ(Java)への移植
line
第6回 ゲームの作成
line
第7回 端末へのデータ保存
line
第8回 サウンドの再生
line
第9回 バイブレーションとブラウザ起動
line 最終回 HTTPによる通信
line

開発講座TOPへ



講師プロフィール
株式会社ユビキタスエンターテインメント 布留川英一

1999年のJavaMEの仕様公開を期に「JAVA PRESS(技術評論社)」にてライターとしての活動をはじめ、2000年の503iシリーズの発売を期にドワンゴへ移り、携帯アプリの研究開発を行う。2005年に独立し、ユビキタスエンターテインメントにて次世代コンテンツを作成中。
【著書】
MIDP 2.0 携帯Javaアプリ開発ハンドブック
iアプリゲーム開発テキストブック
ActionScript 3.0 ゲームプログラミングブック
他6冊

 

 

第6回 
ゲームの作成


第6回の今回は、これまでに紹介したテクニックを使って「弾幕避けゲーム」を作成します。ゲーム内容は、画面上部から迫りくる弾幕を避け続け、どれだけ長い時間生き残れるかを競うゲームです。自機が弾にふれるとゲームオーバーです。操作方法は、決定キーでゲーム進行、方向キーで自機の移動です。
[サンプルプログラムのダウンロード]



プログラムは、次の2つのクラスで構成されています。

  • DanmakuGameクラス(DanmakuGameEx.java)
  • DanmakuCanvasクラス(DanmakuCanvas.java)

プロジェクト名とクラス名「DanmakuGame」でプロジェクトを作成してください。

画像ファイルの準備

今回使用する画像ファイルは2枚です。プロジェクトのresフォルダに置いてください。

・自機(0.png 24x16)


・弾(1.png 12x12)



DanmakuGameクラス

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

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

//弾幕避けゲーム(本体)
public class DanmakuGame extends MIDlet {

    //コンストラクタ
    public DanmakuGame() {
        //キャンバスの指定
        DanmakuCanvas c=new DanmakuCanvas();
        Display.getDisplay(this).setCurrent(c);

        //スレッドの実行
        (new Thread(c)).start();
    }

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

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

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

DanmakuCanvasクラス

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

DanmakuCanvas.java
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;
import java.util.*;

//弾幕避けゲーム(キャンバス)
class DanmakuCanvas extends GameCanvas 
    implements Runnable {
    //シーン定数
    private final static int
        S_TITLE   =0,//タイトル
        S_PLAY    =1,//プレイ
        S_GAMEOVER=2;//ゲームオーバー

    //システム
    private int      init=S_TITLE;      //初期化
    private int      scene;             //シーン
    private int      keyEvent;          //キーイベント
    private int      keyState;          //キー状態
    private Graphics g;                 //グラフィックス
    private Image[]  image=new Image[4];//イメージ
    private Random   rand =new Random();//乱数
    
    //ゲーム
    private int score;//スコア
    
    //自機
    private int myX;//X座標
    private int myY;//Y座標

    //弾幕
    private static int[] balX=new int[64];//X座標
    private static int[] balY=new int[64];//Y座標

    //コンストラクタ
    DanmakuCanvas() {
        //キーイベントの抑制
        super(false);
    }

    //実行
    public void run() {
        int i;
        long sleepTime=0L;

        //グラフィックスの取得
        g=getGraphics();

        //画像ファイルの読み込み
        Image[] image=new Image[2];
        try {
            for (i=0;i<2;i++) {
                image[i]=Image.createImage("/"+i+".png");
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        while (true) {
            //シーンの初期化
            if (init>=0) {
                //タイトルの初期化
                if (init==S_TITLE) {
                    score=0;
                    myX=120;
                    myY=220;
                    for (i=0;i<balY.length;i++) {
                        balX[i]=rand(240);
                        balY[i]=rand(240);
                        if (balY[i]>180) balY[i]=-999;
                    }
                }
                //共通の初期化
                scene   =init;
                init    =-1;
                keyEvent=-999;
            }

            //タイトルの処理
            if (scene==S_TITLE) {
                //キーイベントの処理
                if (keyEvent==FIRE) init=S_PLAY;
            }
            //プレイの処理
            else if (scene==S_PLAY) {
                //スコア加算
                score+=10;

                //キー状態の処理(自機の移動)
                keyState=getKeyStates();
                if ((UP_PRESSED   &keyState)!=0) myY-=5;//上キー
                if ((DOWN_PRESSED &keyState)!=0) myY+=5;//下キー
                if ((LEFT_PRESSED &keyState)!=0) myX-=5;//左キー
                if ((RIGHT_PRESSED&keyState)!=0) myX+=5;//右キー
                if (myX< 10) myX=10;
                if (myX>230) myX=230;
                if (myY< 10) myY=10;
                if (myY>230) myY=230;

                //弾幕の処理
                for (i=0;i<balY.length;i++) {
                    //発射中
                    if (balY[i]!=-999) {
                        balY[i]+=5;                    //移動
                        if (balY[i]>=300) balY[i]=-999;//非表示へ
                        //衝突判定
                        if (calcLen(myX,myY,balX[i],balY[i])<12*12) {
                            init=S_GAMEOVER;
                        }
                    } 
                    //非表示(10/100の確立で発射)
                    else if (rand(100)<10) {
                        balX[i]=rand(240);
                        balY[i]=-24;
                    }
                }
            }
            //ゲームオーバーの処理
            else if (scene==S_GAMEOVER) {
                //キーイベントの処理
                if (keyEvent==FIRE) init=S_TITLE;
            }
            keyEvent=-999;

            //描画の処理
            g.setColor(25,25,50);
            g.fillRect(0,0,getWidth(),getHeight());
            g.drawImage(image[0],myX-12,myY-6,g.LEFT|g.TOP);
            for (i=0;i<balX.length;i++) {
                if (balY[i]!=-999) {
                    g.drawImage(image[1],balX[i]-6,balY[i]-6,
g.LEFT|g.TOP); } } if (scene==S_TITLE) drawBold("弾幕避けゲーム"); if (scene==S_GAMEOVER) drawBold("ゲームオーバー"); g.setColor(255,255,255); g.drawString("スコア "+score,5,5,g.LEFT|g.TOP); flushGraphics(); //スリープ while (System.currentTimeMillis()<sleepTime+100 && System.currentTimeMillis()-sleepTime<100); sleepTime=System.currentTimeMillis(); } } //キープレスイベント public void keyPressed(int keyCode) { if (keyCode==0) return; if (getGameAction(keyCode)==FIRE) keyEvent=FIRE; } //太字の描画 private void drawBold(String str) { int x=getWidth()/2; int y=getHeight()/3; g.setColor(120,120,120); g.drawString(str,x-1,y-1,g.HCENTER|g.BASELINE); g.drawString(str,x ,y-1,g.HCENTER|g.BASELINE); g.drawString(str,x+1,y-1,g.HCENTER|g.BASELINE); g.drawString(str,x-1,y ,g.HCENTER|g.BASELINE); g.drawString(str,x+1,y ,g.HCENTER|g.BASELINE); g.drawString(str,x-1,y+1,g.HCENTER|g.BASELINE); g.drawString(str,x ,y+1,g.HCENTER|g.BASELINE); g.drawString(str,x+1,y+1,g.HCENTER|g.BASELINE); g.setColor(255,255,255); g.drawString(str,x,y,g.HCENTER|g.BASELINE); } //乱数の計算 private int rand(int num) { return (rand.nextInt()>>>1)%num; } //距離の2乗の計算 private int calcLen(int x0,int y0,int x1,int y1) { return (x0-x1)*(x0-x1)+(y0-y1)*(y0-y1); } }



◎処理の流れ
メインとなる処理はrun()メソッドで行います。処理の流れは次のようになります。「<<>>」はソースコードを埋め込む場所です。

◎1.起動時の初期化
Graphicsオブジェクトの取得と画像ファイルの読み込みを行っています。画像ファイル名は連番にした方が、たくさんの画像ファイルを処理する時に便利です。

◎2.シーンの初期化
今回のゲームには3つのシーンがあります。

  • タイトル(S_TITLE)
    タイトルを表示するシーンです。決定キーを押すことでプレイに遷移します。
  • プレイ(S_PLAY)
    ゲームをプレイするシーンです。自機が弾幕に衝突することでゲームオーバーに遷移します。
  • ゲームオーバー(S_GAMEOVER)
    ゲームオーバーという文字を表示するシーンです。決定キーを押すことでタイトルに遷移します。

現在のシーンの値に応じて処理の内容を変更します。現在のシーンはscene変数で保持しています。次に遷移するシーンはinit変数に指定し、メインループ最初でシーンの遷移を行います。遷移しない時はinitは-1を指定します。

◎3.タイトルの処理
決定キーを押した時、シーンをプレイに遷移させます。


◎4.プレイの処理
「スコアの加算」「自機の移動」「弾幕の処理」を行っています。弾のY座標「balY」は、発射してない時は-999を指定します。

弾が発射中(balYが-999以外)の時は、Y座標を加算し、もしY300ドットを越えていたら非表示に遷移させ(balYに-999を指定)、最後に衝突判定を行っています。衝突判定では、自機のXY座標と弾のXY座標の距離が12ドット未満かどうかで判定し、衝突していた時はシーンをゲームオーバーに遷移させます。

弾が非表示(balYが-999)の時は、100分の10の確立で発射中に遷移させています。


◎5.ゲームオーバーの処理
決定キーを押した時、シーンをタイトルに遷移させます。


◎6.描画の処理
各種描画処理を行っています。規模の大きなアプリになると、画面の描画もシーンごとに分けることになります。


◎7.スリープ

100ミリ秒に1回処理を行うために必要なスリープを行っています。System.getCurrentTimeMillis()メソッドで、現在時刻をlong値で取得できます。sleepTime変数にスリープ完了した時の時間を指定し、次のスリープ時には、その時間から100ミリ秒経つまで空のループを行って処理を止めています。

[Systemクラス]
long currentTimeMillis()
戻り値 現在時刻と協定世界時のUTC1970年1月1日午前0時との差:long型
現在時刻をlong値で取得する。



◎乱数の取得
乱数をあつかうには、「java.util.Random」クラスを使います。使い方はRandomオブジェクトを生成後、nextInt()メソッドを呼ぶだけです。これで、int型の最小値から最大値(-2147483648〜2147483647)の乱数を取得できます。取得した乱数を>>>演算子を使って、1ビットだけ符号なしビットシフトを行えば、最上位ビットが0になるので、正数の乱数(0〜2147483647)を取得できます。さらに、正数の乱数を任意の値で割ったあまりを求めれば、0から任意の正数未満の乱数を取得できます。

[Randomクラス]
int nextInt()
戻り値 乱数:int型
乱数を取得する。

おわりに

今回はこれでおしまいです。次回は「端末へのデータ保存」について解説します。


ページ先頭へ


  Copyright(C) Spicysoft Corporation All rights reserved.
 
アプリゲット