Jirr を使った文字描画2 (月面着陸ゲーム)
Jirr を使った,文字列表示の簡単なサンプルプログラムです. 矢印キーで,文字列 "A" が動く. Jirr のインストールについては,jirr インストールの Web ページを見てください.
要点
MyEventReceiver で,イベントの種類を判別し,所定のイベント(この場合は4つの矢印キー)のとき,内部状態 x, y を更新します. 同時に,x, y の位置に所定の文字列 "A" を表示す.また,噴射を表す「煙」を表示す.
- 内部状態.java
着陸船の座標値と速度と加速度,地面の形状,表示画面の大きさ
- モデル.java
座標値と速度の計算,衝突判定
- 表示.java
着陸船の表示,地面の表示
- Demo.java メイン関数,キーボードイベントの処理
(参考) Eclipseでの Jirr プログラム作成手順
- Java プロジェクトの作成: プロジェクト名は何でもよい
- クラスの作成: クラス名は下記の通り
- 外部JAR の追加
プロジェクト名を右クリック → Java のビルド・パス → ライブラリー → 外部JAR の追加 C:\1.3.1\lib/jirr131.jar を追加
- (オプション)Java ソースの添付と,Javadoc の設定
- プロジェクト名の下の「参照ライブラリー」を展開すると「jirr131.jar」があるはずなので,右クリック
- まず,「Javaソースの添付」を選び,
「外部フォルダー」をクリックし,ロケーション・パスとして,「Jirr ソースコード・インストールディレクトリ」c:\Program Files\JIRR\1.3.1\classes を指定
- 次に,「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])); } } } }