2012年7月27日金曜日

日記ちゃんあんどろ


GL10PrimitiveEx1.apk
size: 1.59MB

 BGMの再生、オブジェクト管理クラスの追加あたり。
オブジェクト動作管理は自分で双方向リストを構築してなんとかしている。
javaの汎用コンテナだとメモリをどっかから持ってきそうでいやな予感がしたから回避した。
 あらかじめ用意した頂点数をオーバーするので表示されないオブジェクトがちらちらする、ロマン。
実機で10FPS程度、エミュだと3FPSぐらい。もうちょっと現実的な環境を用意したいが。
 未使用オブジェクトの検索が線形なので、
リストにするとかして効率をあげたほうがいいかも。



以下ソースコード。1200行ぐらい
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;
 
import android.media.MediaPlayer;
 
 
 
 
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();
    AppObject.getInstance().getMusic().create( this, R.raw.music );
    AppObject.getInstance().getMusic().play();
  }
  
//  @Override
  public void onPause()
  {
    super.onPause();
    glView.onPause();
    AppObject.getInstance().getMusic().release();
  }
  
  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;
  
  // priority
  public static final int PRIO_ENEMY = 1;
  public static final int PRIO_PLAYER = 2;
  public static final int PRIO_SHOT = 3;
  public static final int PRIO_EFFECT = 4;
  public static final int PRIO_BULLET = 5;
 
  
  
  private ImageArray imageArray = new ImageArray( 128 );
  
  private TestObject[] testObj = new TestObject[ 8 ];
  private ShotObject[] shotObj = new ShotObject[ 16 ];
  private Bullet[] bulletObj = new Bullet[ 128 ];
  private ShotHitEffect[] shotHitObj = new ShotHitEffect[ 64 ];
  private PlayerObject playerObject = new PlayerObject();
  private RenderObject tmpObj = new RenderObject();
  
  TaskManager taskMan = new TaskManager();
  
  private Texture texture = new Texture();
  FPSCount fps = new FPSCount();
  SoundManager sound = new SoundManager();
  Music music = new Music();
  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; }
  Music getMusic() { return music; }
  PlayerObject getPlayerObject() { return playerObject; }
  Random getRandom() { return random; }
  
  
  
  
  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].vx = (random.nextFloat()*2-1) * 0.0625f;
      testObj[i].vy = (random.nextFloat()*2-1) * 0.0625f;
 
      taskMan.add( testObj[i], PRIO_ENEMY );
    }
    
    for (int i=0; i<shotObj.length; ++i)
    {
      shotObj[i] = new ShotObject();
    }
    
    for (int i=0; i<shotHitObj.length; ++i)
    {
      shotHitObj[i] = new ShotHitEffect();
    }
    
    for (int i=0; i<bulletObj.length; ++i)
    {
      bulletObj[i] = new Bullet(); 
    }
    
    taskMan.add( playerObject, PRIO_PLAYER );
  }
  
  
  
  public void makeTexture( GL10 gl, Bitmap bmp )
  {
    texture.makeTexture( gl, bmp );
  }
  
  public void update()
  {
    taskMan.update();
    
    sound.update();
  }
  
  public void render( GL11 gl )
  {
    imageArray.clear();
 
    taskMan.render( imageArray );
    renderNum( fps.getFps(), 0, 0 );
    renderNum( taskMan.getCount(), 0, 0.5f );
 
    imageArray.render( gl, texture.getTextureID() );
  }
  
  public void renderNum( int num, float x, float y )
  {
    tmpObj.x = x;
    tmpObj.y = y;
    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].getState() == Task.STATE_NONE )
      {
        shotObj[i].set( x, y, angle, speed );
        taskMan.add( shotObj[i], PRIO_SHOT );
        break;
      }
    }
  }
  
  public void createShotHitEffect( float x, float y )
  {
    int num = 0;
    for (int i=0; i<shotHitObj.length; ++i)
    {
      if ( shotHitObj[i].getState() == Task.STATE_NONE )
      {
        shotHitObj[i].set( x, y, random.nextFloat()*360, random.nextFloat()*0.25f );
        taskMan.add( shotHitObj[i], PRIO_EFFECT );
        if ( ++num > 8 ) break;
      }
    }
  }
  
  public void createBullet( float x, float y, float angle, float speed )
  {
    for (int i=0; i<bulletObj.length; ++i)
    {
      if ( bulletObj[i].getState() == Task.STATE_NONE )
      {
        bulletObj[i].set( x, y, angle, speed );
        taskMan.add( bulletObj[i], PRIO_BULLET );
        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 Task
{
  private RenderObject renderObj = new RenderObject();
  public float vx;
  public float vy;
  int time;
 
  public TestObject( float x, float y )
  {
    renderObj.x = x;
    renderObj.y = y;
    renderObj.width = 0.5f; 
    renderObj.height = 0.5f;
    renderObj.setUVs( 256, 256, 0, 0, 32 , 32 );
    renderObj.angle = x*27 + y*31;
  }
  
  public void update()
  {
    renderObj.angle += 8;
    if ( renderObj.angle > 360 ) renderObj.angle -= 360;
    
    renderObj.x += vx;
    renderObj.y += vy;
    if ( renderObj.x < -3 || renderObj.x > 3 ) { vx = -vx; }
    if ( renderObj.y < -2 || renderObj.y > 2 ) { vy = -vy; }
    
    if ( (time & 3) == 0 )
    {
      float targetX = AppObject.getInstance().getPlayerObject().getPosX();
      float targetY = AppObject.getInstance().getPlayerObject().getPosY();
      float angle = (float)Math.atan2( (double)(targetY-getPosY()), (double)(targetX-getPosX()) ) * 180 / 3.141592f;
      
      for (int i=0; i<8; ++i)
      {
        float speed = AppObject.getInstance().getRandom().nextFloat()*0.125f + 0.0625f;
        AppObject.getInstance().createBullet( getPosX(), getPosY(), angle, speed );
      }
    }
    
    collision();
    
    time++;
/*    
    if ( playSound )
    {
      AppObject.getInstance().getSoundManager().play( AppObject.SOUND_HIT );
    }
*/
  }
  
  public void render( ImageArray image )
  {
    image.push( renderObj );
  }
  
  public float getPosX() { return renderObj.x; }
  public float getPosY() { return renderObj.y; }
  
  private void collision()
  {
    Task runner = parent.find( AppObject.PRIO_SHOT );
    while ( runner != null )
    {
      if ( runner.getPriority() == AppObject.PRIO_SHOT )
      {
        if ( runner.getState() == Task.STATE_NORMAL )
        {
          try
          {
            ShotObject obj = (ShotObject)runner;
            double tmpX = obj.getPosX() - getPosX();
            double tmpY = obj.getPosY() - getPosY();
            double dist = Math.sqrt( tmpX*tmpX + tmpY*tmpY );
            if ( dist < 0.25 )
            {
              AppObject.getInstance().createShotHitEffect( obj.getPosX(), obj.getPosY() );
              obj.kill();
            }
          }
          catch ( Exception e ) {}
        }
      }
      else break;
      
      runner = runner.next;
    }
  }
}
 
 
 
 
class PlayerObject extends Task
{
  private RenderObject renderObj = new RenderObject();
  private float vx, vy;
  private int time;
 
  public PlayerObject()
  {
    vx = vy = 0;
    time = 0;
 
    renderObj.x = 0;
    renderObj.y = 0;
    renderObj.width = 0.5f;
    renderObj.height = 0.5f;
    renderObj.angle = 0;
    renderObj.setUVs( 256, 256, 32, 0, 32, 32 );
  }
 
  public void update()
  {
    if ( (time & 3) == 0 )
    {
      float angle = (float)Math.atan2( (double)vy, (double)vx ) * 180 / 3.141592f;
      AppObject.getInstance().createShotObject( renderObj.x, renderObj.y, angle, 0.25f );
    }
    
    renderObj.x += vx;
    renderObj.y += vy;
    vy = vx = 0;
 
    collision();
    
    
    time++;
  }
  
  public void render( ImageArray image )
  {
    image.push( renderObj );
  }
  
  synchronized void move( float vx, float vy )
  {
    this.vx += vx;
    this.vy += vy;
  }
  
  private void collision()
  {
    Task runner = parent.find( AppObject.PRIO_BULLET );
    while ( runner != null )
    {
      if ( runner.getPriority() == AppObject.PRIO_BULLET )
      {
        if ( runner.getState() == Task.STATE_NORMAL )
        {
          try
          {
            Bullet b = (Bullet)runner;
            double tmpX = b.getPosX() - getPosX();
            double tmpY = b.getPosY() - getPosY();
            double dist = Math.sqrt( tmpX*tmpX + tmpY*tmpY );
            if ( dist < 0.125 )
            {
              b.kill();
            }
          }
          catch ( Exception e )
          {
          }
          
        }
      }
      else break;
      
      runner = runner.next;
    }
  }
  
  
  public float getPosX() { return renderObj.x; }
  public float getPosY() { return renderObj.y; }
}
 
 
 
 
class ShotObject extends Task
{
  private RenderObject renderObj = new RenderObject(); 
  private float vx;
  private float vy;
  private int time;
  
  ShotObject()
  {
    renderObj.width = 0.5f;
    renderObj.height = 0.5f;
    renderObj.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 );
    super.state = Task.STATE_NORMAL;
 
    time = 0;
    renderObj.x = x;
    renderObj.y = y;
    renderObj.angle = angle;
    double rad = angle / 180 * 3.141592;
    vx = (float)Math.cos( rad ) * speed;
    vy = (float)Math.sin( rad ) * speed;
  }
  
  public void update()
  {
    renderObj.x += vx;
    renderObj.y += vy;
    
    if ( renderObj.x < -3 || renderObj.x > 3 || renderObj.y < -2 || renderObj.y > 2 || time > 60 )
    {
      //System.out.println("shotObject.update() - no used" );
      kill();
    }
 
    time++;
  }
  
  public void render( ImageArray image )
  {
    image.push( renderObj );
  }
  
 
  public float getPosX() { return renderObj.x; }
  public float getPosY() { return renderObj.y; }
}
 
 
 
 
 
 
class ShotHitEffect extends Task
{
  private RenderObject renderObj = new RenderObject();
  private float vx;
  private float vy;
  private int time;
  
  ShotHitEffect()
  {
    renderObj.width = 0.125f;
    renderObj.height = 0.125f;
    renderObj.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 );
    super.state = Task.STATE_NORMAL;
 
    time = AppObject.getInstance().getRandom().nextInt() & 7;
    renderObj.x = x;
    renderObj.y = y;
    renderObj.angle = angle;
    double rad = angle / 180 * 3.141592;
    vx = (float)Math.cos( rad ) * speed;
    vy = (float)Math.sin( rad ) * speed;
  }
  
  public void update()
  {
    renderObj.x += vx;
    renderObj.y += vy;
    vx *= 0.92f;
    vy *= 0.92f;
    
    if ( time > 18 )
    {
      //System.out.println("shotObject.update() - no used" );
      kill();
    }
 
    time++;
  }
  
  public void render( ImageArray image )
  {
    image.push( renderObj );
  }
}
 
 
 
 
class Bullet extends Task
{
  private RenderObject renderObj = new RenderObject();
  private float vx;
  private float vy;
  private int time;
  
  Bullet()
  {
    renderObj.width = 0.25f;
    renderObj.height = 0.25f;
    renderObj.setUVs( 256, 256, 96, 0, 32, 32 );
  }
  
  public void set( float x, float y, float angle, float speed )
  {
    super.state = Task.STATE_NORMAL;
 
    time = 0;
    renderObj.x = x;
    renderObj.y = y;
    renderObj.angle = angle;
    double rad = angle / 180 * 3.141592;
    vx = (float)Math.cos( rad ) * speed;
    vy = (float)Math.sin( rad ) * speed;
  }
  
  public void update()
  {
    renderObj.x += vx;
    renderObj.y += vy;
    
    if ( renderObj.x < -3 || renderObj.x > 3 || renderObj.y < -2 || renderObj.y > 2 || time > 60 )
    {
      //System.out.println("shotObject.update() - no used" );
      kill();
    }
 
    time++;
  }
  
  public void render( ImageArray image )
  {
    image.push( renderObj );
  }
  
  public float getPosX() { return renderObj.x; }
  public float getPosY() { return renderObj.y; } 
}
 
 
 
 
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;
    }
  }
 
}
 
 
 
 
class Music
{
  private MediaPlayer music = null;
  
  public void release()
  {
    if ( music != null )
    {
      music.stop();
      music.release();
      music = null;
    }
  }
  
  public void create( Context context, int resID )
  {
    release();
 
    music = MediaPlayer.create( context, resID );
    music.setLooping( true );
  }
  
  
  public void play()
  {
    if ( music != null )
    {
      music.start();
    }
  }
}
 
 
 
 
class Task
{
  public static final int STATE_NONE = -2;
  public static final int STATE_KILL = -1;
  public static final int STATE_NORMAL = 0;
  
  
  public int priority = 0;
  public int state = STATE_NONE;
  public Task prev = null;
  public Task next = null;
  public TaskManager parent = null;
  
  public void update() {}
  public void render( ImageArray image ) {}
  public void release() {}
  
  public int getPriority() { return priority; }
  public int getState() { return state; }
  public TaskManager getParent() { return parent; }
 
  public void kill() { state = STATE_KILL; }
}
 
 
 
class TaskManager
{  
  private Task begin = new Task();
  private Task end = new Task();
  
  
  public final static int MAX_PRIORITY = 16;
  private Task prioTop[] = new Task[ MAX_PRIORITY ];  //< ラストにendTaskをおくので+1する
  
  private int count = 0;
  
  
  private void link( Task newTask, Task next )
  {
    newTask.next = next;
    newTask.prev = next.prev;
    next.prev.next = newTask;
    next.prev = newTask;
  }
  
  private Task findTopPrioObj( int priority )
  {
    if ( priority >= 0 && priority < MAX_PRIORITY )
    {
      for (int i=priority; i<MAX_PRIORITY; ++i)
      {
        if ( prioTop[i] != null ) return prioTop[i]; 
      }
    }
    
    return null;
  }
  
  /**
   * constructor
   */
  TaskManager()
  {
    begin.priority = 0;
    begin.next = end;
    begin.parent = this;
    end.priority = MAX_PRIORITY;
    end.prev = begin;
    end.parent = this;
    
    prioTop[0] = begin;
    prioTop[ MAX_PRIORITY-1 ] = end;
  }
  
  public Task add( Task newTask, int priority )
  {
    if ( priority >= 0 && priority < MAX_PRIORITY )
    {
      Task next = findTopPrioObj( priority );
      if ( next == null ) next = end;
      
      link( newTask, next );
        
      newTask.priority = priority;
      newTask.parent = this;
      newTask.state = Task.STATE_NORMAL;
      prioTop[ priority ] = newTask;
      
      count++;
 
      return newTask;
    }
    
    return null;
  }
  
  public Task addBack( Task newTask, int priority )
  {
    if ( priority >= 0 && priority < MAX_PRIORITY )
    {
      Task next = findTopPrioObj( priority+1 );
      if ( next == null ) next = end;
      
      link( newTask, next );
      
      newTask.priority = priority;
      newTask.parent = this;
      if ( prioTop[ priority ] == null ) prioTop[ priority ] = newTask;
      
      count++;
 
      return newTask;
    }
    
    return null;
  }
  
  public void update()
  {
    Task runner = begin;
    while ( runner != null )
    {
      if ( runner.getState() == Task.STATE_NORMAL )
      {
        runner.update();
      }
      else if ( runner.getState() == Task.STATE_KILL )
      {
        Task del = runner;
        runner = runner.prev;
        del.prev.next = del.next;
        del.next.prev = del.prev;
        // priority先端チェック
        if ( prioTop[del.getPriority()] == del )
        {
          if ( del.next.getPriority() == del.getPriority() )
          {
            prioTop[ del.getPriority() ] = del.next;
          }
          else
          {
            prioTop[ del.getPriority() ] = null;
          }
        }
        
        count--;
        del.state = Task.STATE_NONE;
        del.release();
      }
      
      runner = runner.next;
    }
  }
  
  public void render( ImageArray image )
  {
    Task runner = begin;
    while ( runner != null )
    {
      if ( runner.getState() == Task.STATE_NORMAL ) runner.render( image );
      runner = runner.next;
    }
  }
  
  public Task find( int priority )
  {
    if ( priority >= 0 && priority < MAX_PRIORITY )
    {
      return prioTop[ priority ];
    }
 
    return null;
  }
  
  public int getCount() { return count; }
}

2012年7月25日水曜日

日記ちゃん


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

2012年7月21日土曜日

日記ちゃんあんどろ


GL10PrimitiveEx1.apk
size: 62KB
 とりあえずオブジェクトの描画と音をならせるようになった。
GLSurfaceViewにおまかせコース。
頂点をバッファに登録していき、一度に全部描く方法をとった。
実機だと三角形32個ぐらいの描画が限界で、これじゃあどうにもならなそうだ。
頂点数が問題ならともかく、描画面積的に限界がきてるっぽいから背景描いたりとかもちろんできない。



以下ソースコード。面倒だったから1ファイルに全部書いた。
某所のサンプルをいじくったので痕跡が残ってる。
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.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.media.SoundPool;
import android.media.AudioManager;
import android.content.Context;
 
 
 
 
 
public class GL10PrimitiveEx1 extends Activity
{
  private GLSurfaceView glView;
  
  
//  @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 );
    
    AppObject.getInstance().getSoundManager().create( this );
  }
  
//  @Override
  public void onResume()
  {
    super.onResume();
    glView.onResume();
  }
  
//  @Override
  public void onPause()
  {
    super.onPause();
    glView.onPause();
  }
}
 
 
 
 
 
class AppObject
{
  private ImageArray imageArray = new ImageArray( 128 );
  private TestObject[] testObj = new TestObject[ 32 ];
  private Texture texture = new Texture();
  FPSCount fps = new FPSCount();
  SoundManager sound = new SoundManager();
  
  
  
  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; }
  
  private AppObject()
  {
    Random random = new Random();
    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 );
    }
  }
  
  
  
  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();
    }
    
    sound.update();
  }
  
  public void render( GL11 gl )
  {
    imageArray.clear();
 
    int n = testObj.length;
    for (int i=0; i<n; ++i)
    {
      imageArray.push( testObj[i] );
    }
 
    renderNum( fps.getFps() );
 
    imageArray.render( gl, texture.getTextureID() );
  }
  
  public void renderNum( int num )
  {
    RenderObject obj = new RenderObject();
    obj.x = 0;
    obj.y = 0;
    obj.width = 0.5f;
    obj.height = 0.5f;
    for (;;)
    {
      obj.setUVs( 256, 256, 0 + (num%10)*16, 48, 16, 16 );
      imageArray.push( obj );
      
      
      num /= 10;
      if ( num == 0 ) break;
      obj.x -= 0.5f;
    }
  }
}
 
 
 
 
 
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;
//      float c = (float)Math.cos( obj.angle / 180 * Math.PI );
//      float s = (float)Math.sin( obj.angle / 180 * Math.PI );
      
      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 )
  {
    this.x = x;
    this.y = y;
    vx = -0.5f;
    vy = -0.5f;
  }
  
  void update()
  {
    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();
    }
  }
}
 
 
 
 
 
 
 
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; }
}
 
 
 
class SoundManager
{
  private SoundPool sound = new SoundPool( 8, AudioManager.STREAM_MUSIC, 0 );
  private int soundID = 0;
  private int useStreamID = 0;
  private boolean play = false;
  
  
  
  
  
  
  void update()
  {
    if ( play )
    {
      if ( useStreamID != 0 )
      {
        sound.stop( useStreamID );
      }
 
      useStreamID = sound.play( soundID, 1.0f, 1.0f, 0, 0, 1.0f );
      play = false;
    }
  }
 
  void create( Context context )
  {
    soundID = sound.load( context, R.raw.sound, 1 );
  }
  
  void release()
  {
    sound.unload( soundID );
  }
  
  public void play()
  {
    play = true;
  }
}