GL10PrimitiveEx1.apk
size: 65.6KB
前回のに若干手を加えて遊べそうな感じに。
実機ですでに処理オチする。
オブジェクトの管理が垢抜けない。
updateやらrender専用の管理クラスを用意する必要があるが、
メモリは別の場所で確保が妥当だろうから少し頭を使わないといけない。
あと音が0.5秒に位階程度しか鳴らせないのでショット音とかは無理そう。
効果音管理をHashMapで行うように。
グラフィックは用意が面倒だから全部ドロイド君。
打ち出されるドロイド君に、あたってはじける破片もドロイド君。
以下ソースコード
package my.gl10primitiveex; import android.app.Activity; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import android.os.Bundle; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.Map; import java.util.Random; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; import android.opengl.GLU; import android.view.MotionEvent; import android.media.SoundPool; import android.media.AudioManager; import java.util.HashMap; import android.content.Context; public class GL10PrimitiveEx1 extends Activity { private GLSurfaceView glView; private float prevTouchPosX; private float prevTouchPosY; // @Override protected void onCreate( Bundle bundle ) { super.onCreate( bundle ); glView = new GLSurfaceView( this ); glView.setRenderer( new GLRenderer(this) ); // glView.setRenderMode( GLSurfaceView.RENDERMODE_WHEN_DIRTY ); // glView.setDebugFlags( GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS ); setContentView( glView ); glView.setFocusable( true ); AppObject.getInstance().getSoundManager().create( AppObject.SOUND_HIT, this, R.raw.sound ); } // @Override public void onResume() { super.onResume(); glView.onResume(); } // @Override public void onPause() { super.onPause(); glView.onPause(); } public boolean onTouchEvent( MotionEvent event ) { if ( event.getAction() == MotionEvent.ACTION_MOVE ) { float rate = 0.01f; float vx = (event.getX() - prevTouchPosX) * rate; float vy = (event.getY() - prevTouchPosY) * rate; // 移動距離が長すぎるようなら移動しないように if ( 0.5 > Math.sqrt( (double)(vx*vx + vy*vy) ) ) { AppObject.getInstance().getPlayerObject().move( vx, -vy ); } } prevTouchPosX = event.getX(); prevTouchPosY = event.getY(); return super.onTouchEvent( event ); } } class AppObject { // 効果音 public static final int SOUND_HIT = 0; private ImageArray imageArray = new ImageArray( 128 ); private TestObject[] testObj = new TestObject[ 8 ]; private ShotObject[] shotObj = new ShotObject[ 16 ]; private ShotHitEffect[] shotHitObj = new ShotHitEffect[ 64 ]; private PlayerObject playerObject = new PlayerObject(); private RenderObject tmpObj = new RenderObject(); private Texture texture = new Texture(); FPSCount fps = new FPSCount(); SoundManager sound = new SoundManager(); Random random = new Random(); static private AppObject instance = null; static public AppObject getInstance() { if ( instance == null ) instance = new AppObject(); return instance; } FPSCount getFPSCount() { return fps; } SoundManager getSoundManager() { return sound; } PlayerObject getPlayerObject() { return playerObject; } private AppObject() { for (int i=0; i<testObj.length; ++i) { float x = (random.nextFloat() * 2 - 1) * 2; float y = (random.nextFloat() * 2 - 1) * 2; testObj[i] = new TestObject( x, y ); testObj[i].width = 0.5f; testObj[i].height = 0.5f; testObj[i].vx = (random.nextFloat()*2-1) * 0.0625f; testObj[i].vy = (random.nextFloat()*2-1) * 0.0625f; testObj[i].setUVs( 256, 256, 0, 0, 32 , 32 ); } for (int i=0; i<shotObj.length; ++i) { shotObj[i] = new ShotObject(); } for (int i=0; i<shotHitObj.length; ++i) { shotHitObj[i] = new ShotHitEffect(); } } public void makeTexture( GL10 gl, Bitmap bmp ) { texture.makeTexture( gl, bmp ); } public void update() { int n = testObj.length; for (int i=0; i<n; ++i) { testObj[i].update(); } playerObject.update(); n = shotObj.length; for (int i=0; i<n; ++i) { if ( shotObj[i].isUse() ) shotObj[i].update(); } for (int i=0; i<shotHitObj.length; ++i) { if ( shotHitObj[i].isUse() ) shotHitObj[i].update(); } collision(); sound.update(); } public void render( GL11 gl ) { imageArray.clear(); int n = testObj.length; for (int i=0; i<n; ++i) { imageArray.push( testObj[i] ); } imageArray.push( playerObject ); n = shotObj.length; for (int i=0; i<n; ++i) { if ( shotObj[i].isUse() ) { imageArray.push( shotObj[i] ); } } for (int i=0; i<shotHitObj.length; ++i) { if ( shotHitObj[i].isUse() ) { imageArray.push( shotHitObj[i] ); } } renderNum( fps.getFps() ); imageArray.render( gl, texture.getTextureID() ); } public void renderNum( int num ) { tmpObj.x = 0; tmpObj.y = 0; tmpObj.width = 0.5f; tmpObj.height = 0.5f; for (;;) { tmpObj.setUVs( 256, 256, 0 + (num%10)*16, 48, 16, 16 ); imageArray.push( tmpObj ); num /= 10; if ( num == 0 ) break; tmpObj.x -= 0.5f; } } public void createShotObject( float x, float y, float angle, float speed ) { for (int i=0; i<shotObj.length; ++i) { if ( !shotObj[i].isUse() ) { shotObj[i].set( x, y, angle, speed ); break; } } } public void createShotHitEffect( float x, float y ) { int num = 0; for (int i=0; i<shotHitObj.length; ++i) { if ( !shotHitObj[i].isUse() ) { shotHitObj[i].set( x, y, random.nextFloat()*360, random.nextFloat()*0.25f ); if ( ++num > 8 ) break; } } } public void collision() { for (int i=0; i<testObj.length; ++i) { for (int j=0; j<shotObj.length; ++j) { if ( shotObj[j].isUse() ) { float tmpX = testObj[i].x - shotObj[j].x; float tmpY = testObj[i].y - shotObj[j].y; double dist = Math.sqrt( tmpX*tmpX+tmpY*tmpY ); if ( dist < 0.25 ) // 大きさは適当 { shotObj[j].kill(); createShotHitEffect( shotObj[j].x, shotObj[j].y ); sound.play( SOUND_HIT ); break; } } } } } } class GLRenderer implements GLSurfaceView.Renderer { private Activity activity; private float aspect; private long prevTime; GLRenderer( Activity activity ) { this.activity = activity; prevTime = 0; } // @Override public void onSurfaceCreated( GL10 gl, EGLConfig eglConfig ) { gl.glEnableClientState( GL10.GL_VERTEX_ARRAY ); gl.glEnableClientState( GL10.GL_TEXTURE_COORD_ARRAY ); gl.glEnable( GL10.GL_TEXTURE_2D ); ///* gl.glFrontFace( GL10.GL_CCW ); gl.glEnable( GL10.GL_CULL_FACE ); gl.glCullFace( GL10.GL_BACK ); //*/ Bitmap bmp = BitmapFactory.decodeResource( activity.getResources(), R.drawable.image ); AppObject.getInstance().makeTexture( gl, bmp ); gl.glEnable( GL10.GL_BLEND ); gl.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); } // @Override public void onSurfaceChanged( GL10 gl, int w, int h ) { gl.glViewport( 0, 0, w, h ); aspect = (float)w / (float)h; } // @Override public void onDrawFrame( GL10 gl ) { AppObject.getInstance().update(); gl.glClearColor( 1, 1, 1, 1 ); gl.glClear( GL10.GL_COLOR_BUFFER_BIT ); gl.glMatrixMode( GL10.GL_PROJECTION ); gl.glLoadIdentity(); GLU.gluPerspective( gl, 45.f, aspect, 0.01f, 100 ); gl.glMatrixMode( GL10.GL_MODELVIEW ); gl.glLoadIdentity(); GLU.gluLookAt( gl, 0,0,5, 0,0,0, 0,1,0 ); // gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR ); // gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR ); AppObject.getInstance().render( (GL11)gl ); long now = System.currentTimeMillis(); if ( now-prevTime < 33 ) { try { Thread.sleep( 33 - (now-prevTime) ); } catch ( Exception e ) {} } prevTime = now; AppObject.getInstance().getFPSCount().update( now ); } } class Texture { private int textureID; public Texture() { textureID = 0; } public void dispose( GL10 gl ) { if ( textureID != 0 ) { gl.glDeleteTextures( 0, new int[]{textureID}, 0 ); textureID = 0; } } public int makeTexture( GL10 gl, Bitmap bmp ) { dispose( gl ); int[] textureIDs = new int[1]; gl.glGenTextures( 1, textureIDs, 0 ); gl.glActiveTexture( GL10.GL_TEXTURE0 ); gl.glBindTexture( GL10.GL_TEXTURE_2D, textureIDs[0] ); GLUtils.texImage2D( GL10.GL_TEXTURE_2D, 0, bmp, 0 ); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST ); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST ); textureID = textureIDs[0]; return textureIDs[0]; } int getTextureID() { return textureID; } } class ImageArray { private FloatBuffer vertexBuffer; private FloatBuffer uvBuffer; private ShortBuffer indexBuffer; private int maxNumOfSprite; private int numOfRenderSprite; /** * constructor * @param num numOfSprite */ public ImageArray( int numOfSprite ) { this.maxNumOfSprite = numOfSprite; // 四角の数*(x,y,z)*4(=四角で必要な頂点数)*sizeof(float) vertexBuffer = ByteBuffer.allocateDirect( numOfSprite * 3 * 4 * 4 ).order( ByteOrder.nativeOrder() ).asFloatBuffer(); // 四角の数*(u,v)*4(=四角で必要な頂点数)*sizeof(float) uvBuffer = ByteBuffer.allocateDirect( numOfSprite * 2 * 4 * 4 ).order( ByteOrder.nativeOrder() ).asFloatBuffer(); // 四角の数*6(=四角で必要な頂点index数) * sizeof(short) indexBuffer = ByteBuffer.allocateDirect( numOfSprite * 6 * 2 ).order( ByteOrder.nativeOrder() ).asShortBuffer(); setIndexs(); vertexBuffer.position( 0 ); uvBuffer.position( 0 ); indexBuffer.position( 0 ); numOfRenderSprite = 0; } public void push( RenderObject obj ) { if ( numOfRenderSprite < maxNumOfSprite ) { int n = numOfRenderSprite; /* double rad = ((double)obj.angle / 180) * Math.PI; float c = (float)Math.cos( rad ); float s = (float)Math.sin( rad ); vertexBuffer.put( n*3*4+0, -0.5f*obj.width*(-s) - 0.5f*obj.height*c + obj.x ); // 左上 vertexBuffer.put( n*3*4+1, 0.5f*obj.height*c + (-0.5f)*obj.width*(-s) + obj.y ); vertexBuffer.put( n*3*4+2, 0 ); vertexBuffer.put( n*3*4+3, 0.5f*obj.width*c - 0.5f*obj.height*s + obj.x ); // 右上 vertexBuffer.put( n*3*4+4, 0.5f*obj.height*s + 0.5f*obj.width*c + obj.y ); vertexBuffer.put( n*3*4+5, 0 ); vertexBuffer.put( n*3*4+6, -0.5f*obj.width*(-c) - (-0.5f)*obj.height*(-s) + obj.x ); // 左下 vertexBuffer.put( n*3*4+7, -0.5f*obj.height*(-s) + (-0.5f)*obj.width*(-c) + obj.y ); vertexBuffer.put( n*3*4+8, 0 ); vertexBuffer.put( n*3*4+9, 0.5f*obj.width*s - (-0.5f)*obj.height*(-c)+ obj.x ); // 右下 vertexBuffer.put( n*3*4+10, -0.5f*obj.height*(-c) + 0.5f*obj.width*s + obj.y ); vertexBuffer.put( n*3*4+11, 0 ); //*/ ///* vertexBuffer.put( n*3*4+0, -0.5f*obj.width + obj.x ); // 左上 vertexBuffer.put( n*3*4+1, 0.5f*obj.height + obj.y ); vertexBuffer.put( n*3*4+2, 0 ); vertexBuffer.put( n*3*4+3, 0.5f*obj.width + obj.x ); // 右上 vertexBuffer.put( n*3*4+4, 0.5f*obj.height + obj.y ); vertexBuffer.put( n*3*4+5, 0 ); vertexBuffer.put( n*3*4+6, -0.5f*obj.width + obj.x ); // 左下 vertexBuffer.put( n*3*4+7, -0.5f*obj.height + obj.y ); vertexBuffer.put( n*3*4+8, 0 ); vertexBuffer.put( n*3*4+9, 0.5f*obj.width + obj.x ); // 右下 vertexBuffer.put( n*3*4+10, -0.5f*obj.height + obj.y ); vertexBuffer.put( n*3*4+11, 0 ); //*/ uvBuffer.put( n*2*4+0, obj.uvs[0] ); //u1[0] uvBuffer.put( n*2*4+1, obj.uvs[1] ); //v1 uvBuffer.put( n*2*4+2, obj.uvs[2] ); //u2[1] uvBuffer.put( n*2*4+3, obj.uvs[1] ); //v1 uvBuffer.put( n*2*4+4, obj.uvs[0] ); //u1[2] uvBuffer.put( n*2*4+5, obj.uvs[3] ); //v2 uvBuffer.put( n*2*4+6, obj.uvs[2] ); //u2[3] uvBuffer.put( n*2*4+7, obj.uvs[3] ); //v2 numOfRenderSprite++; } } public void render( GL11 gl, int textureID ) { if ( numOfRenderSprite > 0 ) { vertexBuffer.position( 0 ); uvBuffer.position( 0 ); indexBuffer.position( 0 ); gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer ); gl.glActiveTexture( GL10.GL_TEXTURE0 ); gl.glBindTexture( GL10.GL_TEXTURE_2D, textureID ); gl.glTexCoordPointer( 2, GL10.GL_FLOAT, 0, uvBuffer ); gl.glDrawElements( GL10.GL_TRIANGLES, 3*2*numOfRenderSprite, GL10.GL_UNSIGNED_SHORT, indexBuffer ); } } public void render( GL11 gl, int textureID, int startIndex, int endIndex ) { if ( numOfRenderSprite > 0 && startIndex < numOfRenderSprite && endIndex < numOfRenderSprite ) { vertexBuffer.position( startIndex*3*4 ); uvBuffer.position( startIndex*2*4 ); indexBuffer.position( startIndex*3*2 ); gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer ); gl.glActiveTexture( GL10.GL_TEXTURE0 ); gl.glBindTexture( GL10.GL_TEXTURE_2D, textureID ); gl.glTexCoordPointer( 2, GL10.GL_FLOAT, 0, uvBuffer ); gl.glDrawElements( GL10.GL_TRIANGLES, 3*2*(endIndex-startIndex), GL10.GL_UNSIGNED_SHORT, indexBuffer ); } } public void clear() { numOfRenderSprite = 0; } public FloatBuffer getVertexBuffer() { return vertexBuffer; } public FloatBuffer getUVBuffer() { return uvBuffer; } public ShortBuffer getIndexBuffer() { return indexBuffer; } public int getMaxNumOfSprite() { return maxNumOfSprite; } public int getNumOfRenderSprite() { return numOfRenderSprite; } private void setIndexs() { int n = maxNumOfSprite; for (int i=0; i<n; ++i) { indexBuffer.put( i*6 + 0, (short)(0 + i*4) ); indexBuffer.put( i*6 + 1, (short)(2 + i*4) ); indexBuffer.put( i*6 + 2, (short)(1 + i*4) ); indexBuffer.put( i*6 + 3, (short)(2 + i*4) ); indexBuffer.put( i*6 + 4, (short)(3 + i*4) ); indexBuffer.put( i*6 + 5, (short)(1 + i*4) ); } } } class RenderObject { public float x, y, width, height; public float angle; // no use public float[] uvs = new float[ 4 ]; //< u1, v1, u2, v2 protected void setUVs( int texWidth, int texHeight, int srcX, int srcY, int width, int height ) { uvs[0] = (float)srcX / (float)texWidth; uvs[1] = (float)srcY / (float)texHeight; uvs[2] = (float)(srcX+width) / (float)texWidth; uvs[3] = (float)(srcY+height) / (float)texHeight; } } class TestObject extends RenderObject { public float vx; public float vy; public TestObject( float x, float y ) { super.x = x; super.y = y; vx = -0.5f; vy = -0.5f; super.angle = 0; } void update() { angle += 1; if ( angle > 360 ) angle = 360; x += vx; y += vy; boolean playSound = false; if ( x < -3 || x > 3 ) { vx = -vx; playSound = true; } if ( y < -2 || y > 2 ) { vy = -vy; playSound = true; } /* if ( playSound ) { AppObject.getInstance().getSoundManager().play( AppObject.SOUND_HIT ); } */ } } class PlayerObject extends RenderObject { private float vx, vy; private int time; public PlayerObject() { vx = vy = 0; time = 0; super.x = 0; super.y = 0; super.width = 0.5f; super.height = 0.5f; super.angle = 0; setUVs( 256, 256, 32, 0, 32, 32 ); } synchronized void update() { if ( (time & 3) == 0 ) { float angle = (float)Math.atan2( (double)vy, (double)vx ) * 180 / 3.141592f; AppObject.getInstance().createShotObject( super.x, super.y, angle, 0.25f ); } super.x += vx; super.y += vy; vy = vx = 0; time++; } synchronized void move( float vx, float vy ) { this.vx += vx; this.vy += vy; } } class ShotObject extends RenderObject { private float vx; private float vy; private int time; private boolean isUse; ShotObject() { isUse = false; super.width = 0.5f; super.height = 0.5f; super.setUVs( 256, 256, 64, 0, 32, 32 ); } public void set( float x, float y, float angle, float speed ) { //System.out.println("shotObject.set() " + x + ", " + y ); isUse = true; time = 0; super.x = x; super.y = y; super.angle = angle; double rad = angle / 180 * 3.141592; vx = (float)Math.cos( rad ) * speed; vy = (float)Math.sin( rad ) * speed; } public void update() { super.x += vx; super.y += vy; if ( x < -3 || x > 3 || y < -2 || y > 2 || time > 60 ) { //System.out.println("shotObject.update() - no used" ); kill(); } time++; } public boolean isUse() { return isUse; } public void kill() { isUse = false; } } class ShotHitEffect extends RenderObject { private float vx; private float vy; private int time; private boolean isUse; ShotHitEffect() { isUse = false; super.width = 0.125f; super.height = 0.125f; super.setUVs( 256, 256, 64, 0, 32, 32 ); } public void set( float x, float y, float angle, float speed ) { //System.out.println("shotObject.set() " + x + ", " + y ); isUse = true; time = 0; super.x = x; super.y = y; super.angle = angle; double rad = angle / 180 * 3.141592; vx = (float)Math.cos( rad ) * speed; vy = (float)Math.sin( rad ) * speed; } public void update() { super.x += vx; super.y += vy; vx *= 0.92f; vy *= 0.92f; if ( time > 18 ) { //System.out.println("shotObject.update() - no used" ); kill(); } time++; } public boolean isUse() { return isUse; } public void kill() { isUse = false; } } class FPSCount { private int fps; private int[] data = new int[ 32 ]; private int lastSecond; public FPSCount() { int n = data.length; for (int i=0; i<n; ++i) { data[i] = 0; } fps = 0; lastSecond = 0; } void update( long now ) { //long now = System.currentTimeMillis(); int a = (int)(now / 200); data[ a & 31 ]++; if ( a != lastSecond ) { lastSecond = a; fps = 0; for (int i=1; i<=5; ++i) { fps += data[ (a-i)&31 ]; } for (int i=5; i<=20; ++i) data[ (a+i)&31 ] = 0; } } public int getFps() { return fps; } } //[参考]http://www.kumikomi.net/archives/2011/12/di09and3.php?page=4 class SoundManager { class SoundData { public static final int STATE_NONE = 0; public static final int STATE_PLAY = 1; public static final int STATE_STOP = 2; public int soundID; public int state; public int streamID; SoundData() { soundID = -1; streamID = -1; state = STATE_NONE; } } private static final int NUM_OF_STREAM = 8; private SoundPool sound = new SoundPool( NUM_OF_STREAM, AudioManager.STREAM_MUSIC, 0 ); private HashMap<Integer, SoundData> list = new HashMap<Integer, SoundData>(); void update() { for ( Map.Entry<Integer, SoundData> e: list.entrySet() ) { SoundData data = e.getValue(); if ( data.state == SoundData.STATE_PLAY ) { sound.stop( data.streamID ); // 再生 data.streamID = sound.play( data.soundID, 1.0f, 1.0f, 0, 0, 1.0f ); data.state = SoundData.STATE_NONE; } } } void create( int soundID, Context context, int resID ) { release( soundID ); SoundData data = new SoundData(); data.soundID = sound.load( context, resID, 1 ); list.put( soundID, data ); } void release( int soundID ) { SoundData data = list.get( soundID ); if ( data != null ) { sound.unload( data.soundID ); list.remove( soundID ); } } void release() { for ( Map.Entry<Integer, SoundData> e: list.entrySet() ) { sound.unload( e.getValue().soundID ); } list.clear(); } public void play( int soundID ) { SoundData data = list.get( soundID ); if ( data != null ) { data.state = SoundData.STATE_PLAY; } } }
0 件のコメント:
コメントを投稿