▼Androidメモ▼
アクションゲーム


OpenGL ES 2.0でアクションゲームを作成する。
HelloGL20_24.png

3Dモデルファイル
以下の8つのファイルをプロジェクトのassetsに配置。
テクスチャのα値使うときはpng-8でなくpng-24。
ライブラリ
3Dモデルの読み込みで利用した独自ライブラリを追加。

ソースコード
GL20ActionGame.java
package net.npaka.gl20actiongame;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

//アクションゲーム
public class GL20ActionGame extends Activity implements Runnable {
private GLSurfaceView glView;
private GLRenderer renderer;
private Thread thread;

//アクティビティ生成時に呼ばれる
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
//フルスクリーンの設定
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);

//GLサーフェイスビューの生成
renderer=new GLRenderer(this);
glView=new GLSurfaceView(this);
glView.setEGLContextClientVersion(2);
glView.setRenderer(renderer);
glView.setOnTouchListener(renderer);
setContentView(glView);
}

//アクティビティレジューム時に呼ばれる
@Override
public void onResume() {
super.onResume();
glView.onResume();
thread=new Thread(this);
thread.start();
}

//アクティビティポーズ時に呼ばれる
@Override
public void onPause() {
super.onPause();
glView.onPause();
thread=null;
}

//スレッドの処理
public void run() {
while(thread!=null) {
//定期処理
renderer.onTick();
//スリープ
try {
Thread.sleep(50);
} catch (Exception e) {
}
}
}
}

GLRenderer.java
package net.npaka.gl20actiongame;
import java.util.Random;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import net.npaka.gles.GLES;
import net.npaka.gles.Graphics;
import net.npaka.gles.ObjLoader;
import net.npaka.gles.Object3D;
import net.npaka.gles.Texture;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.view.MotionEvent;
import android.view.View;

//レンダラー
public class GLRenderer implements
GLSurfaceView.Renderer,View.OnTouchListener {
//シーン定数
private final static int
S_TITLE =0,
S_PLAY =1,
S_GAMEOVER=2;

//システム
private float aspect; //アスペクト比
private boolean touchDown;//タッチダウン
private Graphics g; //2D描画用グラフィックス

//シーン
private int scene=S_TITLE;//シーン
private int init =S_TITLE;//初期化
private int msg =-1; //メッセージ
private int score=0; //スコア

//プレイヤー
private float playerY; //Y座標
private int jumpPow; //ジャンプ力
private boolean jumpAble;//ジャンプ可

//地面
private int mapDX; //X相対位置
private int[] mapH=new int[16];//高さ

//モデル
private Droid droid=new Droid(); //ドロイド君
private int droidFrame; //ドロイド君のフレーム
private Object3D block=new Object3D();//ブロック
private Texture texture; //2D描画用テクスチャ

//コンストラクタ
public GLRenderer(Context context) {
GLES.context=context;
}

//サーフェイス生成時に呼ばれる
@Override
public void onSurfaceCreated(GL10 gl10,EGLConfig eglConfig) {
//プログラムの生成
GLES.makeProgram();

//光源色の指定
GLES20.glUniform4f(GLES.lightAmbientHandle,0.2f,0.2f,0.2f,1.0f);
GLES20.glUniform4f(GLES.lightDiffuseHandle,0.7f,0.7f,0.7f,1.0f);
GLES20.glUniform4f(GLES.lightSpecularHandle,0.9f,0.9f,0.9f,1.0f);

//カリングの指定
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);

//モデルとテクスチャの読み込み
try {
//ドロイド君の読み込み
droid.load();
droid.position.set(3.5f,1.5f*2,0.0f);
droid.rotate.y=90;

//ブロックの読み込み
block.figure=ObjLoader.load("block.obj");

//2D描画用テクスチャの読み込み
texture=Texture.createTextureFromAsset("sprite.png");
} catch (Exception e) {
android.util.Log.e("debug",e.toString());
for (StackTraceElement ste:e.getStackTrace()) {
android.util.Log.e("debug"," "+ste);
}
}
}

//画面サイズ変更時に呼ばれる
@Override
public void onSurfaceChanged(GL10 gl10,int w,int h) {
//画面の表示領域の指定
GLES20.glViewport(0,0,w,h);
aspect=(float)w/(float)h;
g=new Graphics(w,h);
}

//毎フレーム描画時に呼ばれる
@Override
public void onDrawFrame(GL10 gl10) {
//画面のクリア
GLES20.glClearColor(0.4f,0.8f,1.0f,1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|
GLES20.GL_DEPTH_BUFFER_BIT);

//デプステストと光源の有効化
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glUniform1i(GLES.useLightHandle,1);

//テクスチャ行列の指定
Matrix.setIdentityM(GLES.texMatrix,0);
GLES20.glUniformMatrix4fv(GLES.texMatrixHandle,
1,false,GLES.texMatrix,0);

//射影変換
Matrix.setIdentityM(GLES.pMatrix,0);
GLES.gluPerspective(GLES.pMatrix,
45.0f, //Y方向の画角
aspect, //アスペクト比
0.01f, //ニアクリップ
100.0f);//ファークリップ

//光源位置の指定
Matrix.setIdentityM(GLES.mMatrix,0);
GLES20.glUniform4f(GLES.lightPosHandle,5.0f,5.0f,5.0f,1.0f);

//ビュー変換
GLES.gluLookAt(GLES.mMatrix,
2.5f,5.0f,6.0f, //カメラの視点
6.5f,5.0f,0.0f, //カメラの焦点
0.0f,1.0f,0.0f);//カメラの上方向

//ドロイド君の描画
droid.setAnimFrame(droidFrame);
droid.position.y=1.5f+playerY;
droid.draw();

//ブロックの描画
for (int i=mapH.length-1;i>=0;i--) {
if (mapH[i]==0) continue;
block.draw();
block.position.x=(mapDX/100.0f+i)*1.5f;
block.position.y=mapH[i]*1.5f;
if (i%2==0) {
block.scale.set(1.05f,1.05f,1.05f);
} else {
block.scale.set(1.06f,1.06f,1.06f);
}
}

//2Dグラフィックスの描画
g.setup2D();
drawNum(score,8,8,8);
drawMsg(msg);
}

//定期処理
public void onTick() {
//初期化
if (init>=0) {
scene=init;
init=-1;
//タイトル
if (scene==S_TITLE) {
msg =1;
score =0;
playerY =1.5f;
jumpPow =0;
jumpAble=true;
mapDX =0;
for (int i=0;i<16;i++) mapH[i]=1;
}
//プレイ
else if (scene==S_PLAY) {
msg=-1;
touchDown=false;
}
//ゲームオーバー
else if (scene==S_GAMEOVER) {
msg=2;
}
}

//プレイ時の処理
if (scene==S_PLAY) {
//スコア加算
score++;

//衝突判定
if (playerY<mapH[3]*1.5f) {
init=S_GAMEOVER;
jumpAble=false;
}
//上移動
else if (jumpPow>=0) {
playerY+=jumpPow*0.03f;
jumpPow--;
}
//下移動
else {
playerY-=0.2f;
jumpAble=false;
if (mapH[3]!=0 &&
playerY<mapH[3]*1.5f) {
playerY=mapH[3]*1.5f;
jumpAble=true;
}
}
//スクロール処理
mapDX-=10;
if (mapDX==-100) {
mapDX=0;

//地面の高さを左にシフト
System.arraycopy(mapH,1,mapH,0,16-1);

//地面の高さを指定
int idx=rand(6);
//[14]が穴の時は[13]と同じ高さ
if (mapH[14]==0) {
mapH[15]=mapH[13];
}
//)1/6の確率で[15]を穴
else if (idx==0) {
mapH[15]=0;
}
//1/6の確率で[15]を[14]の高さ+1
else if (idx==1) {
mapH[15]=mapH[14]+1;
if (mapH[15]>4) mapH[15]=4;
}
//1/6の確率で[15]を[14]の高さ-1
else if (idx==2) {
mapH[15]=mapH[14]-1;
if (mapH[15]<1) mapH[15]=1;
}
}

//ジャンプ処理
if (jumpAble) {
if (touchDown) {
jumpAble=false;
jumpPow =14;
}
} else {
if (!touchDown) {
jumpPow=-10;
}
}
}
//ゲームオーバー時の処理
else if (scene==S_GAMEOVER) {
if (playerY>=-2) playerY-=0.2f;
}

//ドロイド君のフレーム
droidFrame=(jumpAble)?(score*5+45)%180:0;
}

//タッチイベントの処理
public boolean onTouch(View v,MotionEvent event) {
int action=event.getAction();
if (action==MotionEvent.ACTION_DOWN) {
if (scene==S_TITLE) {
init=S_PLAY;
} else if (scene==S_PLAY) {
touchDown=true;
} else {
if (playerY<-2) init=S_TITLE;
}
} else if (action==MotionEvent.ACTION_UP) {
if (scene==S_PLAY) touchDown=false;
}
return true;
}

//数字の描画
private void drawNum(int num,int len,int x,int y) {
int v=10;
for (int i=2;i<len;i++) v=v*10;
for (int i=0;i<len;i++) {
g.drawImage(texture,x+i*28,y,28,40,(num/v)*28,0,28,40);
num=num%v;
v/=10;
}
}

//メッセージの描画
private void drawMsg(int msg) {
if (msg<0) return;
g.drawImage(texture,(g.screenW-512)/2,120,
512,80,0,80*msg,512,80);
}

//乱数の取得
private static Random rand=new Random();
private static int rand(int num) {
return (rand.nextInt()>>>1)%num;
}
}

Droid.java
package net.npaka.gl20actiongame;
import net.npaka.gles.Figure;
import net.npaka.gles.ObjLoader;
import net.npaka.gles.Object3D;

//ドロイド君
public class Droid extends Object3D {
private Object3D handL=new Object3D();
private Object3D handR=new Object3D();
private Object3D legL =new Object3D();
private Object3D legR =new Object3D();

//ロード
public void load() throws Exception {
//手
Figure handFigure=ObjLoader.load("droid_hand.obj");
handL.figure=handFigure;
handL.position.set(-0.65f,1.24f,0.0f);
handR.figure=handFigure;
handR.position.set( 0.65f,1.24f,0.0f);

//足
Figure legFigure=ObjLoader.load("droid_leg.obj");
legL.figure=legFigure;
legL.position.set(-0.2f,0.5f,0.0f);
legR.figure=legFigure;
legR.position.set( 0.2f,0.5f,0.0f);

//体
figure=ObjLoader.load("droid_body.obj");
childs.add(handL);
childs.add(handR);
childs.add(legL);
childs.add(legR);
position.set(0.0f,1.5f,0.0f);
}

//アニメフレームの指定(frame:0〜180)
public void setAnimFrame(int frame) {
//左手・右足
int rotate=frame%180;
rotate=(rotate<90)?rotate-45:180-45-rotate;
handL.rotate.x=rotate;
legR.rotate.x=rotate;

//右手・左足
rotate=(frame+90)%180;
rotate=(rotate<90)?rotate-45:180-45-rotate;
handR.rotate.x=rotate;
legL.rotate.x=rotate;
}
}



−戻る−