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]));
}
}
}
}