2011년 1월 5일 수요일

안드로이드 실제 디바이스에서 실행 및 디버깅 하기

1. AndroidManifest.xml 파일을 열고 <application> 태그 안에 android:debuggable="true" 를 추가한다.

2. 안드로이드 폰에서 MENU > Application > Development 메뉴에서 USB Debugging을 체크한다.

3. Android-SDK 폴더에서 <sdk>\google-usb_driver\ 에서 윈도우 USB드라이버를
설치한다.

4. 설정이 성공했는지 확인해보려면 android-sdk의 platform-tools폴더에 있는
adb를 "adb devices" 와 같이 실행해본다. device가 리스트로 뜨면 성공이다.

5. 성공했다면 이클립스를 실행하되 디버그 설정에서 Target탭에서 Deployment Target Selection Mode에서 Automatic을 선택한다. 디버깅을 하면 실제 디바이스에 apk파일을 다운로드한후 디바이스에서 실행/디버깅이 이뤄진다.

2011년 1월 4일 화요일

Configuring Android NDK development environment in Windows

=> Install Cygwin

LINK : http://cygwin.com/setup.exe

Install Devel group.

=> Install JDK

LINK : http://www.oracle.com/technetwork/java/javase/downloads/index.html
FILE NAME : jdk-6u23-windows-i586.exe

=> Install Eclipse

eclipse IDE LINK :
http://www.eclipse.org/downloads/download.php?file=/eclipse/downloads/drops/R-3.6.1-201009090800/eclipse-SDK-3.6.1-win32.zip

eclipse java plug-in LINK :
http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/helios/SR1/eclipse-java-helios-SR1-win32.zip

eclipse c++ plug-in LINK:
http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/helios/SR1/eclipse-cpp-helios-SR1-win32.zip

Download those three and unzip and copy java/c++ plug-in to eclipse IDE folder,
overwrite existing folders.

=> Install Android SDK

LINK : http://dl.google.com/android/installer_r08-windows.exe

=> Install Android NDK

LINK : http://dl.google.com/android/ndk/android-ndk-r5-windows.zip

Unzip and Rename&Copy to C:/android-ndk for convenience.

=> Install ADT-8.0.1

LINK : http://dl.google.com/android/ADT-8.0.1.zip

Unzip and copy to eclipse, overwrite existing folders.



After installing above programs, run eclipse and configure Android SDK to eclipse by
Setting eclipse menu Window->Preferences->Android->SDK Location a Folder you
installed Android SDK , this typically is "C:\Program Files\Android\android-sdk-windows".

Then create android project named TestJNI.

Create jni folder in your project root folder

In jni folder create file Android.mk and copy&paste and save below codes

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c test.cpp

include $(BUILD_SHARED_LIBRARY)
if your project uses c++ and STL then create file Application.mk and copy&paste and save below codes

APP_STL := stlport_shared

create file hello-jni.c with codes below

#include <string.h>
#include <jni.h>


const char *my_cpp_func();
jstring Java_a_b_TestJNIActivity_stringFromJNI( JNIEnv* env,jobject thiz )
{
    return (*env)->NewStringUTF(env, my_cpp_func());
}


create file test.cpp with codes below

#include <string>
class MyC
{
public:
 MyC()
 {}

 ~MyC()
 {}

 virtual const char * f()
 {
  return "MyC";
 }
};

class MyB:public MyC
{
 static std::string s;
public:
 MyB()
 {
  s="MyB ";
 }
 virtual const char * f()
 {
  s=s+"it works!!";
  return s.c_str();
 }
};

std::string MyB::s;
extern "C" const char *my_cpp_func()
{
 MyC *c=new MyB();
 const char *p=c->f();
 delete c;
 return p;
}


In the Property dialog of a created android project, click Builders
menu and click New... button to add new c++ compiling builder.

In Main tab set as below
Location = C:\cygwin\bin\bash.exe
Working Directory = C:\cygwin\bin
Arguments = --login -c "cd /cygdrive/c/users/workspace/TestJNI && /cygdrive/c/android-ndk/ndk-build"

modify "c/users/workspace" to your workspace folder.

In Refresh tab
check Refresh resources upon completion.
check Specific resources radio and Click Specify Resources... button and select "libs" folder in your project.
and Check Recursively include sub-folders.

In Build Options tab
check Launch In Background
check During Auto Builds
check Specify working set of relevant resources and click Specify Resources... Button and "jni" folder in
your project. jni folder is the source folder where your c/c++ sources will be located , if it's not exist then
create folder first.

Create TestJNIActivity.java source in the project with Eclipse IDE and paste this code

package a.b;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class TestJNIActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();

    public native String  unimplementedStringFromJNI();
    static {
     System.loadLibrary("stlport_shared");
        System.loadLibrary("hello-jni");
    }
}


run it in AVD emulator or Android Device. if text is not visible, and there's no libstlport_shared.so
file in libs folder then copy that file from \TestJNI\obj\local\armeabi folder to libs folder.

2010년 12월 31일 금요일

예제 안드로이드 게임 - 퍼즐 버블

안드로이드 게임 소스

package b.pb;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import b.pb.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.opengl.*;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
public class AndroPB implements GLSurfaceView.Renderer ,
 View.OnTouchListener
{
 int  VIEW_LEFT= 0;
 int  VIEW_TOP= 320;
 int  VIEW_WIDTH= 240;
 int  VIEW_HEIGHT =320;
 final int  NUM_MAX_BALL= 100;
 final int  NUM_BOARD_ROW= 11;
 final int  NUM_BOARD_COL= 8;
 final int  CONSOLE_HEIGHT =40;
 int  SCORE_LEFT =(VIEW_LEFT+3);
 int  SCORE_TOP;
 int  SCORE_WIDTH =(8*10);
 int  SCORE_HEIGHT =14;
 int  TIME_LEFT =(VIEW_LEFT+3);
 int  TIME_TOP;
 int  TIME_WIDTH =(8*10);
 int  TIME_HEIGHT= 14;
 int  CUR_BALL_X =(int)(VIEW_LEFT+VIEW_WIDTH/2);
 int  CUR_BALL_Y;
 int  NEXT_BALL_X =(int)(VIEW_LEFT+VIEW_WIDTH/2+CONSOLE_HEIGHT);
 int  NEXT_BALL_Y;
 final int  NUM_FRAME_BALL= 15;
 //Animation Stage
 final int  AS_NONE =0;
 final int  AS_FLOAT= 1;
 final int  AS_SINK =2;
 final float  SPEED_ANIMATION= 6;
 final int  NUM_BALL_TYPE= 9;
 final float  ROOT3        =1.73205080756887729352744634150590f;
 final float  V_SHOOTER     = 0.00310000000000000000000000000000f;
 final float  RAD_SHOOTER_LOWEST= 0.17453292519943295769236907684886f;
 final float RAD_90     =  1.57079632679489661923132169163975f ;
 final float RAD_SHOOTER_HIGHEST =2.96705972839036028077027430643060f;
 float BALL_REALRADIUS   =15.50f;
 int  BALL_PIXELRADIUS  =15;
 int  BALL_PIXELDIAMETER= 30;
 float  D_COLLISION     =29.5f;
 final float MAX_BONDING_SPEED =5000;
 final float MAX_BONDING_SPEED_NSB =750;
 final String FNAME_BALL ="ball.bmp";
 final int  NUM_MAX_FNAME =20;
 final int  ABF_FALLBALL =0x00000001;
 final int  ABF_SHOOTBALL =0x00000002;
 float SPEED_MOVE_NEXT;

 float frametime=0.03f;
 class BoardBall
 {
  boolean bExist;
  byte BallType;
  byte AniStage;
  float AniIndex;
  byte bProof;
 };
 class ActiveBall
 {
  byte bExist;
  byte bVisible;
  byte BallType;
  float AniIndex;
  byte AniStage;
  int Flag;
  int Age;
  float x,y;
  float vx,vy;
 };
 class Shooter
 {
  boolean bShotEnable;
  //
  byte Cur_BallType;
  int Cur_AniStage;
  float Cur_AniIndex;
  float Cur_x,Cur_y;
  //
  byte Next_BallType;
  int Next_AniStage;
  float Next_AniIndex;
  Shooter()
  {
   bShotEnable=false;
  }
 };
 class NearSameBall
 {
  int row,col;
 };
 class Console
 {
  float Time;//스테이지 시간
  float ShotTime;//일정시간이 지나면 자동발사
  int Score;//게임 점수
  Shooter S;//발사대 정보
  Console()
  {
   S=new Shooter();
  }
 };
 class BallPhysics
 {
  float g;
  float max_speed;
  float LifeTime;
 };
 class GAME
 {
  short nStage;
  String[] pStageFileName;
 };
 class STAGE
 {
  short nBall;
  String pBGMFileName;
  String pBGIFileName;
 };
 class OPTION
 {
  int Level;
  int bSound;
 };
 class FloatPoint
 {
  float x,y;
 };
 class IntegerPoint
 {
  int x,y;
 };

 OPTION Option;
 class RECT
 {
  public int left,top,right,bottom;
 };
 IntegerPoint CircleTable[];
 FloatPoint XYTable[];
 BallPhysics BPTable[];
 //Balls Data
 BoardBall BB[];
 ActiveBall AB[];
 int nAB=0;
 int nABTail=0;
 int nBB=0;
 //계기판
 Console CS;
 MediaPlayer snd_float,snd_glass,snd_metal,snd_nsb,snd_shooting,
  snd_sink,snd_stageclear,snd_stageend,snd_stagestart,snd_stone;
 //game,stage data
 GAME Game;
 STAGE CurStage;
 int nCurStage=0;
 Context ctx;
 public AndroPB(Context ctxi)
 {
  ((DefaultActivity)ctxi).glview.setOnTouchListener(this);
  ctx=ctxi;
  BB=new BoardBall[NUM_BOARD_ROW*NUM_BOARD_COL];
  for(int i=0;i<NUM_BOARD_ROW*NUM_BOARD_COL;i++)
   BB[i]=new BoardBall();
  AB=new ActiveBall[NUM_MAX_BALL];
  for(int i=0;i<NUM_MAX_BALL;i++)
   AB[i]=new ActiveBall();
  Game=new GAME();
  CurStage=new STAGE();
  Option=new OPTION();
  CS=new Console();
 
  snd_float=MediaPlayer.create(ctx,R.raw.floating);
  snd_glass=MediaPlayer.create(ctx,R.raw.glass);
  snd_metal=MediaPlayer.create(ctx,R.raw.metal);
  snd_nsb=MediaPlayer.create(ctx,R.raw.nsb);
  snd_shooting=MediaPlayer.create(ctx,R.raw.shooting);
  snd_sink=MediaPlayer.create(ctx,R.raw.sink);
  snd_stageclear=MediaPlayer.create(ctx,R.raw.stage_clear);
  snd_stageend=MediaPlayer.create(ctx,R.raw.stage_end);
  snd_stagestart=MediaPlayer.create(ctx,R.raw.stage_start);
  snd_stone=MediaPlayer.create(ctx,R.raw.stone);
 
  CircleTable=new IntegerPoint[2*6];
  CircleTable[0]=new IntegerPoint();
  CircleTable[0].x=-1;
  CircleTable[0].y=1;
  CircleTable[1]=new IntegerPoint();
  CircleTable[1].x=-1;
  CircleTable[1].y=0;
  CircleTable[2]=new IntegerPoint();
  CircleTable[2].x=-1;
  CircleTable[2].y=-1;
  CircleTable[3]=new IntegerPoint();
  CircleTable[3].x=0;
  CircleTable[3].y=-1;
  CircleTable[4]=new IntegerPoint();
  CircleTable[4].x=1;
  CircleTable[4].y=0;
  CircleTable[5]=new IntegerPoint();
  CircleTable[5].x=0;
  CircleTable[5].y=1;
  CircleTable[6]=new IntegerPoint();
  CircleTable[6].x=0;
  CircleTable[6].y=1;
  CircleTable[7]=new IntegerPoint();
  CircleTable[7].x=-1;
  CircleTable[7].y=0;
  CircleTable[8]=new IntegerPoint();
  CircleTable[8].x=0;
  CircleTable[8].y=-1;
  CircleTable[9]=new IntegerPoint();
  CircleTable[9].x=1;
  CircleTable[9].y=-1;
  CircleTable[10]=new IntegerPoint();
  CircleTable[10].x=1;
  CircleTable[10].y=0;
  CircleTable[11]=new IntegerPoint();
  CircleTable[11].x=1;
  CircleTable[11].y=1;
 
  XYTable=new FloatPoint[NUM_BOARD_ROW*NUM_BOARD_COL];
  for(int i=0;i<NUM_BOARD_ROW*NUM_BOARD_COL;i++)
   XYTable[i]=new FloatPoint();
 
  BPTable=new BallPhysics[9];
  BPTable[0]=new BallPhysics();
  BPTable[0].g=400;
  BPTable[0].max_speed=2000;
  BPTable[0].LifeTime=12;
 
  BPTable[1]=new BallPhysics();
  BPTable[1].g=400;
  BPTable[1].max_speed=2000;
  BPTable[1].LifeTime=12;
 
  BPTable[2]=new BallPhysics();
  BPTable[2].g=400;
  BPTable[2].max_speed=2000;
  BPTable[2].LifeTime=12;
 
  BPTable[3]=new BallPhysics();
  BPTable[3].g=400;
  BPTable[3].max_speed=2000;
  BPTable[3].LifeTime=12;
 
  BPTable[4]=new BallPhysics();
  BPTable[4].g=400;
  BPTable[4].max_speed=2000;
  BPTable[4].LifeTime=12;
 
  BPTable[5]=new BallPhysics();
  BPTable[5].g=400;
  BPTable[5].max_speed=2000;
  BPTable[5].LifeTime=12;
 
  BPTable[6]=new BallPhysics();
  BPTable[6].g=400;
  BPTable[6].max_speed=2000;
  BPTable[6].LifeTime=12;
 
  BPTable[7]=new BallPhysics();
  BPTable[7].g=400;
  BPTable[7].max_speed=2000;
  BPTable[7].LifeTime=12;
 
  BPTable[8]=new BallPhysics();
  BPTable[8].g=400;
  BPTable[8].max_speed=2000;
  BPTable[8].LifeTime=12;
 }
 public void onDrawFrame(GL10 gl)
 {
  try
  {
   gl.glDisable(GL10.GL_DITHER);
   gl.glTexEnvx(GL10.GL_TEXTURE_ENV,GL10.GL_TEXTURE_ENV_MODE,GL10.GL_MODULATE);
   gl.glClearColor(0,0,0,1);
   gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

   CS.Time+=frametime;
   if(CS.S.bShotEnable==true && nBB!=0)
    CS.ShotTime-=frametime;
  
   PlayMain(gl);
  }
  catch(Exception ex)
  {
   ex.printStackTrace();
   Log.v("",ex.toString());
  }
 }

 protected void draw_ball(GL10 gl,int x,int y,int balltype,int ani_stage)
 {
  float rr=BALL_REALRADIUS;
  float xx=x;
  float yy=y;
  float[] coords=
  {
   xx-rr,yy-rr,
   xx+rr,yy-rr,
   xx+rr,yy+rr,
   xx-rr,yy+rr,
  };
 
  vtx_rect.clear();
  for(int i=0;i<4;i++)
  {
   for(int j=0;j<2;j++)
   {
    vtx_rect.put(coords[i*2+j]);
   }
  }
  vtx_rect.position(0);
 
  int row=balltype;
  int col=ani_stage;
  double adj=307/512.0;
  float[] tcoords=
  {
   (float)((1/15.0)*(col+1)),(float)((1/9.0)*adj*(row+1)),
   (float)((1/15.0)*(col+0)),(float)((1/9.0)*adj*(row+1)),
   (float)((1/15.0)*(col+0)),(float)((1/9.0)*adj*(row+0)),
   (float)((1/15.0)*(col+1)),(float)((1/9.0)*adj*(row+0)),
  };
 
  tc_rect.clear();
  for(int i=0;i<4;i++)
  {
   for(int j=0;j<2;j++)
   {
    tc_rect.put(tcoords[i*2+j]);
   }
  }
  tc_rect.position(0);
 
  gl.glColor4f(1,1,1,1);
  gl.glVertexPointer(2,GL10.GL_FLOAT,0,vtx_rect);
  gl.glTexCoordPointer(2,GL10.GL_FLOAT,0,tc_rect);
  gl.glDrawElements(GL10.GL_TRIANGLES,6,GL10.GL_UNSIGNED_SHORT,idx_rect);
 }

 private FloatBuffer vtx_rect;
 private FloatBuffer tc_rect;
 private ShortBuffer idx_rect;
 private int balltex;
 protected void prepareResource(GL10 gl) throws IOException
 {
  ByteBuffer vbb=ByteBuffer.allocateDirect(4*2*4);
  vbb.order(ByteOrder.nativeOrder());
  vtx_rect=vbb.asFloatBuffer();
 
  ByteBuffer ibb=ByteBuffer.allocateDirect(6*2);
  ibb.order(ByteOrder.nativeOrder());
  idx_rect=ibb.asShortBuffer();
 
  ByteBuffer tbb=ByteBuffer.allocateDirect(4*2*4);
  tbb.order(ByteOrder.nativeOrder());
  tc_rect=tbb.asFloatBuffer();
 
  float[] coords=
  {
   -0.5f,-0.5f,
   0.5f,-0.5f,
   0.5f,0.5f,
   -0.5f,0.5f,
  };
 
  for(int i=0;i<4;i++)
  {
   for(int j=0;j<2;j++)
   {
    vtx_rect.put(coords[i*2+j]);
   }
  }
 
  int row=0,col=14;
  float[] tcoords=
  {
   (1/15.0f)*(col+1),(1/9.0f)*(row+1),
   (1/15.0f)*(col+0),(1/9.0f)*(row+1),
   (1/15.0f)*(col+0),(1/9.0f)*(row+0),
   (1/15.0f)*(col+1),(1/9.0f)*(row+0),
  };
 
  for(int i=0;i<4;i++)
  {
   for(int j=0;j<2;j++)
   {
    tc_rect.put(tcoords[i*2+j]);
   }
  }
 
  short[] idx={0,1,2,0,2,3};
  for(int i=0;i<6;i++)
  {
   idx_rect.put(idx[i]);
  }
 
  vtx_rect.position(0);
  idx_rect.position(0);
  tc_rect.position(0);
 
  loadbitmap(gl);
 
  LoadGame();
 }

 protected void loadbitmap(GL10 gl) throws IOException
 {
  gl.glEnable(GL10.GL_TEXTURE_2D);
  int[] textures=new int[1];
  gl.glGenTextures(1,textures,0);
  balltex=textures[0];
  gl.glBindTexture(GL10.GL_TEXTURE_2D,balltex);
 
  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_LINEAR);
  gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
  GL10.GL_REPEAT);
  gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
  GL10.GL_REPEAT);
 
  gl.glTexEnvf(GL10.GL_TEXTURE_ENV,GL10.GL_TEXTURE_ENV_MODE,GL10.GL_REPLACE);
  InputStream is=ctx.getAssets().open(FNAME_BALL);
  Bitmap bitmap;
  bitmap=BitmapFactory.decodeStream(is);
  is.close();
  GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,bitmap,0);
  bitmap.recycle();
 }
 public void onSurfaceChanged(GL10 gl,int width,int height)
 {
  VIEW_LEFT= 0;
  VIEW_WIDTH= width;
  VIEW_HEIGHT =height;
  VIEW_TOP= VIEW_HEIGHT;
  BALL_PIXELDIAMETER= (int)(VIEW_WIDTH/(float)NUM_BOARD_COL);
  BALL_REALRADIUS   =(float)BALL_PIXELDIAMETER/2; 
  BALL_PIXELRADIUS  =BALL_PIXELDIAMETER/2;
 
  SCORE_LEFT =(VIEW_LEFT+3);
  SCORE_TOP= (VIEW_TOP-VIEW_HEIGHT+CONSOLE_HEIGHT-4);
  SCORE_WIDTH =(8*10);
  SCORE_HEIGHT =14;
  TIME_LEFT =(VIEW_LEFT+3);
  TIME_TOP =(VIEW_TOP-VIEW_HEIGHT+CONSOLE_HEIGHT-22);
  TIME_WIDTH =(8*10);
  TIME_HEIGHT= 14;
  CUR_BALL_X =(int)(VIEW_LEFT+VIEW_WIDTH/2);
  CUR_BALL_Y =(int)BALL_PIXELDIAMETER;
 
  SPEED_MOVE_NEXT=BALL_PIXELDIAMETER*2;
  NEXT_BALL_X =(int)(VIEW_LEFT+VIEW_WIDTH/2+BALL_PIXELDIAMETER);
  NEXT_BALL_Y =(int)BALL_PIXELDIAMETER;
  CS.S.bShotEnable=false;
  CS.S.Cur_x=NEXT_BALL_X;
  CS.S.Cur_y=CUR_BALL_Y;
 
  D_COLLISION=BALL_PIXELDIAMETER;
  gl.glViewport(0,0,VIEW_WIDTH,VIEW_HEIGHT);
  gl.glMatrixMode(GL10.GL_PROJECTION);
  gl.glLoadIdentity();
  gl.glMatrixMode(GL10.GL_MODELVIEW);
  gl.glLoadIdentity();
 
  GLU.gluOrtho2D(gl,0,VIEW_WIDTH,0,VIEW_HEIGHT);
  MakeXYTable();
 }
 public void onSurfaceCreated(GL10 gl,EGLConfig config)
 {
  gl.glDisable(GL10.GL_DITHER);
  gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);
  gl.glClearColor(0.0f,0.0f,0.0f,1);
  gl.glShadeModel(GL10.GL_SMOOTH);
  gl.glEnable(GL10.GL_DEPTH_TEST);
  try
  {
   gl.glEnable(GL10.GL_TEXTURE_2D);
   prepareResource(gl);
  }
  catch(IOException e)
  {
   e.printStackTrace();
  }
 }
 void MakeXYTable()
 {
  int i,j;
  for(i=0;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if(i%2==0)
    {
     XYTable[i*NUM_BOARD_COL+j].x=VIEW_LEFT+BALL_PIXELRADIUS+BALL_PIXELDIAMETER*j;
     XYTable[i*NUM_BOARD_COL+j].y=VIEW_TOP-BALL_PIXELRADIUS-(ROOT3/2)*(BALL_PIXELDIAMETER+1.2f)*i;
    }
    else
    {
     XYTable[i*NUM_BOARD_COL+j].x=VIEW_LEFT+BALL_PIXELDIAMETER+BALL_PIXELDIAMETER*j;
     XYTable[i*NUM_BOARD_COL+j].y=VIEW_TOP-BALL_PIXELRADIUS-(ROOT3/2)*(BALL_PIXELDIAMETER+1.2f)*i;
    }
   }
  }
 }
 //----------------------------------------------알고리즘---------//
 float Distance2D(float x1,float y1,float x2,float y2)
 {
     return (float)Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
 }

 class int_pair
 {
  public int row,col;
 };
 int_pair GetNearestBB(float x,float y,int Base_row,int Base_col)
 {
  int k;
  int minIndex_Circle=0;
  float minD_Circle=99999;//충분히 큰수
  float curD_Circle;
  float x1,y1;
  int EvenOdd=Base_row%2;
  int row,col;
  for(k=0;k<6;k++)
  {
   row=Base_row+CircleTable[EvenOdd*6+k].y;
   col=Base_col+CircleTable[EvenOdd*6+k].x;
   //홀수줄(1,3,5,...)에서 마지막 열은 사용안한다.
   int EvenOdd_me=row%2;
   if((EvenOdd_me==1)&&(col>=(NUM_BOARD_COL-1)))
    continue;
  
   if(row<0 || row>=NUM_BOARD_ROW) continue;
   if(col<0 || col>=NUM_BOARD_COL) continue;
   x1=XYTable[row*NUM_BOARD_COL+col].x;
   y1=XYTable[row*NUM_BOARD_COL+col].y;
   curD_Circle=Distance2D(x1,y1,x,y);
   if(curD_Circle<minD_Circle)
   {
    if(BB[row*NUM_BOARD_COL+col].bExist==false)
    {
     minD_Circle=curD_Circle;
     minIndex_Circle=k;
    }
   }
  }
 
  //충돌한 곳의 BB어레이상의 열,행 위치
  int_pair ip=new int_pair();
  ip.row=Base_row+CircleTable[EvenOdd*6+minIndex_Circle].y;
  ip.col=Base_col+CircleTable[EvenOdd*6+minIndex_Circle].x;
  return ip;
 }
 int MakeNSB(NearSameBall[] pNSB,int SRC_row,int SRC_col,byte SRC_BallType)
 {
  int i,j,k;
  int s;
  int EvenOdd;
  int row,col;
  IntegerPoint[] SameTypeBall=new IntegerPoint[NUM_MAX_BALL];
  for(i=0;i<NUM_MAX_BALL;i++)
   SameTypeBall[i]=new IntegerPoint();
  int STBIndex=0;
  for(i=0;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if((BB[i*NUM_BOARD_COL+j].bExist==true)&&
      (BB[i*NUM_BOARD_COL+j].BallType==SRC_BallType))
    {
     SameTypeBall[STBIndex].x=j;
     SameTypeBall[STBIndex].y=i;
     STBIndex++;
    }
   }
  }
  NearSameBall[] NSB=pNSB;
  int NSBIndex=0;
  int nPrevCSB=0,nCurCSB=1;
  int PrevCSBIndex=0;
  NSB[NSBIndex].row=SRC_row;
  NSB[NSBIndex].col=SRC_col;
  NSBIndex++;
  while(true)
  {
   nPrevCSB=nCurCSB;
   nCurCSB=0;
   for(i=0;i<nPrevCSB;i++)
   {
    EvenOdd=NSB[PrevCSBIndex+i].row%2;
    for(j=0;j<6;j++)
    {
     row=NSB[PrevCSBIndex+i].row+CircleTable[EvenOdd*6+j].y;
     col=NSB[PrevCSBIndex+i].col+CircleTable[EvenOdd*6+j].x;
     for(k=0;k<STBIndex;k++)
     {
      if((SameTypeBall[k].x==col)&&(SameTypeBall[k].y==row))
      {
       for(s=0;s<NSBIndex;s++)
       {
        if((NSB[s].col==col)&&(NSB[s].row==row))
         break;
       }
       if(s==NSBIndex)
       {
        NSB[NSBIndex].row=row;
        NSB[NSBIndex].col=col;
        NSBIndex++;
        nCurCSB++;
       }
       break;
      }
     }
    }
   }
   if(nCurCSB==0)
    break;
   PrevCSBIndex=NSBIndex-nCurCSB;
  }
  return NSBIndex;
 }
 void ShootSB(float v_x,float v_y)
 {
  if(!AddAB_SB(v_x,v_y))
  {
   CS.S.Cur_x=CUR_BALL_X;
   CS.S.Cur_y=CUR_BALL_Y;
   return;
  }
  CS.S.bShotEnable=false;
  CS.ShotTime=10-(nCurStage/(float)Game.nStage)*9;
  CS.S.Cur_BallType=CS.S.Next_BallType;
  CS.S.Cur_AniIndex=14;
  CS.S.Cur_AniStage=AS_NONE;
  CS.S.Cur_x=NEXT_BALL_X;
  CS.S.Cur_y=CUR_BALL_Y;
  CS.S.Next_BallType=RandomBallType();
  CS.S.Next_AniIndex=0;
  CS.S.Next_AniStage=AS_FLOAT;
  snd_sink.start();
 }

 byte RandomBallType()
 {
  byte nBallType=NUM_BALL_TYPE;
  if(nCurStage==1) nBallType=1;
  else if(nCurStage==2) nBallType=2;
  else if(nCurStage==3) nBallType=3;
  else if(nCurStage==4) nBallType=4;
  else if(nCurStage==5) nBallType=5;
  else if(nCurStage==6) nBallType=6;
  else if(nCurStage==7) nBallType=7;
  else if(nCurStage==8) nBallType=8;
  else nBallType=9;
  return (byte)((Math.random()*100000)%(nBallType));
 }
 //시작------------------공들사이의 충돌처리--------------------------//
 void AB_AB_Collision(int i)
 {
  int j;
  float Angle;
  float Vecx,Vecy;
  float S;//공의 속력
  float S_Collision;//충돌시 영향을 미치는 성분의 크기
  float D;//공사이의 거리
  for(j=0;j<nABTail;j++)
  {
   if(AB[j].bExist==1)
   {
    if(i==j)
     continue;
    D=Distance2D(AB[j].x,AB[j].y,AB[i].x,AB[i].y);
    if(D<=D_COLLISION)
    {
     if(AB[i].y>0)
      snd_glass.start();
    
     Vecx=(AB[j].x-AB[i].x)/D;
     Vecy=(AB[j].y-AB[i].y)/D;
    
     //위치보정
     float penet_depth=D_COLLISION-D;
     //+0.3 : prevents collision checking twice
     AB[i].x-=Vecx*(penet_depth+0.3);
     AB[i].y-=Vecy*(penet_depth+0.3);
    
     S=(float)Math.sqrt((AB[i].vx*AB[i].vx)+(AB[i].vy*AB[i].vy));
     Angle=(float)(Math.atan2(Vecy,Vecx)-Math.atan2(AB[i].vy,AB[i].vx));
    
     S_Collision=(float)(S*Math.cos(Angle));
     if(S_Collision<=0)
      continue;
     //snd_metal.start();
     Vecx*=S_Collision;
     Vecy*=S_Collision;
     AB[i].vx-=(Vecx);
     AB[i].vy-=(Vecy);
     AB[j].vx+=(Vecx);
     AB[j].vy+=(Vecy);
    }
   }
  }
 }
 void AB_BB_Collision(int i)
 {
  int j,k;
  float Angle,Vecx,Vecy;
  float S;
  float S_Collision;
  float D;
  for(j=0;j<NUM_BOARD_ROW;j++)
  {
   for(k=0;k<NUM_BOARD_COL;k++)
   {
    if(BB[j*NUM_BOARD_COL+k].bExist==true)
    {
     D=Distance2D(XYTable[j*NUM_BOARD_COL+k].x,XYTable[j*NUM_BOARD_COL+k].y,AB[i].x,AB[i].y);
     //충돌이 이루어 졌다면...
     if(D<=D_COLLISION)
     {
      if(AB[i].y>0)
       snd_glass.start();
     
      NearSameBall[] NSB=new NearSameBall[NUM_MAX_BALL];
      for(int s=0;s<NUM_MAX_BALL;s++)
       NSB[s]=new NearSameBall();
      int nNSB=0;
      int row,col;
     
      Vecx=(XYTable[j*NUM_BOARD_COL+k].x-AB[i].x)/D;
      Vecy=(XYTable[j*NUM_BOARD_COL+k].y-AB[i].y)/D;
     
      //위치보정
      float penet_depth=D_COLLISION-D;
      AB[i].x-=Vecx*penet_depth;
      AB[i].y-=Vecy*penet_depth;
      //SB의 속력
      S=(float)Math.sqrt((AB[i].vx*AB[i].vx)+(AB[i].vy*AB[i].vy));
      Angle=(float)(Math.atan2(Vecy,Vecx)-Math.atan2(AB[i].vy,AB[i].vx));
      S_Collision=(float)(S*Math.cos(Angle));
      if(S_Collision<=0)
       continue;
      //충돌된 AB[i]와 BB[j*NUM_BOARD_COL+k]의 볼 타입이 다른경우 처리
      if(AB[i].BallType!=BB[j*NUM_BOARD_COL+k].BallType)
      {
       if(AB[i].Flag==ABF_FALLBALL)
       {
        Vecx*=S_Collision;
        Vecy*=S_Collision;
        AB[i].vx-=(2*Vecx);
        AB[i].vy-=(2*Vecy);
        return;
       }
       else if(AB[i].Flag==ABF_SHOOTBALL)
       {
        if(S_Collision>=MAX_BONDING_SPEED)
        {
         Vecx*=S_Collision;
         Vecy*=S_Collision;
         AB[i].vx-=(2*Vecx);
         AB[i].vy-=(2*Vecy);
         return;
        }
       }
      }
      AB[i].bExist=0;
      nAB--;
      int_pair ip=GetNearestBB(AB[i].x,AB[i].y,j,k);
      row=ip.row;
      col=ip.col;
      nNSB=MakeNSB(NSB,row,col,AB[i].BallType);
      //충돌속도가 충분히 크면서 같은색 근처공이 3개 이상이면 AB에 추가한다
      //또는 충돌속도가 만족되지않아도 같은색 근처공이 5개이상이면 AB에 추가한다
      if((S_Collision>=MAX_BONDING_SPEED_NSB && nNSB>=3) || nNSB>=10 ||
        (AB[i].Flag==ABF_FALLBALL && nNSB>=3))
      {
       //AB에 추가
       AddAB_NSB(NSB,nNSB,AB[i].BallType);
       AddAB_FB();
      }
      else
      {
       //BB에 AB[i]추가
       BB[row*NUM_BOARD_COL+col].bExist=true;
       BB[row*NUM_BOARD_COL+col].BallType=AB[i].BallType;
       BB[row*NUM_BOARD_COL+col].AniStage=AS_NONE;
       BB[row*NUM_BOARD_COL+col].AniIndex=14;
       BB[row*NUM_BOARD_COL+col].bProof=1;
       nBB++;
      }
      return;
     }
    }
   }
  }
  //}
 }
 //끝------------------공들사이의 충돌처리--------------------------//
 //AB가 존재하면 각각의 AB를 처리한다.
 //중력적용,움직이기,천정 벽 의 충돌처리,콘솔밑으로 떨어졌을때 안보이기
 //나이가 생명시간보다 커지면 없애기
 //AB끼리의 충돌처리
 //BB와의 충돌처리에서 연쇄 충돌효과 일으키기...
 void ProcessAB()
 {
  int i;
  for(i=0;i<nABTail;i++)
  {
   if(AB[i].bExist==0)
    continue;
   //AB[i]의 나이가 그공의 생명시간보다 커지면 서서히 없어진다.
   //{
   AB[i].Age+=frametime;
   if(AB[i].Age>=BPTable[AB[i].BallType].LifeTime)
   {
    //
    AB[i].AniIndex-=SPEED_ANIMATION*frametime;
    if(AB[i].AniIndex<0)
    {
     AB[i].bExist=0;
     nAB--;
     continue;
    }
   }
   //}
   //중력적용
   AB[i].vy-=BPTable[AB[i].BallType].g*frametime;
   //AB 움직이기
   AB[i].x+=AB[i].vx*frametime;
   AB[i].y+=AB[i].vy*frametime;
   //AB[i] 벽 충돌처리
   //{
   if((AB[i].x<=VIEW_LEFT+BALL_PIXELRADIUS)&&(AB[i].vx<0))
   {
    AB[i].vx=-AB[i].vx;
   }
   else if((AB[i].x>=(VIEW_LEFT+VIEW_WIDTH-BALL_PIXELRADIUS))&&(AB[i].vx>0))
   {
    AB[i].vx=-AB[i].vx;
   }
   //}
   //AB 끼리의 충돌처리
   //{
   AB_AB_Collision(i);
   //}
   //AB[i] 와 BB어레이 와의 충돌처리
   //{
   AB_BB_Collision(i);
   //}
   //AB[i]가 콘솔 밑으로 떨어졌을때...
   //{
   if((AB[i].y<=(VIEW_TOP-VIEW_HEIGHT-BALL_PIXELRADIUS))&&(AB[i].bVisible==1))
   {
    if(AB[i].Flag==ABF_FALLBALL)
    {
     CS.Score++;
    }
    else if(AB[i].Flag==ABF_SHOOTBALL)
    {
     snd_nsb.start();
     AddBB();
    }
    //강제로 제거하도록 나이값을 크게 설정
    AB[i].Age=1000;
    AB[i].bVisible=0;
   }
   //}
   //AB[i]와 천장과의 충돌처리...
   //{
   if((AB[i].y>=(VIEW_TOP-BALL_PIXELRADIUS))&&(AB[i].bVisible==1))
   {
    AB[i].y=(VIEW_TOP-BALL_PIXELRADIUS);
    AB[i].vy=-AB[i].vy;
   }
   //}
  }
 }
 void ProcessBB()
 {
  int i,j;
  //BB의 에니메이션 처리
  for(i=0;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if(BB[i*NUM_BOARD_COL+j].bExist==true)
    {
     if(BB[i*NUM_BOARD_COL+j].AniStage==AS_FLOAT)
     {
      BB[i*NUM_BOARD_COL+j].AniIndex+=SPEED_ANIMATION*frametime;
      if(BB[i*NUM_BOARD_COL+j].AniIndex>=14)
      {
       BB[i*NUM_BOARD_COL+j].AniIndex=14;
       BB[i*NUM_BOARD_COL+j].AniStage=AS_NONE;
      }
     }
    }
   }
  }
  for(i=0;i<NUM_BOARD_COL;i++)
  {
   if(BB[10*NUM_BOARD_COL+i].bExist==true)
   {
    snd_stageend.start();
    SystemClock.sleep(2500);
    LoadStage(1);
    break;
   }
  }
 }
 void ProcessCS()
 {
  //에니메이션 처리
  //{
  if(!CS.S.bShotEnable && CS.S.Cur_x!=CUR_BALL_X)
  {
   CS.S.Cur_x-=SPEED_MOVE_NEXT*frametime;
   if(CS.S.Cur_x<=CUR_BALL_X)
   {
    CS.S.Cur_x=CUR_BALL_X;
    CS.S.bShotEnable=true;
   }
  }
  //else
  {
   if(CS.S.Next_AniStage==AS_FLOAT)
   {
    CS.S.Next_AniIndex+=SPEED_ANIMATION*frametime;
    if(CS.S.Next_AniIndex>=14)
    {
     CS.S.Next_AniIndex=14;
     CS.S.Next_AniStage=AS_NONE;
    }
   }
   else if(CS.S.Next_AniStage==AS_SINK)
   {
    CS.S.Next_AniIndex-=SPEED_ANIMATION*frametime;
    if(CS.S.Next_AniIndex<=0)
    {
     CS.S.Next_AniIndex=0;
     CS.S.Next_AniStage=AS_NONE;
    }
   }
  }
  //}
  if((CS.S.bShotEnable==true)&&(CS.ShotTime<=0))
  {
   ShootSB((float)Math.random()*1600.0f-800,800.0f);
  }
 }
 void AddBB()
 {
  int i;
  int row,col;
  int add_row,add_col;
  int EvenOdd;
  while(true)
  {
   add_row=(int)((Math.random()*100000)%NUM_BOARD_ROW);
   EvenOdd=add_row%2;
   add_col = (int)((EvenOdd==0) ? ((Math.random()*100000)%
    NUM_BOARD_COL) : ((Math.random()*100000)%NUM_BOARD_COL-1));
   if(BB[add_row*NUM_BOARD_COL+add_col].bExist==false)
   {
    for(i=0;i<6;i++)
    {
     row=add_row+CircleTable[EvenOdd*6+i].y;
     col=add_col+CircleTable[EvenOdd*6+i].x;
    
     int EvenOdd_me=row%2;
     if(EvenOdd_me==1 && (col>=NUM_BOARD_COL-1)) continue;
     if((row==-1)||(row==NUM_BOARD_ROW)||(col==-1)||(col==NUM_BOARD_COL))
      continue;
     if(BB[row*NUM_BOARD_COL+col].bExist==true)
      break;
    }
    if((add_row==0) || (i!=6))
    {
     BB[add_row*NUM_BOARD_COL+add_col].bExist=true;
     BB[add_row*NUM_BOARD_COL+add_col].AniIndex=0;
     BB[add_row*NUM_BOARD_COL+add_col].AniStage=AS_FLOAT;
     BB[add_row*NUM_BOARD_COL+add_col].BallType=RandomBallType();
     nBB++;
     return;
    }
   }
  }
 }
 void AddAB_FB()
 {
  int i,j,k;
  int s,t;
  int row,col;
  int EvenOdd;
  int ABIndex=0;
  //천정에서 떨어진 공들 AB에 추가
  //{
  //bProof 플래그 초기화
  for(i=0;i<NUM_BOARD_COL;i++)
   if(BB[0*NUM_BOARD_COL+i].bExist==true)
    BB[0*NUM_BOARD_COL+i].bProof=1;
  for(i=1;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if(BB[i*NUM_BOARD_COL+j].bExist==true)
    {
     BB[i*NUM_BOARD_COL+j].bProof=0;
    }
   }
  }
  //천정에서 두번째 줄부터 왼쪽 - 아래 로(한줄당 두번) 스캔하면서 천정과 닿았는지 검사 -- bProof 이용
  //다시 아래에서 위로 검사... -- 이렇게 6번해야 완벽하게 결과가 나옴...
  //t : 스캔 줄방향  i : 현재 줄  j : 스캔 열방향  k : 현재 열 s : CircleTable 에서 1-4까지
  int scan_col,scan_row;
  for(t=0;t<6;t++)
  {
   for(i=1;i<NUM_BOARD_ROW;i++)
   {
    scan_row = ((t%2)==0) ? i : (NUM_BOARD_ROW-1-i);
    EvenOdd=scan_row%2;
    for(j=0;j<2;j++)
    {
     for(k=0;k<NUM_BOARD_COL;k++)
     {
      scan_col = (j==0) ? k : (NUM_BOARD_COL-1-k);
      if(BB[scan_row*NUM_BOARD_COL+scan_col].bExist==true)
      {
       for(s=0;s<6;s++)
       {
        row=scan_row+CircleTable[EvenOdd*6+s].y;
        col=scan_col+CircleTable[EvenOdd*6+s].x;
       
        int EvenOdd_me=row%2;
        if(EvenOdd_me==1 && (col>=NUM_BOARD_COL-1)) continue;
        if((col<0)||(col>=NUM_BOARD_COL))
         continue;
        if((row<0)||(row>=NUM_BOARD_ROW))
         continue;
        if((BB[row*NUM_BOARD_COL+col].bExist==true)&&
         (BB[row*NUM_BOARD_COL+col].bProof==1))
        {
         BB[scan_row*NUM_BOARD_COL+scan_col].bProof=1;
         break;
        }
       }
      }
     }
    }
   }
  }
  //nABTail최소화
  if(nABTail!=0)
   while((nABTail!=0) && (AB[nABTail-1].bExist==0)){nABTail--;}
  while(AB[ABIndex].bExist==1){ABIndex++;}
  for(i=1;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if((BB[i*NUM_BOARD_COL+j].bExist==true)&&(BB[i*NUM_BOARD_COL+j].bProof==0))
    {
     AB[ABIndex].AniIndex=14;
     AB[ABIndex].AniStage=AS_NONE;
     AB[ABIndex].BallType=BB[i*NUM_BOARD_COL+j].BallType;
     AB[ABIndex].Flag=ABF_FALLBALL;
     AB[ABIndex].x=XYTable[i*NUM_BOARD_COL+j].x;
     AB[ABIndex].y=XYTable[i*NUM_BOARD_COL+j].y;
     AB[ABIndex].vx=500*((float)((Math.random()*100000)%100)/200);
     AB[ABIndex].vy=500*((float)((Math.random()*100000)%100)/200);
     AB[ABIndex].Age=0;
     AB[ABIndex].bVisible=1;
     AB[ABIndex].bExist=1;
     //BB어레이에서 삭제 -- AB로 이동
     BB[i*NUM_BOARD_COL+j].bExist=false;
     while(AB[ABIndex].bExist==1){ABIndex++;}
     nAB++;
     nBB--;
    }
   }
  }
  //}
  if(ABIndex>=nABTail){nABTail=(ABIndex+1);}
 }
 void AddAB_NSB(NearSameBall[] pNSB,int nBall,int BallType)
 {
  int i;
  int row,col;
  int ABIndex=0;
  //nABTail최소화
  if(nABTail!=0)
   while((nABTail!=0) && (AB[nABTail-1].bExist==0)){nABTail--;}
  //NSB를 AB에 추가
  //{
  nAB+=nBall;
  while(AB[ABIndex].bExist==1){ABIndex++;}
  for(i=0;i<nBall;i++)
  {
   row=((pNSB[i])).row;
   col=((pNSB[i])).col;
   AB[ABIndex].AniIndex=14;
   AB[ABIndex].AniStage=AS_NONE;
   AB[ABIndex].BallType=(byte)BallType;
   AB[ABIndex].Flag=ABF_FALLBALL;
   AB[ABIndex].x=XYTable[row*NUM_BOARD_COL+col].x;
   AB[ABIndex].y=XYTable[row*NUM_BOARD_COL+col].y;
   AB[ABIndex].vx=700*((float)((Math.random()*100000)%100)/200);
   AB[ABIndex].vy=700*((float)((Math.random()*100000)%100)/200);
   AB[ABIndex].Age=0;
   AB[ABIndex].bVisible=1;
   AB[ABIndex].bExist=1;
   //BB어레이에서 삭제 -- AB로 이동
   BB[row*NUM_BOARD_COL+col].bExist=false;
   while(AB[ABIndex].bExist==1){ABIndex++;}
  }
  if(ABIndex>=nABTail){nABTail=(ABIndex+1);}
  nBB-=(nBall-1);//1은 슛볼 또는 AB중 BB에 붙은공을 의미
  //}
 }
 boolean AddAB_SB(float v_x,float v_y)
 {
  double spd=Math.sqrt(v_x*v_x+v_y*v_y);
  if(spd==0.0) return false;
  int ABIndex=0;
  //nABTail최소화
  if(nABTail!=0)
   while((nABTail!=0) && (AB[nABTail-1].bExist==0)){nABTail--;}
  while(AB[ABIndex].bExist==1){ABIndex++;}
  AB[ABIndex].bExist=1;
  AB[ABIndex].AniIndex=CS.S.Cur_AniIndex;
  AB[ABIndex].AniStage=AS_NONE;
  AB[ABIndex].BallType=CS.S.Cur_BallType;
  AB[ABIndex].Flag=ABF_SHOOTBALL;
  AB[ABIndex].bVisible=1;
  AB[ABIndex].Age=0;
  float maxspeed=BPTable[AB[ABIndex].BallType].max_speed;
  double tgtspd=Math.min(maxspeed,spd);
  double mult=tgtspd/spd;
  float vx=(float)((double)v_x*mult);
  float vy=(float)((double)v_y*mult);
  AB[ABIndex].vx=(float)vx;
  AB[ABIndex].vy=(float)vy;
  AB[ABIndex].x=CS.S.Cur_x;
  AB[ABIndex].y=CS.S.Cur_y;
  nAB++;
  if(ABIndex>=nABTail){nABTail=(ABIndex+1);}
 
  return true;
 }
 void LoadGame()
 {
  int i;
  Game.nStage=30;
  Game.pStageFileName=new String[Game.nStage]; 
  for(i=0;i<Game.nStage;i++)
  {
   Game.pStageFileName[i]=new String("");
  }
 
  //Loads Fonts...
  //{
//  pDDSWhiteFont=DDLoadBitmap(pDD,"font_fixedsys_white.bmp",0,0,1);
//  DDSetColorKey(pDDSWhiteFont,RGB(0,0,0));
//
//  pDDSRedFont=DDLoadBitmap(pDD,"font_fixedsys_red.bmp",0,0,1);
//  DDSetColorKey(pDDSRedFont,RGB(255,255,255));
//
//  pDDSBlueFont=DDLoadBitmap(pDD,"font_fixedsys_blue.bmp",0,0,0);
//  DDSetColorKey(pDDSBlueFont,RGB(255,255,255));
  //}
  Option.bSound=1;
  Option.Level=0;
 
  LoadStage(1);
 }
 int LoadStage(int stage)
 {
  int i,j;
  nCurStage=stage;
  CurStage.nBall=(byte)stage;
  nBB=0;
  //Loads Total Ball Data to BB - BoardBall Array
  //{
  for(i=0;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    BB[i*NUM_BOARD_COL+j].bExist=false;
    BB[i*NUM_BOARD_COL+j].AniIndex=14;
    BB[i*NUM_BOARD_COL+j].AniStage=1;
   }
  }
  for(i=0;i<CurStage.nBall;i++)
  {
   AddBB();
  }
  //}
  for(i=0;i<NUM_MAX_BALL;i++)
  {
   AB[i].bExist=0;
  }
  CS.Score=0;
  CS.Time=0;
  CS.S.bShotEnable=false;
  CS.ShotTime=10-(nCurStage/(float)Game.nStage)*9;
  CS.S.Cur_AniIndex=14;
  CS.S.Cur_BallType=RandomBallType();
  CS.S.Cur_x=NEXT_BALL_X;
  CS.S.Cur_y=CUR_BALL_Y;
  CS.S.Next_AniIndex=14;
  CS.S.Next_BallType=RandomBallType();
  nAB=0;
  nABTail=0;
 
  snd_stagestart.start();
  return 1;
 }
 void ReleaseStage(int stage)
 {
  int i,j;
  //BB초기화
  for(i=0;i<NUM_BOARD_ROW;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    BB[i*NUM_BOARD_COL+j].bExist=false;
    BB[i*NUM_BOARD_COL+j].AniIndex=14;
    BB[i*NUM_BOARD_COL+j].AniStage=1;
   }
  }
 }

 void DrawBB(GL10 gl)
 {
  int i,j;
  for(i=0;i<NUM_BOARD_ROW-1;i++)
  {
   for(j=0;j<NUM_BOARD_COL;j++)
   {
    if(BB[i*NUM_BOARD_COL+j].bExist==true)
    {
     draw_ball(gl,(int)XYTable[i*NUM_BOARD_COL+j].x,(int)XYTable[i*NUM_BOARD_COL+j].y,
      (int)BB[i*NUM_BOARD_COL+j].BallType,(int)BB[i*NUM_BOARD_COL+j].AniIndex);
    }
   }
  }
  for(i=0;i<NUM_BOARD_COL;i++)
  {
   if(BB[(NUM_BOARD_ROW-1)*NUM_BOARD_COL+i].bExist==true)
   {   
    draw_ball(gl,(int)XYTable[(NUM_BOARD_ROW-1)*NUM_BOARD_COL+i].x,
    (int)XYTable[(NUM_BOARD_ROW-1)*NUM_BOARD_COL+i].y,
    (int)BB[(NUM_BOARD_ROW-1)*NUM_BOARD_COL+i].BallType,
    (int)BB[(NUM_BOARD_ROW-1)*NUM_BOARD_COL+i].AniIndex);
   }
  }
 }
 void DrawAB(GL10 gl)
 {
  int i;
  for(i=0;i<nABTail;i++)
  {
   if((AB[i].bVisible==1)&&(AB[i].bExist==1))
   {
    draw_ball(gl,(int)AB[i].x,(int)AB[i].y,
     (int)AB[i].BallType,(int)AB[i].AniIndex);
   }
  }
 }
 void DrawConsole(GL10 gl)
 {
  //next ball
  draw_ball(gl,(int)NEXT_BALL_X,(int)NEXT_BALL_Y,
   (int)CS.S.Next_BallType,(int)CS.S.Next_AniIndex);
  //current ball
  draw_ball(gl,(int)CS.S.Cur_x,(int)CS.S.Cur_y,
   (int)CS.S.Cur_BallType,(int)CS.S.Cur_AniIndex);
  //arrow
  //time shot
//  if(CS.S.bShotEnable==true)
//  {
//   ZeroMemory(szTemp,sizeof(szTemp));
//   sprintf(szTemp,"%d",CS.ShotTime);
//   if((CS.ShotTime)>=(int)(TIME_SHOT*0.3f))
//    DrawText(pDDSBuffer,pDDSBlueFont,szTemp,CUR_BALL_X-12,CUR_BALL_Y-8);
//   else
//    DrawText(pDDSBuffer,pDDSRedFont,szTemp,CUR_BALL_X-12,CUR_BALL_Y-8);
//  }
  //score
//  ZeroMemory(szTemp,sizeof(szTemp));
//  sprintf(szTemp,"Score:%4d",CS.Score);
//  DrawText(pDDSBuffer,pDDSWhiteFont,szTemp,SCORE_LEFT,SCORE_TOP);
// 
//  //stage number
//  ZeroMemory(szTemp,sizeof(szTemp));
//  sprintf(szTemp,"Time:%4d",CS.Time);
//  DrawText(pDDSBuffer,pDDSWhiteFont,szTemp,TIME_LEFT,TIME_TOP);
//
//  //limit line
//  pDDSBuffer->GetDC(&hdc);
//  hpe=CreatePen(PS_SOLID,1,RGB(255,255,255));
//  holdpe=(HPEN)SelectObject(hdc,hpe);
//  MoveToEx(hdc,VIEW_LEFT,VIEW_TOP+VIEW_HEIGHT-40,NULL);
//  LineTo(hdc,VIEW_LEFT+VIEW_WIDTH,VIEW_TOP+VIEW_HEIGHT-40);
//  SelectObject(hdc,holdpe);
//  DeleteObject(hpe);
//  pDDSBuffer->ReleaseDC(hdc);
 }
 long t2;
 void PlayMain(GL10 gl)
 {
  long t1=SystemClock.elapsedRealtime();
  frametime=(t1-t2)/1000.0f;
  if(frametime>0.1f)
   frametime=0.1f;
  t2=t1;
 
  //process
  ProcessCS();
  ProcessBB();
  ProcessAB();
 
  if((nBB<=0)&&(nAB<=0))
  {
   snd_stageclear.start();
   SystemClock.sleep(2500);
   ++nCurStage;
   if(nCurStage>=Game.nStage)
   {
    snd_stageclear.start();
    SystemClock.sleep(2500);
    snd_stageclear.start();
    SystemClock.sleep(2500);
    snd_stageclear.start();
    SystemClock.sleep(2500);
    nCurStage=1;
   }
   LoadStage(nCurStage);
  }
  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  gl.glActiveTexture(GL10.GL_TEXTURE0);
  gl.glBindTexture(GL10.GL_TEXTURE_2D,balltex);
  //gl.glEnable(GL10.GL_ALPHA_TEST);
  //gl.glAlphaFunc(GL10.GL_GREATER,0);
  gl.glEnable(GL10.GL_BLEND);
  gl.glBlendFunc(GL10.GL_ONE,GL10.GL_ONE);
 
  //draw
  DrawBB(gl);
  DrawAB(gl);
  DrawConsole(gl);
 }

 void DrawText(int font_tex,String sztext,int x,int y)
 {
//  char index;
//  RECT rect1,rect2;
//  int StringIndex=0,start_x=x;
//
//  while(*(sztext+StringIndex)!=0)
//  {
//   if(*(sztext+StringIndex)==CR)
//   {
//    y+=14;
//    StringIndex+=2;
//    x=start_x;
//    continue;
//   }
//
//   if(*(sztext+StringIndex)==' ')
//   {
//    x+=8;
//    StringIndex++;
//    continue;
//   }
//
//   rect1.left=x;
//   x+=8;
//   rect1.top=y;
//   rect1.right=(rect1.left)+8;
//   rect1.bottom=(rect1.top)+14;
//   index=*(sztext+StringIndex)-33;
//   rect2.top=0;
//   rect2.bottom=14;
//   rect2.left=index*8;
//   rect2.right=(rect2.left)+8;
//   pDDSDest->Blt(&rect1,pDDSfont,&rect2,DDBLT_KEYSRC,NULL);
//   StringIndex++;
  //}
 }
 private VelocityTracker vTracker=null;
 private boolean Ball_Dragging=false;
 private float pick_diff_x=0,pick_diff_y=0;
 public boolean onTouch(View v,MotionEvent event)
 {
  int action=event.getAction();
  switch(action)
  {
  case MotionEvent.ACTION_DOWN:
   {
    if(vTracker==null) vTracker=VelocityTracker.obtain();
    else vTracker.clear();
    RECT r=new RECT();
    r.left=(int)(VIEW_WIDTH/2-BALL_PIXELRADIUS*2.3f);
    r.bottom=(int)(VIEW_HEIGHT-BALL_PIXELDIAMETER-BALL_PIXELRADIUS*2.3f);
    r.right=(int)(r.left+BALL_PIXELDIAMETER*2.3f);
    r.top=(int)(r.bottom+BALL_PIXELDIAMETER*2.3f);
    if(CS.S.bShotEnable && event.getX()>r.left && event.getX()<r.right &&
     event.getY()>r.bottom && event.getY()<r.top)
    {
     pick_diff_x=event.getX()-CS.S.Cur_x;
     pick_diff_y=(VIEW_HEIGHT-event.getY())-CS.S.Cur_y;
     Ball_Dragging=true;
    }
    break;
   }
  case MotionEvent.ACTION_MOVE:
   {
    vTracker.addMovement(event);
    vTracker.computeCurrentVelocity(1000);
    RECT r=new RECT();
    r.left=(int)(VIEW_WIDTH/2-BALL_PIXELRADIUS*2.3f);
    r.bottom=(int)(VIEW_HEIGHT-BALL_PIXELDIAMETER-BALL_PIXELRADIUS*2.3f);
    r.right=(int)(r.left+BALL_PIXELDIAMETER*2.3f);
    r.top=(int)(r.bottom+BALL_PIXELDIAMETER*2.3f);
    if(Ball_Dragging)
    {
     CS.S.Cur_x=event.getX()-pick_diff_x;
     CS.S.Cur_y=(VIEW_HEIGHT-event.getY())-pick_diff_y;
    }
    if(Ball_Dragging && !(event.getX()>r.left && event.getX()<r.right &&
     event.getY()>r.bottom && event.getY()<r.top))
    {
     ShootSB(vTracker.getXVelocity()*1.6f,-vTracker.getYVelocity()*1.6f);
     Ball_Dragging=false;
    }
    break;
   }
  case MotionEvent.ACTION_UP:
  case MotionEvent.ACTION_CANCEL:
   {
    if(Ball_Dragging)
    {
     ShootSB(vTracker.getXVelocity()*1.6f,-vTracker.getYVelocity()*1.6f);
     Ball_Dragging=false;
    }
    vTracker.recycle();
    vTracker=null;
    break;
   }
  }
  return true;
 }
}


package b.pb;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
public class DefaultActivity extends Activity
{
 public GLSurfaceView glview;
 public void onCreate(Bundle savedInstanceState)
 {
  try
  {
   super.onCreate(savedInstanceState);
   glview=new GLSurfaceView(this);
   glview.setEGLConfigChooser(false);
   glview.setRenderer(new AndroPB(this));
   glview.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
   getWindow().requestFeature(Window.FEATURE_NO_TITLE);
   setContentView(glview);
  }
  catch(Exception ex)
  {
   ex.printStackTrace();
   Log.v("",ex.toString());
  }
 }

 protected void onResume()
 {
  super.onResume();
  glview.onResume();
 }

 protected void onPause()
 {
  super.onPause();
  glview.onPause();
 }
}