Android遊戲之2D圓形碰撞

Android遊戲之2D圓形碰撞

當物體移動並相互發生作用.需進行碰撞撿測.若重疊便發生碰撞.常見碰撞算法是通過『圓形邊界球』.它是包圍著對象最小圓. 無需隨物體旋轉而旋轉.所以速度最快之算法但精度較差.它由『圓心』與『半徑』所組成.

public class CIRCLE2D{

中心位置

public VECTOR2D center;

半徑

public float radius;

購造圓形邊界球,輸入中心位置與半徑.

public CIRCLE2D(float x,float y,float radius){

this.center = new VECTOR2D(x,y);

this.radius = radius;

}

『圓』與『點』碰撞檢測.計算『圓心』與『點』之距離.若距離小於『圓半徑』則發生碰撞.

public boolean overlap(VECTOR2D point){

float distance = center.Dist(point);

if(distance < radius  )

return true;// 發生碰撞

else

return false;// 無發生碰撞

}

兩圓形碰撞檢測.計算兩圓心『距離』.若『距離』小於兩圓半徑之和則發生碰撞

public boolean overlap(CIRCLE2D circle){

float distance = center.Dist(circle.center);// 兩圓之距

if(distance <= radius + circle.radius)

return true;// 發生碰撞

else

return false;// 無發生碰撞

}

『圓形』與『矩形』碰撞算法最為複雜.需先計算『矩形』與『圓心』最近之點.然後計算『圓心』與『最近點』之距離.若此點在『圓』內則發生重疊.

public boolean overlap(RECT2D rect){

float closestX = center.x; // 最接近X

float closestY = center.y;// 最接近Y

if(center.x < rect.center.x – rect.width/2)

closestX = rect.center.x – rect.width/2;

else

if(center.x > rect.center.x + rect.width/2)

closestX = rect.center.x + rect.width/2;

if(center.y < rect.center.y – rect.height/2)

closestY = rect.center.y – rect.height/2;

else

if(center.y > rect.center.y + rect.height/2)

closestY = rect.center.y + rect.height/2;

if(center.Dist(closestX,closestY) < radius)

return true; // 『圓』內則發生碰撞

else

return false; // 無發生碰撞

}

}

 

Android遊戲之FPS

Android遊戲之FPS

遊戲性能最重要指標FPS『每秒渲染幀量』遊戲越流暢FPS越高.不過所有Android手機都限制FPS最高60幀.若遊戲流暢FPS要高於30幀.下面代碼實現幀計數器

public class FPS {

返回納秒級時鐘.一納秒是一秒十億分之一

static private long startTime = System.nanoTime();

幀計數初此為零

static private int   frames = 0;

計算幀個數並在LOG『日誌』輸出.在GLSurfaceView.onDrawFrame(GL10 gl)中調用

static public void logFrame(){

幀個數加一

++frames;

每1秒輸出一次

if(System.nanoTime() – startTime >= 1000000000){

在LOG『日誌』輸出

Log.d(“FPS”,String.valueOf(frames));

幀個數清零

frames = 0;

更新納秒級時鐘

startTime = System.nanoTime();

}

}

}

 

 

Android遊戲之2D矢量

Android遊戲之2D矢量

『矢量』Vector也稱『向量』它其實是『抽象量』.它可以有多種解析如『位置』『速度』『加速度』『方向』『距離』.因為用於2D遊戲開發定義2D『矢量』

public class VECTOR2D {

浮點數儲存2D矢量

public float x,y;

角度轉弧度

public static float DEGREES_TO_RADIANS = ((1.0f/180.0f)* (float)Math.PI);

弧度轉角度

public static float RADIANS_TO_DEGREES = ((1.0f/(float)Math.PI)*180.0f);

購造函式初此為0

public VECTOR2D(){

x=y=0;

}

購造函式並設定x與y

public VECTOR2D(float x,float y){

this.x = x;

this.y = y;

}

返回新2D『矢量』拷貝

public VECTOR2D Copy(){

VECTOR2D v;

v=new VECTOR2D(x,y);

return v;

}

重設2D矢量

public VECTOR2D set(VECTOR2D v){

this.x = v.x;

this.y = v.y;

return this;

}

2D矢量加法

public VECTOR2D Add(VECTOR2D v){

this.x = this.x + v.x;

this.y = this.y + v.y;

return this;

}

2D矢量減法

public VECTOR2D Sub(VECTOR2D v){

this.x = this.x – v.x;

this.y = this.y – v.y;

return this;

}

2D矢量乘法等於對矢量進行縮放.

public VECTOR2D Mul(float scalar){

this.x = this.x * scalar;

this.y = this.y * scalar;

return this;

}

計算2D矢量長度

public float Len(){

float len;

len = (float) Math.sqrt(xx+yy);

return len;

}

2D矢量單位化,長度為1

public VECTOR2D Normer(){

float len;

len = Len();

if(len != 0) {

x = x / len;

y = y / len;

}

return this;

}

計算2D矢量角度

public float Angle(){

float angle = 0;

angle = (float)Math.atan2(y,x)*RADIANS_TO_DEGREES;

if(angle<0)

angle += 360;

return angle;

}

計算兩2D矢量角度在(0~360)度之間

public float Angle(VECTOR2D v){

float angle = 0;

float distanceX,distanceY;

distanceX = this.x – v.x;

distanceY = this.y – v.y;

angle = (float)Math.atan2(distanceY,distanceX)*RADIANS_TO_DEGREES;

if(angle<0)

angle += 360;

return angle;

}

2D矢量繞原點旋轉, angle為旋轉角度

public VECTOR2D rotate(float angle){

float rad = angle * DEGREES_TO_RADIANS; // 角度轉弧度

float cos = (float) Math.cos(rad);

float sin = (float) Math.sin(rad);

float newX = this.x * cos – this.y * sin;

float newY = this.x * sin + this.y * cos;

this.x = newX;

this.y = newY;

return this;

}

計算兩個2D矢量之間距離

public float Dist(VECTOR2D v){

float distX = this.x – v.x;

float distY = this.y – v.y;

float dist = (float)Math.sqrt(distX*distX + distY * distY);

return dist;

}

計算兩2D矢量之間距離平方

public float DistSquared(VECTOR2D v){

float distX = this.x – v.x;

float distY = this.y – v.y;

float dist = distX*distX + distY * distY ;

return dist;

}

}

Android遊戲之2D相機

Android遊戲之2D相機

定義2D相機即設定『投影矩陣』與『視口』

『視口』用於控制輸出圖像尺寸與渲染幀緩存位置.OpenGL ES使用『視口』將投影到『近裁剪面』點座標變換為『幀緩存』像素座標

GL10.glViewport(int x,int y,int width,int height);

x和y座標為幀緩存區視口左上角.width和height為視口大小.單位為像素.幀緩存座標原點為屏幕左下角.通常將x和y設為0.width和height設為屏幕分辨率.即設為全屏.

gl.glViewport(0, 0, view.getWidth(), view.getHeight());

『投影矩陣』在2D相機中只使用『平行投影』.首先將當前矩陣設為『投影矩陣』

gl.glMatrixMode(GL10.GL_PROJECTION);

載入單位矩陣

gl.glLoadIdentity();

設置正交投影矩陣

GL10. glOrthof(int left,int right,int bottom,int top,int near,int far);

OpenGL ES座標系統正X軸指向右,正Y軸指向上.正Z軸指向我.left與right為X軸左右兩條邊.bottom與 top為Y軸上下兩條邊.near為近端裁剪面.far為遠端裁剪面.若設2D橫向相機左下角(0,0)右上角(480,320)視錐深度為2

gl.glOrthof(0,480,0,320,1, -1);

重設為模型視圖矩陣

gl.glMatrixMode(GL10.GL_MODELVIEW);

gl.glLoadIdentity();

2D相機將『觸屏』坐標轉『世界』坐標.首先設定視錐體寬為15高為10.

float frustum_width = 15;

float frustum_height = 10;

設縮放係數為1.用於放大屏幕大於1,縮小屏幕小於1

float zoom = 1;

首先將『觸屏』位置歸範圍(0~1).然後乘以視錐體寬和高

touch.x = (touch.x / view.getWidth()) * frustum_width * zoom;

因為觸屏座標原點位於屏幕左上角.需倒轉Y軸

touch.y = (1-touch.y /view.getHeight()) * frustum_height * zoom;

與相機位置相機

touch.add(position);

最後減於視錐體中心位置.得到遊戲『世界』坐標

touch.x = touch.x – frustum_width*zoom/2f;

touch.y = touch.y – frustum_height*zoom/2f;

 

Android遊戲之OpenGL ES

Android遊戲之OpenGL ES

OpenGL ES專為移動與嵌入設備設計之圖形編程接口. 它並非由ARB委員會定義.而是由Khronos組織定義.該組織包括ATI、NVIDIA、Intel等組成.雖然『OpenGL』與『OpenGL ES』來自兩個不同組織. 『OpenGL ES』係『OpenGL』刪減板.但你依然可寫出在兩種標準下都可運行之程式.這對於移植遊戲很重要.OpenGL ES同樣以C文檔發佈API.而代碼實現由硬件廠商完成.

如果AndroidManifest.xml中無配置glEsVersion,則表示支持OpenGL ES 1.0但所有Android系統都支持.若支持OpenGL ES 2.0需在AndroidManifest.xml中添加:

<uses-feature  android:required=”false”  android:glEsVersion=”0x00020000″ />

OpenGL ES版本號為32位整數.高16位表示OpenGL ES大版本,低16位表示OpenGL ES小版本.

required必須 簡介
true 必需支持否則不能運行
false 可選支持可能影響部分運行

 

OpenGL ES版本 glEsVersion
GLES 1.0 0x00010000
GLES 1.1 0x00010001
GLES 2.0 0x00020000
GLES 3.0 0x00030000
GLES 3.1 0x00030001
GLES 3.2 0x00030002

 

Android遊戲之紋理映射

Android遊戲之紋理映射

將『Bitmap』加載給OpenGL ES然後加載到圖形卡記憶體重,並最終交給GPU渲染.因為OpenGL最終以三角形為單位進行渲染.而將『Bitmap』映射到三角形上.需要為三角形每個頂點設定紋理座標(texture coordinates).3D座標(x,y,z)和2D座標(x,y)對應紋理座標(u,v)位圖頂點.而(u,v)座標相當於(橫,縱)座標.紋理寬與高必需是2倍數.

紋理映射 (u,v)
左上角 (0,0)
右下角 (1,1)即使寬與高不相等
右上角 (1.0)
左下角 (0,1)

生成紋理隊列,用於保存紋理ID

int[] ids = new int[1];

生成紋理並獲取id.紋理依然為空

gl.glGenTextures(1, ids,0);

紋理id

int id = ids[0];

讀取位圖

Bitmap bitmap = BITMAP.Read(file_name);

獲取位圖寬度

int   width = bitmap.getWidth();

獲取位圖高度

int   height = bitmap.getHeight();

綁定2D紋理

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

上傳2D紋理

GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,bitmap,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);

 

過濾 簡介
GL10.GL_NEAREST 像素採樣點最接近中心點『清晰』
GL10.GL_LINEAR 像素採樣點線性插值平均加權『模糊』

取消綁定紋理ID

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

回收位圖.以免浪費記憶體

bitmap.recycle();

每當Activity被暫停(APP切換)GLSurfaceView都會被銷毀.每當Activity恢復時調用GLSurfaceView. onResume()讓GLSurfaceView重構渲染界面.但OpenGL ES中上傳紋理都會丟失.你需要在onSurfaceCreated()中需重新載入並上傳紋理.

重新載入代碼見上文

Texture.load(filen_ame);

重新綁定紋理後設置紋理屬性

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

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

取消綁定紋理

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

 

當遊戲退出.便要刪除紋理釋放記憶體.首先取消紋理邦定

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

刪除紋理

int[] ids = { id };

gl.glDeleteTextures(1,ids,0);

渲染三角形時要告知OpenGL要啟用紋理渲染

gl.glEnable(GL10.GL_TEXTURE_2D);

完成渲染後禁用紋理渲染

gl.glDisable(GL10.GL_TEXTURE_2D);

2D遊戲通常只佔用圖像一小片區域.需要將像素座標(x,y)轉換為紋理座標(u,v)

計算紋理座標左上角

u1 = x/texture.width;

v1 = y/texture.height;

計算紋理座標右下角

u2 = u1 + width/texture.width;

v2 = v1 + height/texture.height;

 

 

 

Android遊戲之Bitmap讀取

Android遊戲之Bitmap讀取

遊戲由『背景』與『角色』『植物』『房舍』等圖檔組成.在C時代要逐個寫圖檔分析器.而在java可以通過BitmapFactory讀取Bitmap『位圖』.幾乎支持所有常見圖檔『jpg』『png』『bmp』『png』.把所有『位圖』保存到『ASSETS』目錄下

資源管理器用於訪問『ASSETS』目錄

AssetManager asset_manager = context.getAssets();

指定『位圖名』並返回輸入流

InputStream input_stream = asset_manager.open(file_name);

讀取Bitmap默認轉換為RGB_565色

Bitmap bitamp = BitmapFactory.decodeStream(input_stream);

關閉輸入流

Input_Stream.close();

獲取位圖寬度

int width = bitmap.getWidth();

讀取位圖寬度

int height = bitmap.getHeight();

獲取位圖顏色格式.

Bitmap.Config config = bitamp.getConfig();

Bitmap.Config 位圖顏色格式
ALPHA_8 256色
ARGB_8888 32bit含ALPHA分量
RGB_565 16bit(默認)
ARGB_4444 16bit含透明度分量

以特定顏色格式進行讀取.但渲染時最終要與OpenGL ES顏色格式一致

設定顏色格式為ARGB_8888

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Bitmap.Config.ARGB_8888;

指定『位圖名』並返回輸入流

InputStream Input_Stream = asset_manager.open(file_name);

讀取Bitmap並轉換為ARGB_8888色

Bitmap bitamp = BitmapFactory.decodeStream(Input_Stream, null, options);

關閉輸入流

Input_Stream.close();

Android Studio之Error running ‘app’ No target device found

Android Studio之Error running ‘app’ No target device found

剛啟動Android Studio運行app進行調試『Run->Debug』時彈出:

『Error running ‘app’ No target device found』運行app『應用』錯誤穩唔到目標設備.即穩唔到手機.之前已安裝『ADB 驅動』.肯定是ADB服務未啟動所以先未穩到部手機.

啟用ADB服務可按

『Run->Attach debuger to Android process』

或在CMD輸入

adb start-server

 

Android遊戲之全屏

Android遊戲之全屏

自『街機遊戲』到『電腦遊戲』所有遊戲都以全屏形態出現.所以Android遊戲也應全屏.在super.onCreate()調用之前設定全屏

去除APP標題欄

this.requestWindowFeature(Window.FEATURE_NO_TITLE);

去除系通通知欄

Window window = this.getWindow(); window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

創建Activity.需要

super.onCreate(savedInstanceState);

把圖形界面『佈局』填充給Activity

setContentView(R.layout.main);

 

Android遊戲之SharedPreferences『共享參數』

Android遊戲之SharedPreferences『共享參數』

遊戲運行中經常要將數據保在磁盤中.等有需要時讀取.Android提供輕量級存儲工具SharedPreferences它適用於小量數據保存.

獲得默認共享首選項

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

獲得編輯器

Editor editor = sp.edit();

寫入文本.第一參數key文本.第二參數『value』文本

editor.putString(“text”,”data”);

寫入整數數值. 第一參數是key值.第二參數是整數

editor.putInt(“int”,1);

寫入浮點數. 第一參數key值.第二參數是浮點數

editor.putFloat(“float”,0.1);

提交保存數據

editor.commit();

讀取文本.若無該值則返回NULL

String value = sp.getString(“text”,null);

讀取整數.若無該值返回0

int value = sp.getInt(“int”,0);

讀取浮點數.若無該值返回0

float value = sp.getFloat(“float”,0);