金子邦彦研究室人工知能,実世界DBJava でキーボード,マウス,テキスト描画,2次元グラフィックス,画像描画(Java, Jirr を使用)Jirr を使った文字描画2 (月面着陸ゲーム)

Jirr を使った文字描画2 (月面着陸ゲーム)

Jirr を使った,文字列表示の簡単なサンプルプログラムです. 矢印キーで,文字列 "A" が動く. Jirr のインストールについては,jirr インストールの Web ページを見てください.

要点

MyEventReceiver で,イベントの種類を判別し,所定のイベント(この場合は4つの矢印キー)のとき,内部状態 x, y を更新します. 同時に,x, y の位置に所定の文字列 "A" を表示す.また,噴射を表す「煙」を表示す.

(参考) Eclipseでの Jirr プログラム作成手順

  1. Java プロジェクトの作成: プロジェクト名は何でもよい
  2. クラスの作成: クラス名は下記の通り
  3. 外部JAR の追加

    プロジェクト名を右クリック → Java のビルド・パス → ライブラリー → 外部JAR の追加 C:\1.3.1\lib/jirr131.jar を追加

  4. (オプション)Java ソースの添付と,Javadoc の設定
    1. プロジェクト名の下の「参照ライブラリー」を展開すると「jirr131.jar」があるはずなので,右クリック
    2. まず,「Javaソースの添付」を選び,

      「外部フォルダー」をクリックし,ロケーション・パスとして,「Jirr ソースコード・インストールディレクトリ」c:\Program Files\JIRR\1.3.1\classes を指定

    3. 次に,「Javadoc ロケーション」を選び,

      「外部フォルダー」をクリックし,Javadocロケーション・パス(アーカイブ内のJavadocで無い方)に,c:\Program Files\JIRR\1.3.1\classes\javadoc を指定

Demo.java

import net.sf.jirr.*;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

public class Demo {

   static {
      System.loadLibrary("irrlicht_wrap");
   }
  
   static class MyEventReceiver extends IEventReceiver
    {
        private モデル model = null;
        public void setModel(モデル model) {
            this.model = model;
        }
        
        public boolean OnEvent(SEvent event)
        {
            /*
             * キーコードは,マニュアルの Enum EKEY_CODE の項目を見てください
             */
            if (event.getEventType() == EEVENT_TYPE.EET_KEY_INPUT_EVENT &&
                    event.isKeyInputPressedDown())
            {
                if ( event.getKeyInputKey() == EKEY_CODE.KEY_RIGHT ) {
                    model.right();
                }
                else if ( event.getKeyInputKey() == EKEY_CODE.KEY_LEFT ) {
                    model.left();
                }
                else if ( event.getKeyInputKey() == EKEY_CODE.KEY_UP ) {
                    model.upper();
                }
                else if ( event.getKeyInputKey() == EKEY_CODE.KEY_DOWN ) {
                    model.lower();
                }
                 
                // エスケープキー
                else if ( event.getKeyInputKey() == EKEY_CODE.KEY_ESCAPE ) {
                    System.exit(0);
                }
                // Qキー
                else if ( event.getKeyInputKey() == EKEY_CODE.KEY_KEY_Q ) {
                    System.exit(0);
                }
            }
            if (event.getEventType() == EEVENT_TYPE.EET_KEY_INPUT_EVENT &&
                    !event.isKeyInputPressedDown())
            {
                model.neutral();
            }
            return false;
        }
    }        

   // メイン
   public static void main(String argv[]) {
      // イベントレシーバ
      MyEventReceiver receiver = new MyEventReceiver();
      // 内部状態
      内部状態 state = new 内部状態();
      state.create_mountain();
      // 表示
      表示 view = new 表示();
      view.setState(state);
      // モデル
      モデル model = new モデル();
      model.setState(state);
      receiver.setModel(model);
     
     
      // デバイス・オープン
      IrrlichtDevice device = Jirr.createDevice(E_DRIVER_TYPE.EDT_DIRECT3D9,
                new dimension2di(state.width, state.height),
                /* bits */ 16,
                /* fullscreen */ false,
                /* stencilbuffer */ false,
                /* vsync */ false,
                /* receiver */ receiver);
     
      System.out.println("idevice = " + device);
      String windowCaption = "Landing Game Demo";
      device.setWindowCaption(windowCaption);
      IVideoDriver driver = device.getVideoDriver();
     
      view.setDevice( device );
      view.buildGUIs();

      while (device.run()) {
        
         driver.beginScene( /* backbuffer */ true,
                 /* zbuffer */ true,
                 new SColor(/* a */ 0, 30, 30, 30));
         model.tick();
         view.drawAll();
         driver.endScene();
         if ( model.landing() ) {
             System.exit(0);
         }
         if ( model.collision() ) {
             System.exit(0);
         }
         try {
            // Thread.sleep(5);
         } catch (Exception e) {
             // do Nothing
         }
      }

      device.closeDevice();
   }
}

内部状態.java


public class 内部状態 {
    // width, height は画面サイズ
    public final int width = 640;
    public final int height = 320;
    // 地面
    public int mountain[] = null;
    
    // 着陸船の振る舞い
    // x, y 座標値
    private double x = 0;
    private double y = 30;
    private double vx = 0.2;
    private double vy = 0;
    private double ax = 0;
    private double ay = 0;

    /*
     * 正規分布に近い乱数の発生
     */
    private double my_random( int n ) {
        int i;
        double r;
        r = 0;
        for ( i = 0; i < n; i++ ) {
            // Math.random()は0以上1未満の乱数
            r = r + Math.random();
        }
        return r;
    }
    public void create_mountain() {
        int DIST = 30; // 分散値
        double HH = height / 8; // 平均高さ
        mountain = new int[ width ];
        mountain[0] = height - (int)my_random( height / 2 );
        for ( int i = 1; i < width; i++ ) {
            mountain[i] =
                (int)( (double)mountain[i-1] + my_random( height / DIST ) - ( height / ( 2 * DIST ) )
                + ( ( height - mountain[i-1] ) / HH ) - 1 );
        }
    }
   
    /*
     * セッター,ゲッター
     */
    public double getX() {
        return x;
    }
    public void setX(double x) {
        this.x = x;
    }
    public double getY() {
        return y;
    }
    public void setY(double y) {
        this.y = y;
    }
    public double getVx() {
        return vx;
    }
    public void setVx(double vx) {
        this.vx = vx;
    }
    public double getVy() {
        return vy;
    }
    public void setVy(double vy) {
        this.vy = vy;
    }
    public double getAx() {
        return ax;
    }
    public void setAx(double ax) {
        this.ax = ax;
    }
    public double getAy() {
        return ay;
    }
    public void setAy(double ay) {
        this.ay = ay;
    }
}

モデル.java

import net.sf.jirr.SColor;
import net.sf.jirr.recti;

public class モデル {

    // エンジンパワー
    public final double power = 0.0002;
    // 重力
    public final double gravity = 0.0001;

    private 内部状態 state = null;
    public void setState(内部状態 state) {
        this.state = state;
    }
    
    /*
     * 噴射
     */
    public void upper() {
        this.state.setAx( 0 );
        this.state.setAy( this.power );
    }
    public void lower() {
        this.state.setAx( 0 );
        this.state.setAy( -this.power );
    }
    public void left() {
        this.state.setAx( this.power );
        this.state.setAy( 0 );
    }
    public void right() {
        this.state.setAx( -this.power );
        this.state.setAy( 0 );
    }
    public void neutral() {
        this.state.setAx( 0 );
        this.state.setAy( 0 );
    }
    /*
     * 単位時間進める処理
     */
    void tick() {
        state.setVx( state.getVx() + state.getAx() );
        state.setVy( state.getVy() + state.getAy() + gravity );
        state.setX( state.getX() + state.getVx() );
        state.setY( state.getY() + state.getVy() );
    }
    /*
     * 衝突判定
     *
     */
    public boolean collision() {
        if ( state.getY() > ( state.mountain[(int)state.getX()] - 4 ) ) { return true; };
        if ( state.getX() < 0 ) { return true; };
        if ( state.getX() > state.width ) { return true; };
        if ( state.getY() < 0 ) { return true; };
        if ( state.getY() > state.height ) { return true; };
        return false;
    }
    public boolean landing() {
        if ( state.getY() > ( state.mountain[(int)state.getX()] - 4 ) && ( state.getVy() > -0.001 ) && ( state.getVx() < 0.001 ) && ( state.getVy() < 0.001 )) {
            return true;
        }
        return false;
    }

}

表示.java

import net.sf.jirr.*;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

public class 表示 {
    private 内部状態 state = null;
    public void setState(内部状態 state) {
        this.state = state;
    }
    /*
     * Irrlicht 関係
     */
    private IrrlichtDevice device = null;
    private IVideoDriver driver = null;
    private ISceneManager smgr = null;
    private IGUIEnvironment guienv = null;
    private IGUIFont font = null;
    public void setDevice(IrrlichtDevice device) {
        this.driver = device.getVideoDriver();
        this.smgr = device.getSceneManager();
        this.guienv = device.getGUIEnvironment();
        this.font = guienv.getBuiltInFont();
    }
    void buildGUIs() {
        // このプログラムでは,GUI は無い.書き方の見本をコメント形式で載せている.
          // String text = new String();
          // recti rect = new recti(10, 10, 400, 30);
          // IGUIStaticText staticText = this.guienv.addStaticText(text, rect, true, true, null, -1);
    }
    void drawAll() {
        smgr.drawAll();
        guienv.drawAll();
        this.font.draw("x = " + state.getX() + ", y = " + state.getY(), new recti( 10, 10, 300, 30 ), new SColor(/* a */255, 255, 255, 255));
        // 着陸船
        this.font.draw("A", new recti( (int)state.getX(), (int)state.getY(), 30, 30 ), new SColor(/* a */255, 255, 255, 255));
        // 噴射
        if ( state.getAx() < 0 ) {
            this.font.draw("<", new recti( (int)state.getX() + 8, (int)state.getY(), 30, 30 ), new SColor(/* a */255, 255, 255, 255));
        }
        if ( state.getAx() > 0 ) {
            this.font.draw(">", new recti( (int)state.getX() - 8, (int)state.getY(), 30, 30 ), new SColor(/* a */255, 255, 255, 255));
        }
        if ( state.getAy() < 0 ) {
            this.font.draw("||", new recti( (int)state.getX() + 1, (int)state.getY() + 8, 30, 30 ), new SColor(/* a */255, 255, 255, 255));
        }
        if ( state.getAy() > 0 ) {
            this.font.draw("||", new recti( (int)state.getX() + 1, (int)state.getY() - 8, 30, 30 ), new SColor(/* a */255, 255, 255, 255));
        }
        // 地面
        for ( int i = 1; i < state.width; i++ ) {
            if ( ( state.mountain[i-1] < state.height ) && ( state.mountain[i] < state.height ) ) {
                driver.draw2DLine(new position2di(i-1,state.mountain[i-1]), new position2di(i,state.mountain[i]));
            }
        }
    }
}