WordPress之啟用Jetpack後控制臺載入慢

WordPress之啟用Jetpack後控制臺載入慢

近日Wordpress進入控制臺極慢.經常出現:

『正在連線到www.gstatic.com…』

但Android手機無此問題.此現像是更新Jetpack後出現. Jetpack版本是6.3.3解卻方法有二.禁用Jetpack或使用舊版.

  1. 手工下載Jetpack 6.3.2
  2. 手工刪除Jetpack 6.3.3
  3. 安裝外掛->上傳外掛->『指定路徑』->立即安裝
  4. 啟用外掛.使用com帳號登入

Android遊戲之紋理區域

Android遊戲之紋理區域

『紋理區域』指『紋理』中『矩形區域』.2D紋理通常尺寸較小.可以將多個2D紋理放在單一個紋理圖中.以提升OpenGL ES渲染性能.將其像素座標到紋理座標轉進行封裝.u1,v1為紋理座標左上角.u2,v2為紋理座標右下角.以確定紋理區域.值範圍0~1

public class REGION {

public TEXTURE texture;// 紋理

public float u1,v1,u2,v2;// 紋理坐標

像素坐標轉紋理坐標.輸入『像素』左上角與『寬和高』

public REGION(TEXTURE texture,float x,float y,float width,float height){

u1 = x/(float)texture.width;

v1 = y/(float)texture.height;

u2 = u1 + width/(float)texture.width;

v2 = v1 + height/(float)texture.height;

this.texture = texture;

}

}

Android遊戲之精靈批處理

Android遊戲之精靈批處理

OpenGL ES盡可能每次渲染多個精靈.以提高渲染性能.為此需要知道精靈『位置』『尺寸』『紋理區域』.『批處理』將多個渲染合拼在一起.這對GPU有利.批處理設立浮點數『緩存』保存頂點.該緩存區初此時需清空.定義『批處理』結構BATCHER.

public class BATCHER {

成員Buffer為浮點型數組用於存儲頂點.

private static float[] buffer;

成員indices為短整形索引.以三角形排列用於儲存頂點.

public static short[] indices ;

頂點列表用於批處理渲染

private static VERTICES vertices;

緩存寫入索引初此為零

private static int index = 0;

精零數量初此為零

public static int num = 0;

初次批處理.max精靈為最大量.

public static void Init(int max){

分配2D精靈頂點緩存,每個精靈有4頂點,每頂點要4個浮點數(空間坐標x,y兩個,紋理坐標u,v兩個)

buffer = new float[max44];

分配頂點緩存.『頂點』『紋理座標』『索引』

vertices = new VERTICES(max * 4, max*6, false,true);

分配索引每個精靈6個索引

indices = new short[max*6];

int len = indices.length;// 索引數組長度

index = 0;// 緩存寫入索引

num = 0;//  精零數量

生成頂點索引

for (int i=0,j=0; i < len; i += 6, j += 4) {

indices[i + 0] = (short)(j + 0);

indices[i + 1] = (short)(j + 1);

indices[i + 2] = (short)(j + 2);

indices[i + 3] = (short)(j + 2);

indices[i + 4] = (short)(j + 3);

indices[i + 5] = (short)(j + 0);

}

設置頂點索引

vertices.SetIndices(indices, 0, indices.length);

}

準備進行批處理渲染.首先綁定紋理.並將『精零數量』『緩存索引』置零

public static void Begin(TEXTURE texture){

texture.Bind();// 綁定紋理

num = 0;//  精零數量

index = 0;// 緩存寫入索引

}

結束批處理渲染.將頂點提交給OpenGL數組並觸發.綁定頂點數據後進形三角形渲染.然後取消數據綁定.每次調用繪畫時.向緩存區中添加4個頂點參數為『位置』『顏色』『索引』『紋理區域』

public void boolean End(){

vertices.SetVertices(buffer,0,index);

vertices.Bind();

vertices.Draw(GL10.GL_TRIANGLES, 0, num * 6);

vertices.Unbind();

}

批處理繪畫計算精靈中心『位置』及『寬和高』.和『紋理區域』.生成精靈『左下角』與『右上角』『空間座標』與『紋理座標』

public static void Draw(float x,float y,float width,float height,REGION region){

float halfWidth = width/2.0f;// 寬度一半

float halfHeight = height/2.0f;//高度一半

float x1 = x – halfWidth;

float y1 = y – halfHeight;

float x2 = x + halfWidth;

float y2 = y + halfHeight;

buffer[index++] = x1;

buffer[index++] = y1;

buffer[index++] = region.u1;

buffer[index++] = region.v2;

buffer[index++] = x2;

buffer[index++] = y1;

buffer[index++] = region.u2;

buffer[index++] = region.v2;

buffer[index++] = x2;

buffer[index++] = y2;

buffer[index++] = region.u2;

buffer[index++] = region.v1;

buffer[index++] = x1;

buffer[index++] = y2;

buffer[index++] = region.u1;

buffer[index++] = region.v1;

++num;

}

}

在調用時首先清理緩存並存入紋理.但只能對使用同一紋理精靈進行批處理.最後結束渲染.如下:

BATCHER.Begin(Texture);

BATCHER.Draw(X,Y,WIDTH,HEIGHT,Region);

BATCHER.End();

Android遊戲之頂點索引

Android遊戲之頂點索引

OpenGL均採用三角形列表進行渲染.每個三角形都有三個頂點.在有些情況下兩個或多個三角形會共用頂點.如上圖有兩個頂點具有相同『位置』『顏色』『紋理座標』.但甘浪費空間.更好解卻方法是將所有頂點保存在列表.而三角形頂點保存索引.OpenGL ES要求索引值使用短整數或字節.即OpenGL ES每次最多渲染65536個頂點.

我地需要一個Vertices類,用於存儲每個頂點『位置』『顏色』與『紋理座標』,並且它需要兩個選項.頂點是否『顏色』與『紋理座標』

public class VERTICES {

boolean color_have = false;//  擁有顏色

boolean texture_have =false;// 擁有紋理坐標

int vertex_size = 0;// 每個頂點所占大小

使用FloatBuffer保存頂點

FloatBuffer  vertex_array = null;

使用ShortBuffer頂點索引

ShortBuffer index_array = null;

頂點隊列分配記憶體,vertex_max為最大頂點量, index_max為最大索引量

VERTICES(int vertex_max,int index_max,boolean color_have,boolean texture_have){

this.color_have  = color_have;//  擁有頂點顏色

this.texture_have = texture_have;// 擁有紋理坐標

計算每頂點所占大小.顏色占4單元.紋理座標占2單元.2D座標占2單元.每個整數占4字節

int vertex_size = (2 + (color_have?4:0) + (texture_have?2:0) ) * 4;

因為OpenGL ES是以C API結口提供.無法直接使用JAVA數組.因此你需要C數組系統堆棧記憶體.而非JAVA虛擬機記憶體.需要 FloatBuffer分配頂點記憶體.

vertex_max為最大頂點量.

ByteBuffer buffer = ByteBuffer.allocateDirect(vertex_size * vertex_max);

將『網絡字節』改為『主機字節』或稱為『CPU字節』

buffer.order(ByteOrder.nativeOrder());

獲取整數數組

vertex_array = buffer.asIntBuffer();

每個短整形占兩個字節.index_max為最大索引量

buffer = ByteBuffer.allocateDirect(index_max * Short.SIZE/8);

將『網絡字節』改為『主機字節』

buffer.order(ByteOrder.nativeOrder());

頂點短整數數組

index_array = buffer.asShortBuffer();

}

將頂點提交給OpenGL數組並觸發

public void SetVertices(float[] vertices,int offset,int count){

vertex_array.clear();// 清空緩存.設定當前位置

int len = offset + count;

for(int i=offset, j=0; i < len; i++, j++)

vertex_Buffer[j] = Float.floatToRawIntBits(vertices[i]);

vertex_array.put(vertex_Buffer, 0, count); // 寫入數據.移動當前位置

vertex_array.flip();// 觸發

}

將頂點索引提交給OpenGL數組

public void SetIndices(short[] indices,int offset,int count) {

index_array.clear();// 清空緩存.設定當前位置

index_array.put(indices, offset, count); // 寫入數據.移動當前位置

index_array.flip();// 觸發

}

綁定數據

public void Bind(){

GL10  gl = GRAPHICS.gl;

啟用頂點數組

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

設置當前寫入位置0

vertex_array.position(0);

設置頂點指針,每個頂點兩個元素.xy兩分量

gl.glVertexPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);

if(color_have == true){//顏色

啟用頂點顏色數組

gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

設置當前寫入位置2

vertex_array.position(2);

設置顏色指針,RGBA四分量

gl.glColorPointer(4, GL10.GL_FLOAT, vertex_size, vertex_array);

}

if(texture_have ){// 紋理坐標

啟用紋理坐標數組

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

vertex_array.position(color_have?6:2);// 寫入位置

設置紋理坐標指針,UV兩分量

gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);

}

}

取消綁定數據

public void Unbind(){

GL10 gl = GRAPHICS.gl;

關閉頂點紋理數組

if(color_have)

gl.glDisableClientState(GL10.GL_COLOR_ARRAY );

關閉頂點顏色數組

if(texture_have)

gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

}

繪畫三角形

public void Draw(int mode,int offset,int count){

GL10 gl = GRAPHICS.gl;

繪畫頂點

if(index_array != null){// 繪畫

index_array.position(offset);//

gl.glDrawElements(mode, count, GL10.GL_UNSIGNED_SHORT, index_array);

}

else{

gl.glDrawArrays(mode, offset, count);

}

}

}

投幣式淨水機之濾芯效果分析

投幣式淨水機不但擁有七個以上濾芯.而且還裝有紫外線殺菌燈.只要定期更換濾芯制水品質便有保正.

過濾層級 濾芯
1 『5微米中空棉濾芯』也稱『PP棉濾芯』
2/3 『壓縮活性碳芯』兩個
4 『1微米中空棉濾芯』也稱『PP棉濾芯』
5 『反滲透膜濾芯』也稱『PR膜』
6 『後置活性碳慮芯』
7 『礦化慮芯』
8 紫外線殺菌燈

 

濾芯 功能簡介
『5微米中空棉濾芯』也稱『PP棉濾芯』 去除水中大於5微米懸浮物如『鐵銹』『砂石』
『壓縮活性碳芯』兩個 去除水中氯氣、異味、異色、有機物、改善水質口感.
『1微米中空棉濾芯』也稱『PP棉濾芯』 去除水中大於1微米懸浮物和雜質.
『反滲透膜濾芯』也稱『PR膜』 『PR膜』孔徑為0.0001微米,需經高壓泵進入『PR膜』.去除『細菌』『病毒』『有機物』『重金屬』『農藥』『膠體』只有純水能透過『PR膜』而廢水則被排出,廢水不能飲用.
『後置活性碳慮芯』 去除水中異味,水質甘甜.
『礦化慮芯』 加入各種微量礦物元素.並進入水箱存儲
『紫外線殺菌燈』 殺菌後出水

 

濾芯 更換週期
『5微米中空棉濾芯』 1到2個月或PP濾棉變褐色
『壓縮活性碳芯』兩個 2到3個月或水質有異味
『1微米中空棉濾芯』 2到3個月或水質有異味
『反滲透膜濾芯』也稱『PR膜』 18到24個月或PP濾棉變褐色
『後置活性碳慮芯』 夏季每月更換,東季每2個月更換.更換越頻繁口感
『礦化慮芯』 每半年更換.各種微量礦物元素含量越高.

 

投幣式净水機之耗水電量

投幣式净水機會耗電也繪水.所以要計算出成本.才能算出最終售價.因售水機內部設計統一所以其耗水電量均一至. 而且商業水電比家用要貴幾倍.

制水攻率 純水制水量 說明簡介
100W 63(L/H) 每小時制水63升.每度電可制升水630升.若每天售水1000升.則為1度電

 

純水與廢水比 說明簡介
1/3 每頓水喉水可制純水0.25頓.而廢水是0.75噸.因1噸=1000升.即每噸可制水250升.

 

每月售水 價格 商業水 商業電 成本/升 收益
1000升 5升2元 每頓5元 每度1.5元 0.0065 793.5

 

Android遊戲之『軸對齊邊界盒』碰撞

Android遊戲之『軸對齊邊界盒』碰撞

大部分模形都適合使用『圓形碰撞』.但若模形呈長條形則適合使用『矩形碰撞』.也稱為『軸對齊邊界盒碰撞』.特點是頂底邊與X軸平行,左右邊與Y軸平行.碰撞算法以包圍著對象最小矩形.但缺點是無法隨物體旋轉.速度快但精度較差.若定義矩形則由中心位置與長寬所組成.

public class RECT2D {

矩形中心位置

public VECTOR2D center;

矩形寬與高

public float  width;

public float  height;

購造『軸對齊邊界盒』輸入中心位置與寬高.

public RECT2D(float x,float y,float width,float height){

this.center = new VECTOR2D(x, y);    // 中心點

this.width  = width;

this.height = height;

}

『點』與『矩形』碰撞.若點為於四條邊之內則發生碰撞

public boolean overlap(VECTOR2D point){

if(point.x  < center.x + width/2 &&

point.x  > center.x – width/2 &&

point.y  < center.y + height/2 &&

point.y  > center.y – height/2 )

return true;

else

return false;

}

兩矩形碰撞測試,原點在左下角.算法睇上圖有D複雜.『矩形1左邊』在『矩形2右邊』之左則.『矩形1右邊』在『矩形2左邊』之右則.『矩形1底邊』在『矩形2頂邊』之下則.『矩形1頂邊』在『矩形2底邊』之上則.只要全部符合則兩矩形重疊.

public boolean overlap(RECT2D rect){

if(center.x – width/2 < rect.center.x + rect.width/2 &&

center.x + width/2 > rect.center.x – rect.width/2 &&

center.y – height/2 < rect.center.y + rect.height/2 &&

center.y + height/2 > rect.center.y – rect.height/2 )

return true;

else

return false;

}

『圓形』與『矩形』碰撞.原點在左下角.需先計算『矩形』與『圓心』之『最近點』.『圓心』在『矩形』之外.則『最近點』落在『矩形』邊緣之上.如果『圓心』在『矩形』之內.則『最近點』是『圓心』.然後計算『圓心』與『最近點』之距離.若小於『圓半徑』內則發生重疊.

public boolean overlap(CIRCLE2D circle){

float closestX = circle.center.x; // 圓心座標X

float closestY = circle.center.y;// 圓心座標Y

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

closestX = center.x – width/2;

else

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

closestX = center.x + width/2;

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

closestY = center.y – height/2;

else

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

closestY = center.y + height/2;

if(circle.center.DistSquared(closestX,closestY) < circle.radius * circle.radius)

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

else

return false; // 無發生碰撞

}

}

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

}

}

}

 

 

Photoshop之繪畫遊戲地圖

Photoshop之繪畫遊戲地圖
Photoshop之繪畫遊戲地圖
Photoshop之繪畫遊戲地圖

在遊戲設計中『地圖』『背景』『人物動畫』都交給美術處理.若由程序員繪畫則成為噩夢.但『遊戲地圖生成器』Game Map Generator為一款Photoshop插件.可以幫助程序員在10分鐘內製作出專業級『地圖』.安裝Game Map Generator其實可直接拖入Photoshop空白位置完成安裝.

檔案->指令碼->瀏覽->載入『Game-Map-Generator-installer.jsx』

啟動Game Map Generator

視窗->延伸攻能-> Game Map Generator

新建地圖檔案

MAP->WIDTH『寬』填1200. HEIGHT『高』填800.按CREATE新建地圖檔案

渲染地面紋理

MAP->GROUND TEXTURE『地面紋理』有三款紋理可選『草地』、『荒地』、『雪地』

渲染網格

MAP->GRID『網格』有四款紋理可選『四邊網格』和『六邊網格』

設置等距變換

MAP->ISOMETRIC TRANSFORM『等距變換』選擇『圖層』後有五個方向可選.但每旋轉一次都會有變形損失.

渲染地形

ELEMENTS->ADD LAYER『+』新建圖層->BRUSHES『畫刷』有5款->繪畫任意地形->CHOOSE AN ELEMENT『選擇元素』

給地形繪畫紋理

TEXTURING『紋理』->BRUSHES『畫刷』有7款-> CHOOSE AN ELEMENT『選擇元素』18款地紋紋理可選『綠地』『草地』『荒地』『歸裂』『雪地』『地板』『熔岩』-> 給圖層繪畫任意形狀紋理

繪畫灌木

TEXTURING『紋理』-> BRUSHES『畫刷』->SPECIAL BRUSHES『灌木畫刷』可選『荊棘』『長草』『野花』-> 給圖層任意繪畫灌木

隨機生成紋理

TEXTURING『紋理』-> BRUSHES『畫刷』->RANDOM TEXTURES『隨機紋理』可選『蘑菇』『野花』『野草』

繪畫圖標『樹木』『岩石』『橋樑』『木屋』『金幣』

ICONS『圖標』-> CHOOSE AN ICON『選擇圖標』點選後可移動縮放

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

 

Photoshop之3D漢字

Photoshop之3D圖像
Photoshop之3D漢字
Photoshop之3D漢字

之前介紹『像素3D文字』效果須好.但在生成時『分辨率』要很大所以略有不便.這裡介紹『Ground Isometric Mock-Up』地面等距模型.『漢字』『英文』『圖像』『形狀』不論尺寸均支持.效果圖如上.安裝與使用方法如下:

  1. 最好用Adobe Photoshop CC 2014
  2. 轉換成英文版本
  3. 『Ground Isometric Mock-UP.atn』與『asl』兩個文檔直接拖入Photoshop空白位置即完成導入.在Actions窗口可查看
  4. 輸入『漢字』或『圖檔』並選定圖層
  5. 選定動作『Action』
  6. 按『動作播放』然後等待
動作Action 簡介
ONE Image::Depth-XX::Transform LEFT 左側3D高度1~5
ONE Image::Depth-XX::Transform RIGHT 右側3D高度1~5

 

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

Photoshop之繪畫像素3D文字

Photoshop之繪畫像素3D文字
Photoshop之繪畫像素3D文字

近日在網上購得專門用於生成『3D立體像素』3D Isometric shape Generator.它其實是Photoshop Action『動作』插件.其立體效果非常好.安裝與使用方法如下:

  1. 最好用Adobe Photoshop CC 2014
  2. 轉換成英文版本
  3. 『筆刷』『動作』『圖案』三個文檔全部都導入PS裡.直接拖入Photoshop空白位置即完成導入.在Actions窗口可查看
  4. 若圖案簡單分辨率可較小.反之分辨率需較大.
  5. 若插入文字像素一定要夠大.像上圖分辨率需3000*3000.(這點不便利)非常非常佔用磁盤空間.
  6. 選定『陰影方向』分別支持左右立體左右陰影.四種方向.
  7. 按『動作播放』然後靜靜等待
  8. 按1-12轉換顏色風格
Action 簡介
RV Right Shadow 右則立體,右陰影
RV Left Shadow 右則立體,左陰影
LV Right Shadow 左則立體,右陰影
LV Left Shadow 左則立體,左陰影
1-12 顏色風格轉換
Default 默認風格
Hand Drawn Stroke Style 手繪筆劃風格

 

Android遊戲之SDK版本

Android遊戲之SDK版本

在AndroidManifest.xml中修改< uses-sdk>以設置APP最低支持Android系統.以及編譯APP之SDK版本.設定Android版本需指定整數值也稱為SDK版本號.編譯版本『targetSdkVersion』應儘量使用最新SDK版本.而最低版本『minSdkVersion』應最量低,讓遊戲在更多Android設備上安裝.但也要避免使用低版SDK從而『不支持某些API』.例如下:

<uses-sdk android:minSdkVersion=”9″ android:targetSdkVersion=”28″ />

獲取當前android系統版本號

int   SDK = Integer.parseInt(android.os.Build.VERSION.SDK);

uses-sdk屬性 簡介
minSdkVersion APP最低支持Android系統
targetSdkVersion 編譯APP之SDK版本

 

Android版本 API Level『SDK版本號』
Android API 28
Android 8.1(Oreo) 27
Android 8.0(Oreo) 26
Android 7.1.1(Nougat) 25
Android 7.0(Nougat) 24
Android 6.0(Marshmallow) 23
Android 5.1(Lollipop) 22
Android 5.0(Lollipop) 21
Android 4.4W(KitKat Wear) 20
Android 4.4(KitKat) 19
Android 4.3(Jelly Bean) 18
Android 4.2(Jelly Bean) 17
Android 4.1(Jelly Bean) 16
Android 4.0.3(IceCreamSandwich) 15
Android 4.0(IceCreamSandwich) 14
Android 3.2(Honeycomb) 13
Android 3.1(Honeycomb) 12
Android 3.0(Honeycomb) 11
Android 2.3.3(Gingerbread) 10
Android 2.3(Gingerbread) 9
Android 2.2(Froyo) 8
Android 2.1(Eclair) 7
Android 2.0.1(Eclair) 6
Android 2.0(Eclair) 5
Android 1.6(Donut) 4
Android 1.5(Cupcake) 3
Android 1.1 2
Android 1.0 1

 

Windows10禁用休眠

Windows10禁用休眠

近日發現Photoshop經常報『暫存磁盤』不足.睇來C盤空間不足.一睇『設定->系統->本機C:->系統與保留->休眠檔案』佔用25.5GB. 『休眠』只是將記憶體保存在磁盤,開機時從磁盤載入以提高開機速度.其實Windows10開機速度還是要睇硬件驅動.現只要禁用『休眠』即可釋放大量磁盤空間以解燃眉之急.

  1. 系統管理員身份執行命令視窗CMD
  2. 禁用休眠系統『powercfg –h off』
  3. 重啟Windows10

 

重啟休眠系統『powercfg –h on』

休眠檔案壓縮50%『 powercfg –h size 50』

Android遊戲之縱向與橫向

Android遊戲之縱向與橫向

Android特點是每當改變手機旋轉方向時APP方向也隨之改變.『橫向』或『縱向』通過加速計傳感器確定.遊戲方向在設計時便確立.所以根本無需改變遊戲方向.只要指定Activity方向便可鎖定不變.在AndroidManifest.xml中修改<activity>屏幕方向屬性『screenOrientation』

強制屏幕縱向

<activity android:name=”.MainActivity”

android:screenOrientation=”portrait”>

強制屏幕橫向

<activity android:name=”.MainActivity”

android:screenOrientation=”landscape”>

在運行時更改屏幕為橫向

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

在運行時更改屏幕為縱向

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

screenOrientation屏幕方向 簡介
unspecified 默認值,由系統決定
landscape 強制屏幕橫屏顯示
portrait 強制屏幕豎屏顯示
behind 與前一個Activity方向相同
sensor 根據加速計傳感器轉動手機90度、180度、270度. Activity都更著變化
sensorLandscape 屏幕只可橫屏旋轉
sensorPortrait 屏幕只可豎屏旋轉
nosensor 忽略加速計傳感器.旋轉手機不會改變方向
user 用戶當前設置方向

 

Android遊戲之版本控制

Android遊戲之版本控制

Android遊戲發佈到Google Play需要追蹤遊戲『版本』.以便Google Play自動更新遊戲.要設定版本號需編輯 AndroidManifest.xml其根元素<manifest>添加versionCode和versionName屬性

versionCode:版本代碼(整數)大於等於1

versionName:版本名(字符).在Google Play上顯示.建議『versionName= versionCode/100.0f』如『versionCode=”2″』則『versionName=”0.02″』.因為任何遊戲都需要幾十次更新才可達置完善.如下:

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”net.bookcard.aa”

android:versionCode=”1″

android:versionName=”0.01″>

獲取包管理器

PackageManager  package_manager = context.getPackageManager();

獲取Android應用包名這裡返回『net.bookcard.aa』

package_name = context.getPackageName();

獲取版本信息

PackageInfo package_info = Package_Manager.getPackageInfo(package_name,0);

獲取版本名

String version = Package_Info.versionName;

獲取版本代碼

int code = Package_Info.versionCode;

 

Android遊戲之喚醒鎖

Android遊戲之喚醒鎖

Android最耗電首當觸摸屏.為節約電能很多人都將亮度降低.但又耗神.折中之法是系統自動變暗進入睡眠狀態.觸屏後自動變明亮.如果想屏膜保持喚醒狀態可是WakeLock.但觸屏遊戲是不需WakeLock『喚醒鎖』.它只適用於通過『加速計』控制之遊戲.

首先在AndroidManifest.xml添加權限

<uses-permission android:name=”android.permission.WAKE_LOCK” />

獲取電源管理器

PowerManager power_manager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

生成喚醒鎖. levelAndFlagsw為控制標記.tag為鎖名

PowerManager.newWakeLock(int levelAndFlags, String tag)

levelAndFlags: 屏幕燈 鍵盤燈
PARTIAL_WAKE_LOCK 關閉 關閉
SCREEN_DIM_WAKE_LOCK 低亮度 關閉
SCREEN_BRIGHT_WAKE_LOCK 高亮度 關閉
FULL_WAKE_LOCK 高亮度 開啟
ON_AFTER_RELEASE 延時關燈 關閉
ACQUIRE_CAUSES_WAKEUP 強制開啟 強制開啟

一般使用PARTIAL_WAKE_LOCK生成喚醒鎖

WakeLock wake_lock = power_manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,”LOCK”);

啟用喚醒鎖.在Activity.onResume()中調用

wake_lock.acquire();

釋放喚醒鎖.在Activity.onPause()中調用

wake_lock.release();

 

Photoshop之中英文快速切換

Photoshop之中英文快速切換

Photoshop常用于『修圖』『做圖』.但有工具卻只能在英文版下運作.下面之方法是在兩種語言之間快速切換,而且適合所有版本.

  1. 確定Photoshop安裝目錄查看其內容可睇到.如Photoshop2015『C:\Program Files\Adobe\Adobe Photoshop CC 2015』
  2. 要打開其中文語言包目錄『Locales\zh_TW\Support Files』
  3. 將『dat』文檔『拷貝幅本』.然後刪除.
  4. 重啟Photoshop即自動轉為英文版
  5. 若要重新改為中文只需將『拷貝幅本』重新命名為『dat』

Photoshop之無法完成『XX』指令因為暫存磁盤已滿

Photoshop之無法完成『XX』指令因為暫存磁盤已滿

在網上買左個ACTIONS『動作』但總是彈出

『無法完成『XX』指令因為暫存磁盤已滿』

睇C盤空間已耗盡.解決方法是為Photoshop設定更多暫存磁盤.

  1. 編輯->偏好設定->暫存磁盤
  2. 勾選更多『磁盤』
  3. 按『確定』

 

Android遊戲之背景音樂

Android遊戲之背景音樂

遊戲聲音分為『音樂』與『音效』.遊戲背景『音樂』播放時間通常達幾分鐘以上.音檔通常較大不能一次性載入記憶體.只能以『數據流』方式逐次讀入數據塊.並解碼為PCM數據交給音頻芯片上.Android系統提供MediaPlayer幫你解卻所有問題. 將所有『音樂』文檔存放在『\app\src\main\assets』目錄下.讓AssetManager能夠訪問.

生成MediaPlayer音頻播放器

MediaPlayer media_player = new MediaPlayer();

獲取ASSET文檔描述符

AssetFileDescriptor afd = asset_manager.openFd(file_name);

文檔描述符

FileDescriptor file_descriptor = afd.getFileDescriptor();

獲取音檔數據開此位置偏移量

long offset = afd.getStartOffset();

獲取音檔數據長度

long length = afd.getLength();

設定音檔數據

media_player.setDataSource(descriptor,offset,length);

每次啟動播放時.都需載入準備播放

media_player.prepare();

啟動播放

media_player.start();

播放中若暫停播放

media_player.pause();

播放中若停止播放

media_player.stop();

設定循環播放.

media_player.setLooping(true);

設定左右聲道音量.數值在0~1之間

media_player.setVolume(volume,volume);

判定時否播放中. isPlaying()若返回true則播放中否則返回false

media_player.isPlaying();

或用註冊OnCompletionListener簡聽器

media_player.setOnCompletionListener(listener);

若退出遊戲需釋放記憶體

media_player.release();

Android遊戲之音效模塊

Android遊戲之音效模塊

遊戲聲音分為『音樂』與『音效』.音效長度不應超過5秒.讓其可以載入『記憶體』中.並將所有音效文檔存放在『\app\src\main\assets』目錄下.讓AssetManager能夠訪問.並將所有『音效』文檔存為『OGG』格式.並且採用低採樣頻率.

Android提供SoundPool『音效池』實現音效載入與播放.

SoundPool(int maxStreams, int streamType, int srcQuality)

maxStreams:用時能播放音效量

streamType:使用音樂流輸出音頻.這裡使用AudioManager.STREAM_MUSIC

srcQuality:廢棄,總為0

構建音效播放

SoundPool sound_pool = new SoundPool(32, AudioManager.STREAM_MUSIC,0);

用於訪問Asset目錄

AssetManager asset_manager = context.getAssets();

獲取資源文檔描述符

AssetFileDescriptor afd = asset_manager.openFd(file_name);

把音效文檔載入『記憶體』中,讓AssetFileDescriptor傳給load()並返回整數ID句柄

int id = sound_pool.load(afd,1);

播放音效

public final int play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)

soundID:音效ID句柄

leftVolume\rightVolume:左右聲道音量在0.0f – 1.0f之間

priority:優先級.值越大優先級越高,0優先級最低

loop:循環次數.0代表不循環

rate:播放速率取值0.5f – 2.0f之間.其中0.5f表示播放速度慢一半.1表示正常速率播放.

單次播放音效

sound_pool.play(id, 1.0f, 1.0f, 0, 0, 1);

當不需要音效時,需要釋放音效記憶體

sound_pool.unload(ID);

當退出遊戲時需釋放SoundPool『音效池』

sound_pool.release();

企業級雙頻千兆無線路郵器

企業級雙頻千兆無線路郵器
企業級雙頻千兆無線路郵器

近日無線(wifi)路郵壞左.剛好有只TP-LINK 150M無線路郵.但手機傳送速度很慢.在淘寶上睇到MERCURY-MER1200G『企業級千兆無線路郵』帶四條全向天線.個老細話與TP-Link同一間廠出.而且價格低廉所以馬上落單.

到手後所謂千兆只是『5G最高867Mbps』+『2.4G最高300Mbps』.並非正真千兆路郵.不過信號超強而且帶有TURBO按鈕.用於擴大路郵器無線覆蓋範圍並增強無線信號穩定性.設定時要啟用UPnP否則影響BT下載速度.而且無USB接口.是否能長時間運行還有待測試.

 

Android Studio之自定遊戲圖標

Android Studio之自定遊戲圖標

Android遊戲都有其icon圖標.而且該圖標也會Google Play上展示.要自定圖標需在遊戲項目『\app\src\main\res\mipmap-xxx』查找icon圖標.然後將其替換.若Eclipse則在drawable目錄下. 並且隨著手機屏幕分辨率不斷進化.遊戲需要提供不同密度圖標『自適應圖標』.圖標分為兩類『圓』與『方』.並且需要在AndroidManifest.xml添加icon圖標屬性.

<application  android:icon=”@mipmap/ic_launcher”

android:roundIcon=”@mipmap/ic_launcher_round”>

Android Studio帶有icon圖標生成器,全自動生成所有不同分辨率圖標.在Eclipse你需要用PS生成.

  1. 項目右擊『New->Image Asset』
  2. Icon Type:選Launcher Icons(Adaptive and Legacy)
  3. Name:填ic_launcher
  4. Asset Type:勾選Image
  5. Path:輸入自定圖標路徑
  6. Trim勾選Yes
  7. Resize拖到100%
  8. Legacy Icon勾選Yes
  9. Gound Icon Generate勾選Yes
  10. Google Play Store Icon勾選Yes
類型 文檔名
方形 ic_launcher.png
圓形 ic_launcher_round.png
前景 ic_launcher_foreground.png
背景 ic_launcher_background.png

 

目錄 簡介 分辨率
mipmap-mdpi 中密度圖標(必須有) 48*48
mipmap-hdpi 高密度圖標 72*72
mipmap-xhdpi 超高密度圖標 96*96
mipmap-xxhdpi 超超高密度圖標 144*144
mipmap-xxxhdpi 超超超高密度圖標 192*192

 

Android遊戲之文檔讀寫

Android遊戲之文檔讀寫

在Android遊戲開發時需要讀入大量Asset(資源).如『3D模型』『紋理』『音頻』『地型』等Asset(資源)文檔.Android建議把資源存放於『res』目錄,但它不適合存放原生資源. 在遊戲開發時使用『assets』目錄,所有遊戲資源文檔存放該目錄下.而且還可以指定目錄結構. 要訪問『assets』目錄需AssetManager資源管理器:

『assets』目錄位於『\app\src\main\assets』這點與Eclipse有所不同.

AssetManager asset_manager = context.getAssets();

打開Asset文檔並返回輸入流InputStream

InputStream input_stream = asset_manager.open(file_name);

讀取『外部緩存』SD卡

Asset只適合用於讀取遊戲資源.若在遊戲運行時讀寫文檔數據.若訪問SD卡需要加入度寫權限:

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

<uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE”/>

然後需要確定手機是否裝有SD卡

獲取『外部緩存』SD卡裝態.其實現在手機載固態硬盤劃出部分空間作為『外部緩存』

String state = Environment.getExternalStorageState();

Environment.MEDIA_MOUNTED:SD卡插入並可正常讀寫

Environment.MEDIA_MOUNTED_READ_ONLY:SD卡已插入,但只能讀取

獲取『外部緩存』路徑

File file_path = Environment.getExternalStorageDirectory();

並不建議在『外部緩存』SD卡讀寫.一SD卡可能被彈出.二可能需要用戶授權.如果數據較小建議寫入『內部存儲』

APP內部存儲緩存目錄『/data/data/< package name >/files/』

File file_path = context.getFilesDir() ;

生成目錄

file_path.mkdir();

連接路徑

File file  = new File(file_path,file_name);

打開文檔並返回輸入流

InputStream input_stream = new FileInputStream(file);

打開文檔並返回輸出流

OutputStream output_stream = new FileOutputStream(file);

獲取指定寫入/讀取路徑 存儲位置
Environment.getExternalStorageDirectory() /mnt/sdcard/
context.getExternalFilesDir() /mnt/sdcard/Android/data/< package name >/files/
context.getExternalCacheDir() /mnt/sdcard/Android/data/< package name >/cach/
context.getFilesDir() /data/data/< package name >/files/
context.getCacheDir() /data/data/< package name >/cach/

 

獲取指定寫入/讀取路徑 簡介
Environment.getExternalStorageDirectory() 外部存儲
context.getExternalFilesDir() 外部存儲
context.getExternalCacheDir() 外部臨時存儲
context.getFilesDir() 內部存儲
context.getCacheDir() 內部臨時緩存

 

Android遊戲之按鍵

Android遊戲之按鍵

Android其實是支持標準鍵盤.大多數Android手機只支持軟鍵盤. 遊戲要捕足按鍵事件你需要實現OnKeyListener接口.它通過與接收鍵盤事件View相連並接收按鍵事件.

public boolean onKey(View view,int keyCode,KeyEvent event);

按鍵編碼keyCode為整數0~127一共128個按鍵.常量值形式為KeyEvent.KEYCODE_XXX

獲取按鍵字符:KeyEvent.getUnicodeChar();

獲取按鍵事件類型: KeyEvent.getAction();

獲取按鍵事件類型: KeyEvent.getAction(); 簡介
KeyEvent.ACTION_MULTIPLE 連續多個重複鍵事件
KeyEvent.ACTION_DOWN 按下按鍵時觸發
KeyEvent.ACTION_UP 鬆開按鍵時觸發

當鍵盤按下時其值被保存在鍵盤列狀態

public static int   MAX_KEY = 128;// 128個按鍵

public static int[] Key_Action  = new int[MAX_KEY];// 按鍵事件,按下/送開

public static int[] Key_Code   = new int[MAX_KEY];// 按鍵代碼

public static int[] Key_Char   = new int[MAX_KEY];// 按鍵字符

public static int  Key_Count  = 0; // 未處理按鍵量

private static boolean[] Key_State = new boolean[128];// 按鍵的當前狀態true按下.false鬆開

private static KeyboardListener Keyboard_Listener = new KeyboardListener();//按鍵監聽器

實現鍵盤監聽接口從View中接收鍵盤事件並處理

static class KeyboardListener implements View.OnKeyListener{

@Override

public boolean onKey(View view, int keyCode, KeyEvent event){

int index;

int Action;

if(Key_Count >= MAX_KEY)

return false;

index = Key_Count;

++Key_Count;

Key_Code[index] = keyCode;// 鍵代碼

Key_Char[index] = event.getUnicodeChar();// 鍵字符

Action = event.getAction();//  獲取按鍵事件類型

if(Action == KeyEvent.ACTION_MULTIPLE)// 多重

return false;

if(Action == KeyEvent.ACTION_DOWN)

{// 按下

if(keyCode >0 && keyCode < 127)

Key_State[keyCode] = true;// 按下

Key_Action[index] = KeyEvent.ACTION_DOWN;

}

else

if(Action == KeyEvent.ACTION_UP)

{// 鬆開

if(keyCode >0 && keyCode < 127)

Key_State[keyCode] = false;

Key_Action[index] = KeyEvent.ACTION_UP;

}

return false;

}

初此按鍵監聽器

public static boolean Init(View view)

{

for(int i=0;i<Key_State.length;++i)

Key_State[i] = false;

view.setOnKeyListener(Keyboard_Listener);// view對按鍵進行監聽

view.requestFocus();//請求焦點

return true;

}

}

電話鍵常量 數值
KEYCODE_CALL 撥號鍵
KEYCODE_ENDCALL 掛機鍵
KEYCODE_HOME Home鍵返回面
KEYCODE_MENU 菜單鍵
KEYCODE_BACK 返回鍵
KEYCODE_SEARCH 搜索鍵
KEYCODE_CAMERA 拍照鍵
KEYCODE_FOCUS 拍照對焦鍵
KEYCODE_POWER 電源鍵
KEYCODE_NOTIFICATION 通知鍵
KEYCODE_MUTE 話筒靜音鍵
KEYCODE_VOLUME_MUTE 揚聲器靜音鍵
KEYCODE_VOLUME_UP 音量增加鍵
KEYCODE_VOLUME_DOWN 音量減小鍵

 

控制鍵常量 數值
KEYCODE_ENTER ENTER回車鍵
KEYCODE_ESCAPE ESC鍵
KEYCODE_DPAD_CENTER 方向鍵/確定鍵
KEYCODE_DPAD_UP 方向鍵/向上鍵
KEYCODE_DPAD_DOWN 方向鍵/向下鍵
KEYCODE_DPAD_LEFT 方向鍵/向左鍵
KEYCODE_DPAD_RIGHT 方向鍵/向右鍵
KEYCODE_MOVE_HOME 光標移動到開始鍵
KEYCODE_MOVE_END 光標移動到末尾鍵
KEYCODE_PAGE_UP 向上翻頁鍵
KEYCODE_PAGE_DOWN 向下翻頁鍵
KEYCODE_DEL 退格鍵
KEYCODE_FORWARD_DEL 刪除鍵
KEYCODE_INSERT 插入鍵
KEYCODE_TAB Tab鍵/焦點切換鍵
KEYCODE_NUM_LOCK 小鍵盤鎖
KEYCODE_CAPS_LOCK 大寫鎖定鍵
KEYCODE_BREAK Break/Pause鍵
KEYCODE_SCROLL_LOCK 滾動鎖定鍵
KEYCODE_ZOOM_IN 放大鍵
KEYCODE_ZOOM_OUT 縮小鍵

 

組合鍵常量 簡介
KEYCODE_ALT_LEFT Alt+Left
KEYCODE_ALT_RIGHT Alt+Right
KEYCODE_CTRL_LEFT Control+Left
KEYCODE_CTRL_RIGHT Control+Right
KEYCODE_SHIFT_LEFT Shift+Left
KEYCODE_SHIFT_RIGHT Shift+Right

 

數字與字母鍵常量 簡介
KEYCODE_0 數字鍵0
KEYCODE_1 數字鍵1
KEYCODE_2 數字鍵2
KEYCODE_3 數字鍵3
KEYCODE_4 數字鍵4
KEYCODE_5 數字鍵5
KEYCODE_6 數字鍵6
KEYCODE_7 數字鍵7
KEYCODE_8 數字鍵8
KEYCODE_9 數字鍵9
KEYCODE_A 字母鍵A
KEYCODE_B 字母鍵B
KEYCODE_C 字母鍵C
KEYCODE_D 字母鍵D
KEYCODE_E 字母鍵E
KEYCODE_F 字母鍵F
KEYCODE_G 字母鍵G
KEYCODE_H 字母鍵H
KEYCODE_I 字母鍵I
KEYCODE_J 字母鍵J
KEYCODE_K 字母鍵K
KEYCODE_L 字母鍵L
KEYCODE_M 字母鍵M
KEYCODE_N 字母鍵N
KEYCODE_O 字母鍵O
KEYCODE_P 字母鍵P
KEYCODE_Q 字母鍵Q
KEYCODE_R 字母鍵R
KEYCODE_S 字母鍵S
KEYCODE_T 字母鍵T
KEYCODE_U 字母鍵U
KEYCODE_V 字母鍵V
KEYCODE_W 字母鍵W
KEYCODE_X 字母鍵X
KEYCODE_Y 字母鍵Y
KEYCODE_Z 字母鍵Z

 

符號鍵常量 簡介
KEYCODE_PLUS 加號+
KEYCODE_MINUS 減號-
KEYCODE_STAR 乘號*
KEYCODE_SLASH 除號/
KEYCODE_EQUALS 等號=
KEYCODE_AT 符號鍵@
KEYCODE_POUND 井號鍵#
KEYCODE_APOSTROPHE 單引號’
KEYCODE_BACKSLASH 斜杆\
KEYCODE_COMMA 逗號,
KEYCODE_PERIOD 句號.
KEYCODE_LEFT_BRACKET 左括號[
KEYCODE_RIGHT_BRACKET 右括號]
KEYCODE_SEMICOLON 分號;
KEYCODE_GRAVE `
KEYCODE_SPACE 空格鍵

 

小鍵盤常量 簡介
KEYCODE_NUMPAD_0 小鍵盤數字鍵0
KEYCODE_NUMPAD_1 小鍵盤數字鍵1
KEYCODE_NUMPAD_2 小鍵盤數字鍵2
KEYCODE_NUMPAD_3 小鍵盤數字鍵3
KEYCODE_NUMPAD_4 小鍵盤數字鍵4
KEYCODE_NUMPAD_5 小鍵盤數字鍵5
KEYCODE_NUMPAD_6 小鍵盤數字鍵6
KEYCODE_NUMPAD_7 小鍵盤數字鍵7
KEYCODE_NUMPAD_8 小鍵盤數字鍵8
KEYCODE_NUMPAD_9 小鍵盤數字鍵9
KEYCODE_NUMPAD_ADD 小鍵盤加號+
KEYCODE_NUMPAD_SUBTRACT 小鍵盤減號-
KEYCODE_NUMPAD_MULTIPLY 小鍵盤乘號*
KEYCODE_NUMPAD_DIVIDE 小鍵盤除號’/’
KEYCODE_NUMPAD_EQUALS 小鍵盤等號’=’
KEYCODE_NUMPAD_COMMA 小鍵盤逗號’,’
KEYCODE_NUMPAD_DOT 小鍵盤點號’.’
KEYCODE_NUMPAD_LEFT_PAREN 小鍵盤左括弧'(‘
KEYCODE_NUMPAD_RIGHT_PAREN 小鍵盤左括弧’)’
KEYCODE_NUMPAD_ENTER 小鍵盤回車鍵

 

功能鍵常量 簡介
KEYCODE_F1 按鍵F1
KEYCODE_F2 按鍵F2
KEYCODE_F3 按鍵F3
KEYCODE_F4 按鍵F4
KEYCODE_F5 按鍵F5
KEYCODE_F6 按鍵F6
KEYCODE_F7 按鍵F7
KEYCODE_F8 按鍵F8
KEYCODE_F9 按鍵F9
KEYCODE_F10 按鍵F10
KEYCODE_F11 按鍵F11
KEYCODE_F12 按鍵F12

 

多媒體鍵常量 簡介
KEYCODE_MEDIA_PLAY 播放鍵
KEYCODE_MEDIA_STOP 停止鍵
KEYCODE_MEDIA_PAUSE 暫停鍵
KEYCODE_MEDIA_PLAY_PAUSE 播放/暫停鍵
KEYCODE_MEDIA_FAST_FORWARD 快進鍵
KEYCODE_MEDIA_REWIND 快退鍵
KEYCODE_MEDIA_NEXT 下一首鍵
KEYCODE_MEDIA_PREVIOUS 上一首鍵
KEYCODE_MEDIA_CLOSE 關閉鍵
KEYCODE_MEDIA_EJECT 彈出鍵
KEYCODE_MEDIA_RECORD 錄音鍵

 

通用遊戲手柄按鈕按鍵常量 簡介
KEYCODE_BUTTON_1 #1
KEYCODE_BUTTON_2 #2
KEYCODE_BUTTON_3 #3
KEYCODE_BUTTON_4 #4
KEYCODE_BUTTON_5 #5
KEYCODE_BUTTON_6 #6
KEYCODE_BUTTON_7 #7
KEYCODE_BUTTON_8 #8
KEYCODE_BUTTON_9 #9
KEYCODE_BUTTON_10 #10
KEYCODE_BUTTON_11 #11
KEYCODE_BUTTON_12 #12
KEYCODE_BUTTON_13 #13
KEYCODE_BUTTON_14 #14
KEYCODE_BUTTON_15 #15
KEYCODE_BUTTON_16 #16
KEYCODE_BUTTON_A 手柄A
KEYCODE_BUTTON_B 手柄B
KEYCODE_BUTTON_C 手柄C
KEYCODE_BUTTON_X 手柄X
KEYCODE_BUTTON_Y 手柄 Y
KEYCODE_BUTTON_Z 手柄 Z
KEYCODE_BUTTON_L1 手柄 L1
KEYCODE_BUTTON_L2 手柄L2
KEYCODE_BUTTON_R1 手柄R1
KEYCODE_BUTTON_R2 手柄R2
KEYCODE_BUTTON_MODE 手柄Mode
KEYCODE_BUTTON_SELECT 手柄Select
KEYCODE_BUTTON_START 手柄Start
KEYCODE_BUTTON_THUMBL 左拇指鍵
KEYCODE_BUTTON_THUMBR 右拇指鍵

 

按鍵常量 簡介
KEYCODE_NUM Number modifier
KEYCODE_INFO Info
KEYCODE_APP_SWITCH App switch
KEYCODE_BOOKMARK Bookmark
KEYCODE_AVR_INPUT A/V Receiver input
KEYCODE_AVR_POWER A/V Receiver power
KEYCODE_CAPTIONS Toggle captions
KEYCODE_CHANNEL_DOWN Channel down
KEYCODE_CHANNEL_UP Channel up
KEYCODE_CLEAR Clear
KEYCODE_DVR DVR
KEYCODE_ENVELOPE Envelope special function
KEYCODE_EXPLORER Explorer special function
KEYCODE_FORWARD Forward
KEYCODE_FORWARD_DEL Forward Delete
KEYCODE_FUNCTION Function modifier
KEYCODE_GUIDE Guide
KEYCODE_HEADSETHOOK Headset Hook
KEYCODE_META_LEFT Left Meta modifier
KEYCODE_META_RIGHT Right Meta modifier
KEYCODE_PICTSYMBOLS Picture Symbols modifier
KEYCODE_PROG_BLUE Blue “programmable”
KEYCODE_PROG_GREEN Green “programmable”
KEYCODE_PROG_RED Red “programmable”
KEYCODE_PROG_YELLOW Yellow “programmable”
KEYCODE_SETTINGS Settings
KEYCODE_SOFT_LEFT Soft Left
KEYCODE_SOFT_RIGHT Soft Right
KEYCODE_STB_INPUT Set-top-box input
KEYCODE_STB_POWER Set-top-box power
KEYCODE_SWITCH_CHARSET Switch Charset modifier
KEYCODE_SYM Symbol modifier
KEYCODE_SYSRQ System Request / Print Screen
KEYCODE_TV TV鍵
KEYCODE_TV_INPUT TV input鍵
KEYCODE_TV_POWER TV power鍵
KEYCODE_WINDOW Window鍵
KEYCODE_UNKNOWN 未知按鍵

 

Android遊戲之觸摸屏

Android遊戲之觸摸屏

Android最初就配備有觸摸屏.單只支持單點觸碰.直到Android2.0(SDK 5)才支持多點觸碰.你需要註冊觸摸監聽器OnTouchListener接口,通過onTouch()函式接受觸碰事件並處理.若已處理觸碰事件返回true但後續ACTION_DOWN事件將無法接收.在遊戲中要持續監聽應返回false.而且要分別處理單點觸碰和多點觸碰兩種情況.

static class TounchListener implements View.OnTouchListener {

@Override

public boolean onTouch(View v, MotionEvent event)

{

if(SYSTEM.GetVersion() >= 5)

MultiTouch(v, event);// 多點觸碰

else

SingleTouch(v, event);// 單點觸碰

return false;

}

觸摸監聽器

private static TounchListener Tounch_Listener = new TounchListener();

然後定義三個觸碰事件. 鬆開、按下、拖動.

public static int ACTION_UP    =  1;//  鬆開事件

public static int ACTION_DOWN =   2;// 按下事件

public static int ACTION_DRAGGED = 3;// 拖動事件

定義觸碰點緩存列.

public static int MAX_POINT = 1024;

public static int[] Point_Action = new int[MAX_POINT]; // 事件緩存

public static int[] Point_X    = new int[MAX_POINT];// x座標緩存

public static int[] Point_Y    = new int[MAX_POINT];// y座標緩存

public static int   Point_Count = 0;// 觸碰點量

因為遊戲分辨率與屏幕分辨率並不一至.所以你縮放比變量.

private static float  Scale_X;//  寬度縮放比

private static float  Sacle_Y;//  高度縮放比

初此觸碰模塊.view為Activity.分別輸入『目標分辨率寬與高』和『設備分辨率寬與高』

public static boolean Init(View view,int target_width,int target_height,int real_width,int real_height)

{ // 設置觸摸監聽器

view.setOnTouchListener(Tounch_Listener);

// 計算縮放因子

Scale_X = (float)target_width/(float)real_width;

Sacle_Y = (float)target_height/(float)real_height;

return true;

}

獲取觸摸事件. MotionEvent.getAction()返回觸摸事件

觸摸常量 簡介
ACTION_DOWN 第一隻手指觸屏時觸發
ACTION_POINTER_DOWN 非第一隻手指屏時觸發
ACTION_UP 最後一隻手指離開屏幕時觸發
ACTION_POINTER_UP 非最後一隻手指離開屏幕時觸發
ACTION_CANCEL 手勢取消時觸發
ACTION_MOVE 一隻或多隻手指移動時觸發
ACTION_MASK 提取觸摸事件

MotionEvent.getAction() & MotionEvent.ACTION_MASK

ACTION_POINTER_ID_MASK 提取手指索引

MotionEvent.getAction()&MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINT

處理單點觸碰事件.獲取觸摸座標MotionEvent.getX()返回x軸. MotionEvent.getY()返回y軸.屏幕左上角為座標原點.x軸指向右邊.y軸指向下邊

static void SingleTouch(View v, MotionEvent event)

{

int action;

int index;

action = event.getAction() & MotionEvent.ACTION_MASK;// 提取觸碰事件動作

index = Point_Count; //  索引

Point_Count = Point_Count + 1;// 觸碰量加一

Point_Action[index] = action; //  事件

if(action == MotionEvent.ACTION_DOWN ||

action == MotionEvent.ACTION_POINTER_DOWN)

Point_Action[index] = ACTION_DOWN;// 按下

else

if(action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_POINTER_UP ||

action == MotionEvent.ACTION_CANCEL)

Point_Action[index] = ACTION_UP;// 鬆開

else

if(action == MotionEvent.ACTION_MOVE)

Point_Action[index] = ACTION_DRAGGED;// 拖動

// 計算遊戲屏幕座標

Point_X[index] = (int)(event.getX()*Scale_X);

Point_Y[index] = (int)(event.getY()*Sacle_Y);

}

處理多點觸碰可以通過MotionEvent.getPointerCount()獲知同時有幾隻手指觸碰屏膜.而手指觸碰屏幕都會獲得新索引.索引可以通過getAction()提取:

MotionEvent.getAction()&MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT;

static void MultiTouch(View v, MotionEvent event)

{

int action;

int Point_index;// 點索引

action = event.getAction() & MotionEvent.ACTION_MASK;//  提取觸摸事件

Point_index = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT;//手指索引

Point_Count = event.getPointerCount();//  手指觸碰量

for(int index = 0; index< Point_Count ; ++index)

{

if (action != MotionEvent.ACTION_MOVE && index != Point_index)

continue;

if(action == MotionEvent.ACTION_DOWN ||

action == MotionEvent.ACTION_POINTER_DOWN)

Point_Action[index] = ACTION_DOWN;// 按下

else

if(action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_POINTER_UP ||

action == MotionEvent.ACTION_CANCEL)

Point_Action[index] = ACTION_UP;// 鬆開

else

if(action == MotionEvent.ACTION_MOVE)

Point_Action[index] = ACTION_DRAGGED;// 拖動

//讀取指定手指座並計算遊戲屏幕座標

Point_X[index] = (int)(event.getX(index)*Scale_X);

Point_Y[index] = (int)(event.getY(index)*Sacle_Y);

++index;

}

}

Android遊戲之LogCat調試日誌系統

Android遊戲之LogCat調試日誌系統

APP調試最常用是『斷點調試』(break).但如果想睇大量數據變化則力有不逮.可以通Log()不斷把數據輸出到LogCat窗口以此觀察數據變化.通過『View->Tool Windows->Logcat』顯示Logcat窗口.更可以通過濾器隨時顯示5種特定信息.

函式 顏色 簡介
Log.d(String tag, String msg); 藍色 代表debug調試
Log.v(String tag, String msg); 黑色 代表verbose囉嗦之意.一般信息輸出
Log.i(String tag, String msg); 綠色 代表information提示性消息
Log.w(String tag, String msg); 藍色 代表warning警告信息
Log.e(String tag, String msg); 紅色 代表error錯誤消息

 

Android遊戲之Activity模板

Android遊戲之Activity模板

Android是Linux系統.於其上運行APP都是Linux進程.Android APP沒有main入口函式.所以Android系統啟動APP時以Main Activity作為APP入口點.要理解Android APP運行需理解Activity生命週期.睇上圖左邊從Create到Destroyed完成整個生命週期.

Activity.onCreate():在創建時Activity加載界面並分配資源,如讀取音頻和3D模型.只會在啟動時被調用一次.

Activity.onStart():在onCreate()之後被調用或onRestart()之後調用

Activity.onRestart():用於重置Activity在onStop()之後被調用.

Activity.onResume():用於恢復(總會被調用)Activity在onStart()之後被調用或onPause()之後被調用

Activity.onPause():暫停(總會被調用).當Activity進入後臺時被調用

Activity.onStop():停止.當APP進入後臺時被調用.在onPause()之後調用.

Activity.onDestroy():銷毀.當APP退出時調用.若調用onPause()或onStop()之後onDestroy()將不會被調用.

為讓遊戲結構變得簡單.只需重寫onCreate()、onResume()和onPause()三個函式.下面是用於遊戲開發MainActivity.java模板

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

// 初此遊戲資源

super.onCreate(savedInstanceState); //創建Activity時調用

setContentView(R.layout.main); // 把佈局填充給Activity

}

@Override

protected void onResume() {

// 恢復遊戲線程

super.onResume();

}

@Override

protected void onPause() {

// 暫停遊戲線程

super.onPause();

}

}

當你生成MainActivity.java文檔後需要在AndroidManifest.xml文檔中聲明為入口Activity

<activity android:name=”.MainActivity” android:label=”@string/app_name”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

Android Studio啟動Android應用

Android Studio啟動Android應用Debug
Android Studio啟動Android應用Variableh
Android Studio啟動Android應用
Android Studio啟動Android應用ADB
Android Studio啟動Android應用ADB

創建Android應用項目需要在手機上啟動.

  1. 對項目進行編譯工具欄Build/Mak Project
  2. 將手機與電腦連接(USB線)
  3. 設定/更多設定/開發者選項
  4. 啟動『開啟開發人員選項』
  5. 啟動『USB調試』連接USB後啟用偵錯模式
  6. 啟動『USB安裝』允許通過USB安裝應用
  7. 啟動『USB調試』(安全設定)允許通過USB調試修改權限或虛擬觸碰
  8. 編輯配置文檔Run->Edit Configurations->app->General
  9. Module選app
  10. Deploy選Default APK
  11. Launch選Default Activity
  12. Target選USB Device
  13. 在手機上啟動工具欄Run->Run

 

未能正確在手機上調試APP多為ADB(Android Debug Bridge)未正確安裝

  1. 下載Android SDK Platform-Tools(包含EXE)
  2. 系統『Path』變數必須需正確設置. 『%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools;』
  3. 下載並正確安裝Google USB Driver.這點很重要很多時就是Android ADB Interface驅動無正確安裝.
  4. 『系統內容->硬體->裝置管理員->ADB Interface』若有嘆號則代表驅動未正確安裝
  5. 下載驅去頁Get the Google USB Driver 最新驅動Download the Google USB driver下載後解壓縮到D:\Android\usb_driver
  6. 最後強制安裝驅動.『系統內容->硬體->裝置管理員->ADB Interface->更新驅動程式->瀏覽電腦上驅動程式軟體(手動尋找並安裝驅動程式軟體)->讓我從電腦上裝置取動程式清單中挑選(此清單會顯示已安裝並且與裝置相容驅動程式軟體,以及與裝置屬￿同類別所有驅動程式軟體)->從磁片安裝->打開(USB drect)->選擇(Android ADB Interface)』

 

若ADB依然未能正確運行可嘗試手動重啟.win+r輸入CMD啟動命令行窗口

1.停止ADB服務

adb kill-server

2.啟動ADB服務

adb start-server

3.查看目前連接Android 設備

adb devices

Android Studio新建Android項目

Android Studio新建Android項目
Android Studio新建Android項目SDK
Android Studio新建Android項目Activity
Android Studio新建Android項目Build

Android Studio新建Project『項目』與Eclipse類似.下面將創建空項目用於遊戲製作.

  1. 啟動Android Studio
  2. 點擊Start a new Android Studio project
  3. Application name應用名使用全小寫如aa
  4. Company domain公司域名如net
  5. Project location工程目錄路徑D:\Android\aa
  6. Package name包名建議使用反向域名語法創建包名.『應用名+域名』以防Java類名衝突.按Edit輸入『bookcard.aa』
  7. 單擊NEXT選擇生成應用設備類型和SDK版本.設備類型有『手機』『平板』『穿戴設備』『電視』『汽車』『物聯網』
  8. 選擇Android SDK最新版本是API-28甘樣只有最新版本Android設備才能運行.而且Android向前(舊版本)兼容.所以你應儘量使用低版本SDK依度選擇API-14 Andorid4.0舊設備已經很難在市面見到.按Help me choose可查看Android各板本市場佔有率
  9. 安卓即時應用程序『Include Android Instant app support』無需勾選
  10. 單擊NEXT選擇模板.這裡選擇Empty Activity不含用戶界面只創建XML佈局
  11. 單擊NEXT生成主Activity文件.『Activity Name』填MainActivity 勾選『Generate Layout File』生成佈局文檔
  12. 『Layout Name』佈局名填main勾選Backwards Compatibility(AppCompat) 『向兼容』
  13. 最後單擊Finish生成Project

Android Studio導入Eclipse項目

Android Studio導入Eclipse項目
Android Studio導入Eclipse項目

雖然Eclipse ADT和Android Studio有不同目錄結構與配置.但可通過其自帶『Import Project』完成工程轉換

  1. 使用『Import Project(Gradle,Eclipse ADT,etc.)』導入Eclipse項目
  2. 選擇Eclipse項目目錄路徑『Select Eclipse or Gradle Project to Import』如G:\Android\aa
  3. 選擇導入目標目錄路徑『Import Destination Directory』如D:\Android\aa
  4. 若目錄不存在則生成『Directory Does Not Exist』
  5. 勾選Replace jars with dependencise,when possible『替換附屬jars』
  6. 勾選Replace library sources with dependencies,when possible『替換附屬Lib文檔』
  7. 勾選Create Gradle-style(camelCase)module names『創建漸變風格模塊名稱』
  8. 如果出現項目無法轉換 如properties Library reference .. could not be found只需要編輯project.properties刪掉無穩到Library即可
  9. 按Finish轉換項目

Android Studio下載與安裝

安裝JAVA
安裝Android Studio
安裝Android Studio
安裝Android Studio
Android Studio下載與安裝Path

以前開發Android遊戲一直使用Eclipse ADT插件進行.因為換左台新電腦覺得是時候轉入『Android Studio』懷抱.只因它是Google親生仔.而且已中止為Eclipse ADT插件和Android Ant編譯系統更新開發.而且Android Studio比起Eclipse啟動速度快很多(因為無需載入所有項目)

安裝JAVA

  1. 首先要下載Java SE(Standard Edition) Development Kit簡稱(JDK) 下載jdk-10.0.1_windows-x64_bin.exe(JDK10只有x64版本)
  2. 單擊開此安裝JDK,勾選『Public JRE』和『Development Tools』、『Source Code』
  3. 安裝路徑選擇默認即可C:\Program Files\Java\jdk-10.0.1\
  4. 為JAVA設定Windows環境路徑.按『Win + Pause Break』鍵打開『系統』/進階系統設定/環境變量/系統變數(S)/
  5. 按『新增』系統變數.『變數名稱』輸入JAVA_HOME『變數值』輸入JAVA安裝路徑『C:\Program Files\Java\jdk-10.0.1\』按『確定』保存
  6. 編輯『Path』系統變數追加『%JAVA_HOME%\bin;』 按『確定』保存

 

安裝Android Studio

  1. 打開下載 Android Studio頁面. Windows有x64安裝版android-studio-ide-173.4819257-windows.exe
  2. 單擊安裝Android Studio 並且確保電腦能連結互聯網.因為安裝過程需要下載所需文檔
  3. 構選Android Virtual Device
  4. 安裝路徑選擇默認即可C:\Program Files\Android\Android Studio
  5. 首次啟動會睇到Complete Installation對話框.因為是全新安裝勾選Do not import settings不導入設置
  6. 第一次啟動時若出現『Unable to access Android SDK add-on list』因為沒有安裝Android SDK.按Cancel後逐安裝
  7. 介面有Intellij和Darcula可選
  8. 勾選Android Virtual Device和Performance(Intel HAXM)HAXM是虛擬引擎加強運行x86 Android虛擬機表現
  9. 最後下載Android SDK完成安裝.要保正常連結com
  10. 為Android Studio設定Windows環境路徑.按『Win + Pause Break』鍵打開『系統』/進階系統設定/環境變量/系統變數(S)/
  11. 按『新增』系統變數.『變數名稱』輸入ANDROID_HOME『變數值』輸入Android SDK安裝路徑如『C:\Android\Sdk』. 按『確定』保存.如果你唔知SDK安裝路徑可同過『FILE->Other Settings->Default Project Structure->Android SDK location』睇到
  12. 編輯『Path』系統變數追加『%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools;』 按『確定』保存
  13. 啟動Android Studio
  14. 下載『SDK Tools』按『File->Settings->Android SDK->SDK Tools』
  15. 勾選Android SDK Build-Tools
  16. 勾選Android SDK Platform-Tools
  17. 勾選Android SDK Tools
  18. 勾選Google Play APK Expansion library
  19. 勾選Google Play Licensing library
  20. 勾選Google Play services
  21. 勾選Google USB Driver
  22. 勾選Google WEB Driver
  23. 按Apply自動下載安裝

 

若在手機上調試APP必須正確安裝ADB

  1. 下載Android SDK Platform-Tools
  2. 系統『path』變數必須需正確設置.
  3. 下載並正確安裝Google USB Driver.
  4. adb.exe devices 查看是否正確連接手機

 

 

FPS第一人稱視角射擊遊戲

是時候將之前所學OpenGL/DirectX知識粘合成一完整遊戲.最簡單是『第一人稱視角射擊遊戲』FPS(First-person Shooter). 通過控制『準星』描准『怪物』按滑鼠左鍵發射『飛彈』.若擊中便會『爆炸』並擊殺『怪物』,其實它會在另地在生.若擊中地面則會『爆炸』.按鍵盤上下鍵在山地中前後移動.若近距離接近『怪物』它會另轉面自動走開(AI部分). 所有『怪物』與『飛彈』都是MD2模型.按ESC鍵退出遊戲.下載FPS:

下面所需要文章與代碼:

  1. AI之行走
  2. 準星
  3. 粒子爆炸特效
  4. 邊界球
  5. MD2文檔讀取與解析
  6. WAV音檔分釋與讀取
  7. DirectSound3D
  8. DirectInput之查詢滑鼠
  9. DirectInput之查詢鍵盤
  10. 粒子系統
  11. 廣告牌
  12. 紋理地形
  13. 天幕
  14. 紋理映射

AI之行走

AI之行走

AI也就是『人工智能』在遊戲中實現多數使用『狀態機』.通過定義大量狀態.然後通過條件判斷而切換當前狀態.從而實現對外界作出反應. 當『怪物』它會觀察四周.當你接近它時會另轉面走開.

首先定義下面幾個狀態

#define AI_IDLE       0 // 站立

#define AI_RUN        1 // 奔跑

#define AI_DEATH      3// 死亡

初此AI狀態變量設為站立

int ai = AI_IDLE;

『狀態機』判斷結構

if (ai == AI_IDLE) // 站立

{

}

else

if (ai == AI_RUN)// 奔跑

{

}

else

if (ai == AI_DEATH)// 死亡

{

}

 

站立轉身走開AI代碼

1.首先計算兩者距離

VECTOR3D distance = pos – player.pos;

length = Length_VECTOR3D(&distance);

2.計算『怪物』與『玩加』 矢量之間夾角

Normalize_VECTOR3D(&distance);// 長度歸一

VECTOR3D v;

Init_VECTOR3D(&v, 0, 0, -1);

angle = RAD_TO_DEG(Angle_VECTOR3D(&distance, &v0));// 角度

2.若距離小於10米則會轉身離開

if (length < 10  ){

ai = AI_RUN;// 奔跑

rot.y = (angle – 90) + ((rand() % 90) – 45);// 轉身走開.有45度隨機角度

}

 

OpenGL之準星

OpenGL之準星
準星紋理

射擊遊戲在屏幕中畫有『準星』用於射擊怪獸.因為『準星』固定在屏幕中心.當你移動滑鼠時.相機視角也隨之改變.而當你要移動射擊位置可通過按鍵盤上下鍵

移動滑鼠 響應 角度
左移 相機視角繞Y軸左轉 if (y >= 360.0f) y = 0.0f;
右移 相機視角繞Y軸右轉 if (y <= -360.0f) y = 0.0f;
上移 相機視角繞Z軸右轉 if (z > 60.0f) z = 60.0f;
下移 相機視角繞Z軸左轉 if (z < -60.0f) z = -60.0f;

 

鍵盤鍵 響應 位置與角度
上鍵 位置前移 x = x – (speed * cos(DEG_TO_RAD(y)));

z = z – (speed * sin(DEG_TO_RAD(y)));

下鍵 位置後移
左鍵 相機視角繞Y軸左轉 if (y >= 360.0f) y = 0.0f;
右鍵 相機視角繞Y軸右轉 if (y <= -360.0f) y = 0.0f;

更新相機位置與角度

1.旋轉角度 deltaTime為時間間隔

rot.x = rot.x + dx * 60 *deltaTime;

rot.y = rot.y + dy * 60 *deltaTime;

rot.z = rot.z + dz * 60 *deltaTime;

2.限制視口角度否則會上下搖恍

if ( rot.y >= 360.0f || rot.y <= -360.0f)

player->rot.y = 0.0f;

if (player->rot.z > 60.0f)

player->rot.z = 60.0f;

if (player->rot.z < -60.0f)

player->rot.z = -60.0f;

3.角度轉弧度

float radian = 3.141592654f * player->rot.y / 180.0f;

計算移動速度

4.float speed = dx * 3 *deltaTime;

5.計算位置

pos.x = (float)(pos.x – (speed * cos(radian)));

pos.z = (float)(pos.z – (speed * sin(radian)));

radian = 3.141592654f * rot.z / 180.0f; // 弧度

pos.y = (float)(pos.y – (speed * sin(radian)));

6.UVN相機模型

LookAt_Camera3D();

最後製作準星紋理

  1. 繪製白色準星
  2. 背景色填充為黑色
  3. 模色設為『索引色』256色
  4. 設定透面色為黑色glAlphaFunc(GL_GREATER, 0);

OpenGL之粒子爆炸特效

OpenGL之粒子爆炸特效

遊戲中最常見『爆炸』特效.最常見做法是使用粒子系統模擬『爆炸』.你大約需要:

  1. 黑底白圓位圖『bmp』如上圖
  2. 爆炸特效音頻『wav』
  3. 粒子系統
  4. 廣告牌

其實所有『爆炸』特效都是圓心紋理位圖.只是它使用『廣告牌』技術讓其正對著你即『相機』. 渲染為紅色並讓其隨時間『下墜』『變暗』『縮小』直至消亡.之前模擬『飄雪』特效時使用三級系統.這次我將粒子結構大大簡化.其代碼我都在模擬『爆炸』特效中實現.W

粒子結構體

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D pos_prev;// 之前位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float    energy;// 生命週期(秒)

float size;// 尺寸

float size_delta;//增量

float weight;// 重量

float wdight_delta;// 重量增量

float color[4];// 顏色

float color_delta[4];// 顏色增量

}PARTICLE,*PARTICLE_PTR;

爆炸特效結構體

typedef struct EXPLOSION_TPY {

int          state;//  狀態

PARTICLE_PTR array;// 數組

int          count;// 粒子數量/爆炸量

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     variation;// 速度變量

VECTOR3D     acceleration;// 加速度

float        energy;// 生命週期(秒)

float        size;// 尺寸 5.0f

float        size_variation ;// 尺寸變量 2.0f

float        spread;// 爆炸傳播範圍

float        color[4];// 顏色

VECTOR2D     texture_coord[4];// 紋理座標

}EXPLOSION,*EXPLOSION_PTR;

生成『爆炸』特效函式

count:爆炸量大約1-10即可.

pos:位置

spread: 爆炸傳播範圍 0.1即可

void Build_Explosion(EXPLOSION_PTR explosion,int count,VECTOR3D_PTR pos,float spread){

explosion->state = MODEL3D_STATE_ACTIVE;

explosion->count = count; // 粒子數量

explosion->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

explosion->spread = spread;// 傳播

explosion->size           = 5.0f;// 尺寸

explosion->size_variation = 2.0f;// 尺寸變量

explosion->energy = 1.5f + FRAND_RANGE1() / 2.0f;// 生命週期(秒)

Init_VECTOR3D(&explosion->origin, pos->x, pos->y, pos->z);// 源點

Init_VECTOR3D(&explosion->velocity, 0.0f, 2.0f, 0.0f);// 速度

Init_VECTOR3D(&explosion->variation, 4.0f, 4.0f, 4.0f);// 變量

Init_VECTOR3D(&explosion->acceleration, 0.0f, -5.0f, 0.0f);// 加速度

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i){

particle = &explosion->array[i];// 粒子

Buid_Explosion(explosion, particle);//生成粒子『爆炸』特效

}

Set_Pos_DirectSound(&explosion_sound3D, pos->x, pos->y, pos->z);// 設置音源位置

Play_DirectSound(&explosion_sound3D, false);//播放音頻數據

}

生成爆炸粒子函式

void Buid_Explosion(EXPLOSION_PTR explosion, PARTICLE_PTR  particle){

// 在發射區隨機位置生成粒子

particle->pos.x = explosion->origin.x + FRAND_RANGE1() * explosion->spread;// 傳播

particle->pos.y = explosion->origin.y + FRAND_RANGE1() * explosion->spread;

particle->pos.z = explosion->origin.z + FRAND_RANGE1() * explosion->spread;

//給粒子隨機速度

particle->velocity.x += FRAND_RANGE1() * explosion->velocity.x;

particle->velocity.y += FRAND_RANGE1() * explosion->velocity.y;

particle->velocity.z += FRAND_RANGE1() * explosion->velocity.z;

// 加速度

particle->acceleration = explosion->acceleration;

// 生命週期

particle->energy = 1.5f + FRAND_RANGE1() / 2.0f;

// 顏色

particle->color[0] = 1.0f;

particle->color[1] = 0.5f + FRAND_RANGE1() * 0.5f;

particle->color[2] = 0.0f;

particle->color[3] = 1.0f;

// 顏色增量

particle->color_delta[0] = 0.0f;

particle->color_delta[1] = -(particle->color[1] / 2.0f) / particle->energy;

particle->color_delta[2] = 0.0f;

particle->color_delta[3] = -1.0f / particle->energy;

// 設置粒子的大小

particle->size = explosion->size + FRAND_RANGE1() * explosion->size_variation;

particle->size_delta = -particle->size / particle->energy;

}

釋放『爆炸』特效函式

void Free_Explosion(EXPLOSION_PTR explosion){

if (explosion->array != NULL)

free(explosion->array);

explosion->state = MODEL3D_STATE_NULL;

memset(explosion, 0, sizeof(EXPLOSION));// 清空

}

更新『爆炸』特效函式

deltaTime:時間間隔

void Update_Explosion(EXPLOSION_PTR explosion, float deltaTime){

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < explosion->count; ){

particle = &explosion->array[i];// 粒子

// 基於時間和速度更新粒子的位置

particle->pos = particle->pos + particle->velocity * deltaTime;

particle->velocity = particle->velocity + particle->acceleration * deltaTime;

particle->energy = particle->energy – deltaTime;

particle->size += particle->size_delta * deltaTime;

particle->color[3] += particle->color_delta[3] * deltaTime;

particle->color[1] += particle->color_delta[1] * deltaTime;

// 將最後一個粒子移動到當前位置,並減少計數.

if (particle->energy <= 0.0)

explosion->array[i] = explosion->array[–explosion->count];

else

++i;

}

if (explosion->count == 0)

Free_Explosion(explosion); // 釋放

}

渲染『爆炸』特效紋理函式

void Render_Explosion2(EXPLOSION_PTR explosion, BILLBOARD_PTR billboard){

float viewMatrix[16];

VECTOR3D right, up, pos;

GLfloat size;

right = billboard->right;

up = billboard->up;

// 壓入當前屬性

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, explosion_texture.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glBegin(GL_QUADS);

for (int i = 0; i < explosion->count; ++i){

PARTICLE_PTR particle = &explosion->array[i];// 粒子

size = particle->size / 2;

pos = particle->pos;

glColor4fv(particle->color);

glTexCoord2f(0.0, 0.0); glVertex3fv((pos + (right + up) * -size).M);

glTexCoord2f(1.0, 0.0); glVertex3fv((pos + (right – up) * size).M);

glTexCoord2f(1.0, 1.0); glVertex3fv((pos + (right + up) * size).M);

glTexCoord2f(0.0, 1.0); glVertex3fv((pos + (up – right) * size).M);

}

glEnd();

glDisable(GL_TEXTURE_2D);

glDisable(GL_BLEND);

glEnable(GL_DEPTH_TEST);

glPopAttrib();// 彈出當前屬性

}

OpenGL之曲棍球

OpenGL之曲棍球

『曲棍球』是碰撞算法最好演示,在遊戲中『球臺』寬300長500,並且有四條圍邊,確保『曲棍球』在『球臺』圍邊範圍內移動. 『球臺』會給『曲棍球』帶來『磨擦』後慢慢慢落來並最後停低.而『玩家球』則通過鼠標移動控制.通過與『曲棍球』相碰給『曲棍球』帶來動力.演示程式:下載.我會給出遊戲設計重點,並給出關鍵代碼

1.『冰球』運動核心是碰撞算法.你試想下當『冰球』高速運動時有可能會穿越圍邊(圍邊使用平面定義).所以你需要準確計算碰撞時間.碰撞代碼睇『碰撞時間

2.控制『玩家角色』通過獲取滑鼠標偏移量與位置相加移動『玩家角色』.你需要通過DirectInput獲得滑鼠偏移量. 並且確保不要移出球臺圍邊.若與『冰球』則重新計算返射方向與速度

根據鼠標運動計算速度::Init_VECTOR3D(&player->velocity, diffX, 0, diffY);

計算位置player->position = player->position + player->velocity;

『玩家角色』與『冰球』碰撞使用『邊界球』進行檢測並重新計算返射方向與速度

puck->velocity = Reflection_VECTOR3D(&puck->velocity,&(puck->velocity ^ player->velocity)) + player->velocity*500;

3.設定『球臺』結構與『圍邊平面

typedef struct TABLE_TPY {

TEXTURE   texture;// 球臺紋理

VECTOR3D  position;// 球臺位置

VECTOR3D  corner[4];//球臺角落

PLANE3D    wall[4];// 球臺圍邊平面

float width;// 寬度

float depth;// 深度

float height;// 高度

}TABLE,*TABLE_PTR;

球臺寬300*長500載入紋理Load_File_Texture()綁定紋理Bind_Image_Texture().圍邊使用Init_PLANE3D()定義為平面

4.定義『冰球』結構

typedef struct PUCK_TPY {

VECTOR3D     position;// 位置

VECTOR3D  acceleration;// 加速度

VECTOR3D  velocity;// 速度

float     radius;// 半徑

}PUCK,*PUCK_PTR;

5.定義『玩家角色』結構

typedef struct PLAYER_TPY {

VECTOR3D  position;// 位置

VECTOR3D  velocity;// 速度

float     radius;// 半徑

float     mouseX, mouseY;

}PLAYER,*PLAYER_PTR;

 

 

遊戲建模之碰撞時間

遊戲建模之碰撞時間

『物體』運動核心是碰撞.你試想下當『物體』高速運動時有可能會穿越牆壁.這因遊戲世界中CPU會輪詢處理所有『物體』. 並只會分到有限『運算/時間』,所以每一次你都需要準確計算碰撞時間.通過不斷遞歸而確定最後移動位置

通過使用基於時間二維方程式進行碰撞檢測.

xf=x0+v0t+(1/2)at2

ax2+bx+c=0

利用係數替換可得到

a=(1/2)*a

b=v0

c=x0-xf

將其擴展可得到三維方程式

xt=cx+bxt+axt2

yt=cy+byt+ayt2

zt=cz+bzt+azt2

如果將三維方程應用於平面方程可得

Axt+Byt+C*zt+D=0

此方程說面如果物體與平面發生碰撞.其碰撞點位於(xt,yt,zt)通過代數運算可得到

(Aax+Bay+Caz)x2+(Abx+Bby+Cbz)x+(Acx+Bcy+Ccz)+D=0

這其實就是平面二次方程式(ax2+bx+c=0) ,其點積形式為:

ax2=(Aax+Bay+Caz)x2=N•(1/2)A

利用點積得到二次方程係數:

加速度係數: a=N•(0.5A)

速度係數:  b=N•V

距離係數:  c=N•X+D

有上序二次方程係數就可計算碰撞時間

float a = n % (acceleration * 0.5f);// 加速度係數

float b = n % velocity;// 速度係數

float c = n % position + D – radius;// 距離係數

若a=0加速度係數為零,碰撞時間等於距離係數除以速度係數.因為距離係數小於零所以要去負值.

collisionTime = -c/b;

若a!=≠0加速度係數不為零,可以使用二次方程計算碰撞時間:

x=(-b+√(b2-4ac) ) / 2a

在計算碰撞時間之前可先計算D=b2-4ac 若D小於零則無解.若D大於零則有解

float D = b * b – 4 * a*c;

if (D > 0) // 如果判斷式大於等於零

collisionTime = (-b – sqrtf(D)) / (2 * a); // 計算碰撞時間

因為發生碰撞物體會彈開,方向矢量產生返射需要重新計算位置.通過減去碰撞時間進行遞歸.直到『幀時間』為零

 

下面是『曲棍球』碰撞函式.

puck 是曲棍球.

table 是球臺

deltaTime是幀時間

void Update_Puck(PUCK_PTR puck, TABLE_PTR table, float deltaTime){

float  fastestTime = deltaTime;// 最早碰撞時間

float  collisionTime;// 碰撞時間

PLANE3D_PTR plane          = NULL;// 平面

PLANE3D_PTR planeCollision = NULL;// 碰撞平面

if (deltaTime <= 0.000f)// 遞歸

return;

// 對球臺四個圍邊檢查碰撞

for (int i = 0; i < 4; i++){

plane = &table->wall[i];// 平面

// 點積二次方程

float a = plane->n % (puck->acceleration * 0.5f);// 加速度係數

float b = plane->n % puck->velocity;// 速度係數

float c = plane->n % puck->position + plane->D – puck->radius;// 距離係數

if (a == 0 && b != 0 && c != 0){// 如果無加速度

// 碰撞時間等於距離除以速度

collisionTime = -c/b;// 碰撞時間

if (collisionTime >= 0 && collisionTime < fastestTime){// 發生碰撞

fastestTime = collisionTime;// 保存碰撞時間

planeCollision = plane;// 平面

}

}

else

if(a != 0){

// 加速度a不等於零

// 計算判別式

float D = b * b – 4 * a*c;

if (D > 0) {// 如果判斷式大於等於零

// 計算碰撞時間

collisionTime = (-b – sqrtf(D)) / (2 * a);

if (collisionTime >= 0.0f && collisionTime < fastestTime) {// 發生碰撞

fastestTime = collisionTime;

planeCollision = plane;

}

}

}

}// END FOR

// 速度設上限每秒800米

if (Length_VECTOR3D(&puck->velocity) > 800)

puck->velocity = Normalize_VECTOR3D(&puck->velocity) * 800;

// 如果冰球正移動,則應用磨擦力

if (Length_VECTOR3D(&puck->velocity) > 0)

puck->acceleration = -puck->velocity * 0.2f;// 計算加速度

// 計算當前位置

puck->position = puck->position + puck->velocity * fastestTime + puck->acceleration * (fastestTimefastestTime0.5f);

// 應用磨擦力

puck->velocity = puck->velocity + puck->acceleration * fastestTime;

// 如果發生碰撞,反轉速度

if (planeCollision != NULL)// 碰撞平面

puck->velocity = Reflection_VECTOR3D(&puck->velocity, &planeCollision->n);

// 遞歸調用

Update_Puck(puck, table, deltaTime – fastestTime);

}

遊戲建模之碰撞反應

遊戲建模之碰撞反應

若3D模型發生碰撞後需要計算碰撞反應,不通物體運動有不同碰撞反應.但物體多數以直線運動.物體彈回角度和碰撞角度相等.

入射角度:桌球運動方向與邊沿平面法線向量之間夾角.

反射角度:垂直於運動方向矢量

例:當桌球撞向邊沿.它將按撞擊角度與之對等『入射角度』彈開

方程並沒有考慮球體旋轉作用力與磨擦力.最終得到計算反射方向方程式

給定運動方向矢量I與垂直法線N求碰撞反射方向F

F = (I – N2 (I % N)) * | I |;

計算反射方向代碼,dir為射線方向,normal為碰撞面法線

VECTOR3D Reflection_VECTOR3D(VECTOR3D_PTR dir,VECTOR3D_PTR normal){

VECTOR3D vec ;

Normalize_VECTOR3D(dir, &vec); // 單為化方向向量

*dir = (vec – *normal * 2.0f * (vec % *normal)) * Length_VECTOR3D(dir);

return *dir;

}

遊戲建模-平面碰撞

遊戲建模-平面碰撞

平面是3D圖形學重要部分.平面有兩個重要概念.

1.3D平面都無窮遠延伸

2.所有平面都將整個空間分成兩個半空間.正半空間為法線指向空間,負半空間則是令一則空間.這個特性對於碰撞算法重要

平面描敘如下:

平面法線向量:n=(a,b,c)

平面任意點:p0=(x0,y0,z0)

平面任意點:p=(x,y,z)

平面與原點距離:d平面位移常量(plane-shift constant)

因為法線n與向量(p0->p)垂直. 因兩垂直向量點積為零:

n * (p0->p) = 0

轉為分量表示

(a,b,c)*(x-x0,y-y0,z-z0)=0

轉為『頂點』與『法線』表示

a(x-x0)+b(y-y0)+c*(z-z0)=0

ax+by+cz+(-ax0-by0-cz0)=0

令d=-ax0-by0-c*z0

『平面方程』如下:

ax+by+c*z+d=0

 

這足以定義平面結構

typedef struct PLANE3D_TYP{

VECTOR3D n;//平面法線向量(不必是單位向量)

float dist;//平面到原點最近距離

POINT3D p0;//平面上最近原點的點

}PLANE3D,*PLANE3D_PTR;

 

判斷『點』位於『平面』那個半空

3D世界中『視點』、『角色』並不能穿越牆壁.要做到這步需判斷點位於平正空間(法線指向)還是負空間.

要判斷這點需要下面的平面方程

hs=a(x-x0)+b(y-y0)+c*(z-z0)

只需將點(x,y,z)帶入方程中並計算結果

如果hs=0則該點位於平面之上

如果hs>0則該點位於平面正半空間(法線指向)中

如果hs<0則該點位於平面負半空間中

float Compute_Point_In_Plane3D(PLANE3D_PTR plane,VECTOR3D_PTR pt){

float hs = plane->A * pt->x + plane->B * pt->y + plane->C * pt->z + plane->D;

return(hs); // 返回半空間值

}

 

計算平面與直線之相交點(3D空間)

平面『頂點』與『法線』方程如下:

a(x-x0)+b(y-y0)+c*(z-z0)=0

3D直線方程如下:

(x-x0)/a = (y-y0)/b = (z-z0)/c

計算多邊形法線與直線方向點積

double a = plane->n % (line->v);

若為零則直線與平面平行

VECTOR3D Intersect;

if (a == 0)

Intersect = line->p0;

計算相交點

Intersect = line->p0 – (line->v) * ((plane->n % line->p0 + plane->D) / a);

3D線段與3D平面交點函式:

bool Intersect_Line3D_Plane3D(PLANE3D_PTR plane, PARMLINE3D_PTR line,POINT3D_PTR pt){

// 計算直線方向與多邊形法線點積

double a = plane->n % (line->v);

if (a == 0)

return false;

// 計算相交點

*pt = line->p0 – (line->v) * ((plane->n % line->p0 + plane->D) / a);

return true;

}

 

計算頂點是否為於多邊形之上,

首先計算頂點與多邊形所有頂點之間角度總和.如果該頂點位於多邊形之上.那麼此頂點與多邊形所有頂點之間角度總和將等於或接近2PI

bool Intersect_Vector3D_Polygon3D(VECTOR3D_PTR polygon, int num,VECTOR3D_PTR v){

VECTOR3D segment1, segment2;// 頂點 到 多邊形頂點 矢量

double length1, length2;// 矢量長度

double sumAngle = 0;// 矢量之間角度總和

double cosAngle = 0;// 兩矢量余弦角

// 編曆多邊形所有頂點

for (int index = 0; index < num; ++index)     {

segment1 = polygon[index] – *v;

segment2 = polygon[(index+1) % num] – *v; // % num 確保數值環形加一

length1 = Length_VECTOR3D(&segment1);

length2 = Length_VECTOR3D(&segment2);// 矢量長度

//檢查頂點是否落在多邊形邊界上

if ( length1 * length2 <= 0.0000001f){

// 多邊形邊界被認為是多邊形內部

sumAngle = PI2; // pai * 2

break;

}

// 計算上述兩個矢量之間余弦角

cosAngle = (segment1 % segment2) / (length1 * length2);

// 將計算結果累加入角度總和

sumAngle = sumAngle + acosf(cosAngle);

}

if ((sumAngle <= PI2 + 0.0000001f) && (sumAngle >= PI2 – 0.0000001f))

return true;// 頂點與多邊形發生碰撞

else

return false;

return true;

}

 

使用三個頂點初始化(定義)3D平面!

void Init_PLANE3D(PLANE3D_PTR plane,VECTOR3D_PTR va,VECTOR3D_PTR vb,VECTOR3D_PTR vc){

VECTOR3D normalA = *vc – *va;// 計算頂點C->A向量

VECTOR3D normalB = *vc – *vb;// 計算頂點C->B向量

plane->n = Cross_VECTOR3D(&normalA, &normalB);// 計算兩個3D向量叉積

plane->D = Dot_VECTOR3D(&(-(*va)), &plane->n); //距離等於va求返後與n求點積

}

遊戲建模-矢量運算

遊戲建模-矢量運算

矢量(VECTOR)也稱『向量』其實是抽象『量』它在遊戲世界被頂義為『位置』『速度』『磨擦』『方向』『點』等等. 矢量通常有3種

矢量 分量 簡序
2D矢量 x,y 2D空間
3D矢量 x,y,z 3D空間
4D矢量 x,y,z,w 3D空間w總是為1.用於方陣運算

首先定義3D矢量結構:

typedef struct VECTOR3D_TYP{

float x,y,z;

}VECTOR3D,*VECTOR3D_PTR;

下面是3D矢量(VECTOR)運算函式庫.

 

計算3D矢量長度,矢量長度也稱為範數(norm).將其理解為原點(0,0,0)到矢量(x,y,z)之距離

float Length_VECTOR3D(VECTOR3D_PTR va){

return( (float)sqrtf(va->xva->x + va->yva->y + va->z*va->z) );

}

 

3D矢量進行歸一化(normalize),也就使其長度縮放為1,但同時方向保持不變.它通常被用於無需理會長度之運算如『方向』

void Normalize_VECTOR3D(VECTOR3D_PTR va){

// 1.首先計算長度

float length = sqrtf(va->xva->x + va->yva->y + va->z*va->z);

//2.矢量除以長度得到歸一化矢量

va->x= va->x/length;

va->y= va->y/length;

va->z= va->z/length;

}

 

3D矢量點積運算可以理解為矢量乘法.先將各分量相乘後再相加得到一個標量

float operator%(VECTOR3D va, VECTOR3D vb){

return((va.x * vb.x) + (va.y * vb.y) + (va.z * vb.z));

}

float Dot_VECTOR3D(VECTOR3D_PTR va, VECTOR3D_PTR vb){

return( (va->x * vb->x) + (va->y * vb->y) + (va->z * vb->z) );

}

叉積是另一種矢量乘法,叉積運算最小要有3個分量才有效.

VECTOR3D operator^(VECTOR3D va, VECTOR3D vb){

VECTOR3D vn;

vn.x = ((va.y * vb.z) – (va.z * vb.y));

vn.y = -((va.x * vb.z) – (va.z * vb.x));

vn.z = ((va.x * vb.y) – (va.y * vb.x));

return(vn);

}

VECTOR3D Cross_VECTOR3D(VECTOR3D_PTR va, VECTOR3D_PTR vb){

VECTOR3D vn;

vn.x =  ( (va->y * vb->z) – (va->z * vb->y) );

vn.y = -( (va->x * vb->z) – (va->z * vb->x) );

vn.z =  ( (va->x * vb->y) – (va->y * vb->x) );

return(vn);

}

 

計算兩個3D矢量va和vb之間夾角余弦值

float CosTh_VECTOR3D(VECTOR3D_PTR va, VECTOR3D_PTR vb){

return(Dot_VECTOR3D(va,vb)/(Length_VECTOR3D(va)*Length_VECTOR3D(vb)));

}

 

 

計算三角形法線

void Normal_VECTOR3D(VECTOR3D_PTR normal,VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vc){

VECTOR3D u, v, n;

float length;

u = *vb – *va;

v = *vc – *va;

n = u^v;//Cross_VECTOR3D(&u, &v, &n);// 計算叉積

length = sqrtf(n.xn.x + n.yn.y + n.z*n.z);

normal->x = n.x/length;

normal->y = n.y/length;

normal->z = n.z/length;

}

 

將兩個3D矢量相加(va + vb),如用於位置移動

VECTOR3D operator+(VECTOR3D va, VECTOR3D vb){

VECTOR3D vsum;

vsum.x = va.x + vb.x;

vsum.y = va.y + vb.y;

vsum.z = va.z + vb.z;

return (vsum);//返回相加結果!

}

 

將兩個3D矢量相減(va – vb),如用於位置移動

VECTOR3D operator-(VECTOR3D va, VECTOR3D vb){

VECTOR3D vdiff;

vdiff.x = va.x – vb.x;

vdiff.y = va.y – vb.y;

vdiff.z = va.z – vb.z;

return(vdiff);     //返回相減向量!

}

 

3D矢量反數,如返轉方向

VECTOR3D operator-(VECTOR3D v){

VECTOR3D negation;

negation.x = -v.x ;

negation.y = -v.y ;

negation.z = -v.z ;

return(negation);     //返回反數向量!

}

 

使用縮放因子k對3D矢量進行縮放如:位置=位置+速度*時間

VECTOR3D operator*(VECTOR3D va, float k){

VECTOR3D vscaled;

vscaled.x = k * va.x;

vscaled.y = k * va.y;

vscaled.z = k * va.z;

return vscaled;// 返回縮放後向量

}

 

3D矢量賦值

void Init_VECTOR3D(VECTOR3D_PTR v, float x,float y,float z) {

v->x = x;  v->y = y; v->z = z;

}

 

3D矢量拷貝

Copy_VECTOR3D(VECTOR3D_PTR vdst, VECTOR3D_PTR vsrc){

vdst->x = vsrc->x;  vdst->y = vsrc->y; vdst->z = vsrc->z;

}

 

3D矢量比較

bool operator==(VECTOR3D vdst, VECTOR3D vsrc){

if (vdst.x == vsrc.x && vdst.y == vsrc.y && vdst.z == vsrc.z)

return true;

else

return false;

}

 

3D向量不等比較

bool operator!=(VECTOR3D vdst, VECTOR3D vsrc)

{

if (vdst.x != vsrc.x ||         vdst.y != vsrc.y ||vdst.z != vsrc.z)

return true;

else

return false;

}

 

向量歸零(3D向量)無方向,無距離,代表位於原點

void Zero_VECTOR3D(VECTOR3D_PTR v) {

v->x = v->y = v->z = 0.0f;

}

計算兩矢量之間夾角

float Angle_VECTOR3D(VECTOR3D_PTR va, VECTOR3D_PTR vb){

return acosf(*va % *vb);

}

角度轉弧度

#define DEG_TO_RAD(ang) ((ang)*PI/180.0)

弧度轉角度

#define RAD_TO_DEG(rads) ((rads)*180.0/PI)

隨機數 x:下限,  y:上限.

#define RAND_RANGE(x,y) ( (x) + (rand()%((y)-(x)+1)))

隨機數: -1.0 ~ 1.0

#define FRAND_RANGE1()   (((float)rand()-(float)rand())/RAND_MAX)

隨機數 0~1

#define FRAND_RANGE() ((float)rand() / (float)RAND_MAX)

遊戲建模之邊界盒

遊戲建模之邊界盒

邊界球』雖然可解卻大部3D模型『碰撞測試』問題.單若3D模型是長條形則不適合如『牆體』『長劍』.『軸對齊坐標邊界盒』axis-aligned bounding box(AABB)引入則可解決這類問題.每個『邊界盒』均由3D模型『中心點』與『最遠點』、『最近點』所組成.通過遍歷每個3D模型頂點找出XYZ三軸上最遠頂點.定義3D邊界盒:

typedef struct AABB_TYP {

VECTOR3D center;// 中心點

VECTOR3D far;// 最遠

VECTOR3D near;// 最近

}AABB, *AABB_PTR;

 

『3D模型』最遠點與最近點可遍歷所有3D模型頂點獲得:

1.最遠點與最近點清零

VECTOR3D far = {0,0,0};// 最遠點

VECTOR3D near = {0,0,0};// 最近點

2.遍歷所有3D頂點

for (int index = 0; index < vertex_num; ++index){

3.XYZ三軸最遠點

if (far.x > vertex_array[index].x)

vertex_array[index].x = far.x;

if (far.y > vertex_array[index].y)

vertex_array[index].y = far.y;

if (far.z > vertex_array[index].z)

vertex_array[index].z = far.z;

4.XYZ三軸最近點

if (near.x > vertex_array[index].x)

vertex_array[index].x = near.x;

if (near.y > vertex_array[index].y)

vertex_array[index].y = near.y;

if (near.z > vertex_array[index].z)

vertex_array[index].z = near.z;

}

 

判斷頂點是否為於邊界盒

bool Compute_Point_In_AABB(AABB_PTR aabb, VECTOR3D_PTR point)

{

if ((point->x >= aabb->center.x + aabb->near.x && point->x <= aabb->center.x + aabb->far.x) &&

(point->y >= aabb->center.y + aabb->near.y && point->y <= aabb->center.y + aabb->far.y) &&

(point->z >= aabb->center.z + aabb->near.z && point->z <= aabb->center.z + aabb->far.z) )

return true;

return false;

}

遊戲建模之邊界球

遊戲建模之邊界球

在3D遊戲中常對『3D模型』進行多邊形『碰撞檢測』.例如武器擊中『牆體』或『怪物』.最容易最常用是『邊界球』進『碰撞檢測』.每個『邊界球』均由3D模型『中心點』與『半徑』. 這『半徑』並不一定是最長半徑,通常這個值只包裹核心部分.定義3D球體:

typedef struct SPHERE3D_TYP{

float    x, y, z;// 中心點

float    radius;// 球體半徑

}SPHERE3D,* SPHERE3D_PTR;

 

『3D模型』最大半徑與平均平徑可遍歷所有頂點而取得:

1.模型半徑前設為零

radius_avg = 0;// 平均半徑

radius_max = 0;// 最大半徑

2.遍歷3D模型所有頂點

for (int index = 0; index < vertex_num; ++index)

{

3.計算3D頂點與中心距離

float dist = (float)sqrt(vertex_array[index].x*vertex_array[index].x +

vertex_array[index].y*vertex_array[index].y +

vertex_array[index].z *vertex_array[index].z);

4.累加半徑

radius_avg = radius_avg + dist;

5.求得最大半徑

if (dist > radius_max)

radius_max = dist;

}

6.計算平均半徑

radius_avg = radius_avg / vertex_num;

 

要對兩『邊界球』進行『碰撞檢測』只需求得兩『邊界球』之距,然後與兩『邊界球』半徑之和進行比較:

bool Compute_Sphere3D_In_Sphere3D(SPHERE3D_PTR sphereA, SPHERE3D_PTR sphereB)

{1.計算兩頂點XYZ分量距離

float x = sphereA->x – sphereB->x;

float y = sphereA->y – sphereB->y;

float z = sphereA->z – sphereB->z;

2.球體距離

float dist = sqrtf(x*x + y * y + z * z);

3.半徑之和進行比較

if (dist < (sphereA->radius + sphereB->radius))

return true;// 球體重疊/碰撞

else

return false;//

}

Visual Studio之編譯宏定義

Visual Studio之編譯宏定義

在未有win64出現之前只需要編譯x86程式.但出現win64後需要分別編譯x86與x64兩個版本.你需要分開進行編譯.

Visual Studio分別為平臺生成全域宏:

x86下編譯定義_WIN32宏

x64下編譯定義_WIN64宏

#ifdef _WIN64

// x64平臺

#else

// x86平臺

#endif

 

Visual Studio調試模式生成全域宏:

調試模式下編譯定義_DEBUG宏

發行模式下編譯無_DEBUG宏定義

#ifdef  _DEBUG

//調試模式

#else

//發行模式

#endif

 

DirectX SDK下載與設定

DirectX SDK下載與設定

DirectX本是用於取替OpenGL給遊戲廠商使用.但遊戲廠商集體反抗.才另microsoft支持OpenGL.而且自DirectX6全面使用COM模型開發.DirectX是設計用於『影片』『聲音』『輸入』『網絡』抽象軟件界面結口.DirectX接口由microsoft定義.而底層則有驅動程式與硬件進行通信.開發者完全無需理會硬件之間差異.如果硬件不支持則由DirectX進行模擬.

  1. DirectX SDK 下載『DirectX Software Development Kit
  2. 下載『exe』運行後自動進行解壓.
  3. 其實你只需要『Lib』與『Include』這兩個『資料夾』.『Lib』下面分別有『x86』與『x64』
  4. 接下來讓工程生成『DirectX』目錄並複製『Lib』與『Include』下所有『頭文檔』與『庫文檔』
  5. 包含『頭文檔』

#include “..\DirectX\Include\ddraw.h”

#include “..\DirectX\Include\dinput.h”

#include “..\DirectX\Include\dsound.h”

  1. 包含『庫文檔』你編譯時需要分開x86與x64的LIB文檔

#ifdef  _WIN64

#pragma comment(lib, “DirectX\Lib\x64\ddraw.LIB”)

#pragma comment(lib, “DirectX\Lib\x64\dinput8.LIB”)

#pragma comment(lib, “DirectX\Lib\x64\dxguid.LIB”)

#pragma comment(lib, “DirectX\Lib\x64\dsound.LIB”)

#else

#pragma comment(lib, “DirectX\Lib\x86\ddraw.LIB”)

#pragma comment(lib, “DirectX\Lib\x86\dinput8.LIB”)

#pragma comment(lib, “DirectX\Lib\x86\dxguid.LIB”)

#pragma comment(lib, “DirectX\Lib\x86\dsound.LIB”)

#endif

 

上面方法是指定工程目錄.令外你還可制定Visual Studio搜索目錄:

  1. 設定『項目』『屬性』『VC++目錄』.然後分別設定『Include目錄』與『程式庫目錄』
  2. 『Include目錄』包含『D:\ DirectX\Include』.
  3. Win32平臺『程式庫目錄』包含『D:\DirectX\Lib\x64』
  4. Win64平臺『程式庫目錄』包含『D:\DirectX\Lib\x86』
  5. 在包含『頭文檔』與『庫文檔』時無需設定相對路徑

 

在DirectX8之前分別使用DirectSound和DirectMusic處理音頻播放. DirectSound用于處理聲波回放和捕足,而DirectMusic則是加載和播放所有聲音主要組件.但在DirectX8之後合平DirectXAudio組件.若你需要播放MIDI睇『DirectMusic之播放MIDI

 

 

 

MD2文檔讀取與解析

MD2文檔讀取與解析
MD2文檔讀取與解析
MD2文檔讀取與解析

.MD2文檔由美國id Software為其QuakeII開發3D模型.雖然它無骨架但它讀取簡單,而最重要是可以通過Internet穩到大量MD2文檔.根據模型規範.MD2格式最多含198動畫幀編號為0~197. 動畫幀將用於組成『步行』『功擊』『站立』『死亡』等動畫.MD2文檔 通常有下面幾個文檔組成:

文檔 簡述
TRIS.MD2 角色3D模型
WEAPON.MD2 武器3D模型
TRIS.PCX 角色皮膚也就是紋理圖檔,通常為8Bit『256色』長寬為256*256保存為.PCX格式
WEAPON.PCX 武器皮膚

 

MD2格式 簡述
Head 文檔頭部格式3D模型描述
Data 3D數據

 

MD2文檔頭部格式 MD2_HEADER 簡述
int id; MD2文檔標記’IDP2′,用於判斷是否MD2文檔
int version; 版本號總為8
int skin_width 皮膚紋理圖寬度.此值相對於紋理座標textcoord,通常為256
int skin_height; 皮膚紋理圖高度.此值相對於紋理座標textcoord,通常為256
int frame_size; 每幀字節總量
int skin_num; 皮膚紋理總量,忽略.
int vertex_num; 單幀頂點量.每幀字節量均相同.有些教科書搞錯左此值.
int textcoord_num; 紋理座標總量
int polygon_num; 多邊形總量
int command_num; OpenGL命令總量,忽略
int frame_num; 幀總量id softwarek規定為198幀編號為0~197.有些模型可能多於此值
int skin_offset; 皮膚pcx偏移量每個64字節.因包含QuakeII專用路徑.忽略.
int textcoord_offset; 紋理座標偏移量
int polygon_offset; 多邊形偏移量
int frame_offset; 幀偏移量
int command_offset; 命令偏移量忽略
int end_offset; 結尾偏移量.相當於文檔長度

MD2紋理座標通過(data + textcoord_offset)得到,結構如下:

typedef struct MD2_TEXTCOORD_TYP {

WORD u, v;

}MD2_TEXTCOORD,*MD2_TEXTCOORD_PTR;

 

MD2多邊形通過(data + polygon_offset)得到,保存頂點與紋理索引.結構如下:

typedef struct MD2_POLYGON_TYP {

WORD vertex_index[3];// 頂點索引

WORD textcoord_index[3];// 紋理座標索引

}MD2_POLYGON,*MD2_POLYGON_PTR;

 

MD2關鍵幀通過(data + frame_offset)得到,幀頭包含對頂點進行縮放與平移因子.頂點數組長度由vertex_num確定.結構如下:

typedef struct MD2_FRAME_TYP{

float scale[3];// 縮放

float translate[3];// 平移

char  name[16];// 動畫幀ASCII名

MD2_POINT list[1];// 頂點數組

}MD2_FRAME,*MD2_FRAME_PTR;

 

MD2頂點包含於關鍵幀裡,每個頂點由『xyz座標』與『法線索引』組成.『法線索引表』你需要自已構建.法線將在運行時計算所以忽略.結構如下:

typedef struct MD2_POINT_TYP {

BYTE v[3];// xyz頂點

BYTE noraml_index;// 頂點法線索引,此值忽略

}MD2_POINT,*MD2_POINT_PTR;

 

MD2動畫,用於控制模型動作速度,以秒為單位.結構如下:

typedef struct MD2_ANIMATION_TYP{

int   start;  // 起始幀索引

int   end;    // 結束幀索引

float irate;   // 插幀頻率(0.0f~1.0f),1.0f表示不插幀

float speed;   // 用於控制動畫播放時間,一般設為0~10秒

} MD2_ANIMATION, *MD2_ANIMATION_PTR;

動畫索引 動作名 幀索引 插幀頻率(幀) 播放時間(秒)
0 STANDING_IDLE站立 0-39 0.5f 4
1 RUN奔跑 40-45 0.5f 4
2 ATTACK開火/攻擊 46-53 0.5f 2
3 PAIN1 54-57 0.5f 4
4 PAIN2 58-61 0.5f 4
5 PAIN3 62-65 0.5f 4
6 JUMP跳躍 66-71 0.5f 5
7 FLIP手勢 72-83 0.5f 3
8 SALUTE  敬禮 84-94 0.5f 5
9 TAUNT 嘲笑 95-111 0.5f 5
10 WAVE 揮手致意 112-122 0.5f 5
11 POINT 指向它人 123-134 0.5f 5
12 CROUCH STAND   蹲伏-不動 135-153 0.5f 5
13 CROUCH WALK    蹲伏-行走 154-159 0.5f 5
14 CROUCH ATTACK  蹲伏-開火 160-168 0.5f 5
15 CROUCH_PAIN    蹲伏-被擊打 169-172 0.5f 5
16 CROUCH_DEATH  蹲伏-死亡 173-177 0.25f 5
17 DEATH_BACK    死亡-後趴 178-183 0.25f 5
18 DEATH_FORWARD 死亡-前趴 184-189 0.25f 5
19 DEATH_SLOW   緩慢死亡 190-197 0.25f 5

現在需要MD2結構用於保存3D模型數據

MD2模型數據結構 簡述
MD2_ANIMATION animation_array[20] 動畫,MD2通常有20種不同動作
int animation_num; 動畫總量
int frame_num; 幀量id softwarek規定198幀
MD2_POLYGON_PTR polygon_array; 多邊形列表
int polygon_num; 多邊形總量
VECTOR3D_PTR  vertex_array; 頂點數組 長度=幀總量*單幀頂點量
int vertex_num; 單幀頂點量
VECTOR2D_PTR textcoord_array; 紋理座標數組,只有一組,由所有幀共享
int textcoord_num; 紋理座標總量
TEXTURE texture_array[8] 紋理skin
int texture_num; 皮膚紋理圖總量
float radius_avg; 平均半徑
float radius_max; 最大半徑

在遊戲中低級單位都一至如步兵.需要單獨保存位置、面方、動畫等狀態.需要一個新結構對MD2進行封裝.以共享數據節約空間.並且為讓動畫流暢需要對關鍵幀進行插值(插入其它幀).

3D模型結構定義(MODEL3D) 簡述
int state; 狀態, 死亡/存活
int attr; 屬性
int color; 沒有紋理時使用顏色 RGB(255,255,255)
VECTOR3D pos; 位置
VECTOR3D rot; 旋轉
int anim; 動畫索引
bool  loop; 循環播放
float speed; 動畫速度,(0.0f~1.0f)數值越小速度越慢,數值越大速度越快
float frame; 當前幀索引
float irate; 插幀頻率(0.0f~1.0f),1.0f表示不插幀
float count; 插幀計數器
bool complete; 動畫完成播放標記
VECTOR3D_PTR vertex_array; 單幀頂點量
VECTOR3D_PTR normal_array; 法線數組
TEXTURE_PTR texture; 指向紋理
MD2_PTR md2; 指向MD2模型

在載入數據之前還需要定義幾個頂點控制

#define MD2_INVERT_X       0x0001// 反轉X軸

#define MD2_INVERT_Y       0x0002// 反轉Y軸

#define MD2_INVERT_Z       0x0004// 反轉Z軸

#define MD2_SWAP_XY        0x0010// 交換XY軸

#define MD2_SWAP_YZ        0x0020// 交換YZ軸

#define MD2_SWAP_XZ        0x0040// 交換XZ軸

#define MD2_INVERT_WINDING_ORDER  0x0100 // 反轉環繞順序

基本結構已定義可以讀取數據並分釋:

bool Load_Data_MD2(MD2_PTR md2, PBYTE data, int size,float scale,DWORD flag ){

1.讀取MD2頭部,data為文檔數據

MD2_HEADER_PTR    header = (MD2_HEADER_PTR)data;

2.判斷是否MD2模型

if (header->id != ‘2PDI’) // MD2文檔標記

return false;

3.判斷版本號總為8

if (header->version != 8)

return false;

4.幀總量id softwarek規定198幀

md2->frame_num = header->frame_num;

5.皮膚紋理總量設為0

md2->texture_num   = 0;

4.多邊形總量

md2->polygon_num = header->polygon_num;

5.讀取幀頂點量

md2->vertex_num = header->vertex_num;

6.分配多邊形記憶體

md2->polygon_array = (MD2_POLYGON_PTR)malloc(md2->polygon_num*sizeof(MD2_POLYGON));

7.分配頂點記憶體

md2->vertex_array = (VECTOR3D_PTR)malloc(md2->frame_num * md2->vertex_num * sizeof(VECTOR3D));

8.分配紋理座標記憶體.以繪畫三角形進行排列

md2->textcoord_array = (VECTOR2D_PTR)malloc(md2->polygon_num * 3 * sizeof(VECTOR2D));

9.遍歷每一幀

for (int findex = 0; findex < md2->frame_num; ++findex){

MD2_FRAME_PTR frame;// 讀取幀

frame = (MD2_FRAME_PTR)(data+header->frame_offset + header->frame_size * findex);

10.遍歷每一頂點

for (int vindex = 0; vindex < md2->vertex_num; ++vindex){

VECTOR3D v ;

11.對頂點座標進行縮放和平移

v.x = frame->list[vindex].v[0] * frame->scale[0] + frame->translate[0];

v.y = frame->list[vindex].v[1] * frame->scale[1] + frame->translate[1];

v.z = frame->list[vindex].v[2] * frame->scale[2] + frame->translate[2];

12.跟據傳入參數進行縮放

if (scale != NULL)

v = v * scale;

13.反轉座標軸

if (flag & MD2_INVERT_X)

v.x = -v.x;

if (flag & MD2_INVERT_Y)

v.y = -v.y;

if (flag & MD2_INVERT_Z)

v.z = -v.z;

14.交換座標軸

float temp;

if (flag & MD2_SWAP_YZ)

SWAP(v.y, v.z, temp);

if (flag & MD2_SWAP_XZ)

SWAP(v.x, v.z, temp);

if (flag & MD2_SWAP_XY)

SWAP(v.x, v.y, temp);

15.將頂點插入列表中

md2->vertex_array[findex*md2->vertex_num + vindex] = v;

}

}

16.讀取紋理座標

MD2_TEXTCOORD_PTR textcoord = (MD2_TEXTCOORD_PTR)(data + header->textcoord_offset);

17.讀取多邊形

MD2_POLYGON_PTR polygon = (MD2_POLYGON_PTR)(data+header->polygon_offset);

18.遍歷多邊形

for (int pindex = 0; pindex<header->polygon_num; ++pindex){

if (flag & MD2_INVERT_WINDING_ORDER) {

19.反轉頂點環繞順序

md2->polygon_array[pindex].vertex_index[0] = polygon[pindex].vertex_index[2];

md2->polygon_array[pindex].vertex_index[1] = polygon[pindex].vertex_index[1];

md2->polygon_array[pindex].vertex_index[2] = polygon[pindex].vertex_index[0];

// 反轉紋理座標環繞順序

md2->polygon_array[pindex].textcoord_index[0] = polygon[pindex].textcoord_index[2];

md2->polygon_array[pindex].textcoord_index[1] = polygon[pindex].textcoord_index[1];

md2->polygon_array[pindex].textcoord_index[2] = polygon[pindex].textcoord_index[0];

}

else

{// 不改變頂點環繞順序

md2->polygon_array[pindex].vertex_index[0] = polygon[pindex].vertex_index[0];

md2->polygon_array[pindex].vertex_index[1] = polygon[pindex].vertex_index[1];

md2->polygon_array[pindex].vertex_index[2] = polygon[pindex].vertex_index[2];

// 不改變紋理座標環繞順序

md2->polygon_array[pindex].textcoord_index[0] = polygon[pindex].textcoord_index[0];

md2->polygon_array[pindex].textcoord_index[1] = polygon[pindex].textcoord_index[1];

md2->polygon_array[pindex].textcoord_index[2] = polygon[pindex].textcoord_index[2];

}

20.以繪畫順序讀取三角形頂點紋理座標,無需在提取幀是在生成.

int tindex0 = md2->polygon_array[pindex].textcoord_index[0];

int tindex1 = md2->polygon_array[pindex].textcoord_index[1];

int tindex2 = md2->polygon_array[pindex].textcoord_index[2];

21.讀取紋理座標轉換為0.0f~1.0f

md2->textcoord_array[pindex*3+0].u = (float)textcoord[tindex0].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+0].v = (float)textcoord[tindex0].v / (float)header->skin_height;

md2->textcoord_array[pindex*3+1].u = (float)textcoord[tindex1].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+1].v = (float)textcoord[tindex1].v / (float)header->skin_height;

md2->textcoord_array[pindex*3+2].u = (float)textcoord[tindex2].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+2].v = (float)textcoord[tindex2].v / (float)header->skin_height;

}

22.遍歷每個頂點計算模型半徑

md2->radius_avg = 0;// 平均半徑

md2->radius_max = 0;// 最大半徑

for (int vindex = 0; vindex < header->vertex_num; ++vindex){

float dist;

dist = (float)sqrt(md2->vertex_array[vindex].x * md2->vertex_array[vindex].x +

md2->vertex_array[vindex].y * md2->vertex_array[vindex].y +

md2->vertex_array[vindex].z * md2->vertex_array[vindex].z);

md2->radius_avg = md2->radius_avg + dist;// 累加半徑

if (dist > md2->radius_max)

md2->radius_max = dist;// 最大半徑

}

23.計算平均半徑

md2->radius_avg = md2->radius_avg / header->vertex_num;//

23.載入默認動畫序列. md2_animations[]數組跟據上面動畫列表定義

for (int aindex = 0; aindex < (sizeof(md2_animations) / sizeof(MD2_ANIMATION)); ++aindex)

md2->animation_array[aindex] = md2_animations[aindex];

return true;

}

 

紋理需要另外載入,紋理高寬需為2x2x.如256256、128*128

bool Load_Texture_MD2(MD2_PTR md2, const char * path){

int index = md2->texture_num; // 索引

++md2->texture_num;// 紋理數量

Load_File_Texture(&md2->texture_array[index], path);// 載入

Bind_Image_Texture(&md2->texture_array[index]);// 綁定

return true;

}

 

3D模型動畫平滑運動需要幀插值.『當前幀』frame_curr帶有小數在0~127之間.按權重插值公式如下:

vi=v0(1-value) + v1value

逐幀提取函式:

void Extract_Frame_MD2(MD2_PTR md2,VECTOR3D_PTR  vertex_array,VECTOR3D_PTR normal_array,float frame_curr){

MD2_POLYGON_PTR polyon;

int vindex;

1.判斷幀是否插值得到

float ivalue = frame_curr – (int)frame_curr;

if (ivalue == 0.0f) {//判斷是否為整數

2.若為整數則直接讀取

int frame = (int)frame_curr;

if (frame >= md2->frame_num)

frame = md2->frame_num – 1;

3.計算當前幀索引頂點偏移

int base = md2->vertex_num * frame;

4.遍歷每個多邊形

for (int pindex = 0, index = 0; pindex < md2->polygon_num; ++pindex, index= index+3){

5.讀取多邊形每個頂點

polyon = &md2->polygon_array[pindex];

vindex = polyon->vertex_index[0];

vertex_array[index+0]    = md2->vertex_array[base + vindex];

vindex = polyon->vertex_index[1];

vertex_array[index + 1] = md2->vertex_array[base + vindex];

vindex = polyon->vertex_index[2];

vertex_array[index + 2] = md2->vertex_array[base + vindex];

6.計算三角形法線

Normal_VECTOR3D(&normal_array[pindex],&vertex_array[index + 0],&vertex_array[index + 1],&vertex_array[index + 2]);

}

}

else{

2.若有小數數則進行幀插值,讓動畫平滑

int frame0 = (int)frame_curr;

int frame1 = (int)frame_curr + 1;

if (frame0 >= md2->frame_num)

frame0 = md2->frame_num – 1;

if (frame1 >= md2->frame_num)

frame1 = md2->frame_num – 1;

int base0 = md2->vertex_num * frame0;

int base1 = md2->vertex_num * frame1;

3.偏曆三角形在兩幀之間插值計算頂點

for (int pindex = 0, index = 0; pindex < md2->polygon_num; ++pindex, index = index + 3){

4.讀取兩個多邊形頂點並行權重插值

polyon = &md2->polygon_array[pindex];

vindex = polyon->vertex_index[0];

vertex_array[index+0] = md2->vertex_array[base0 + vindex] * (1 – ivalue) +md2->vertex_array[base1 + vindex] * (ivalue);

vindex = polyon->vertex_index[1];

vertex_array[index + 1] = md2->vertex_array[base0 + vindex] * (1 – ivalue) +md2->vertex_array[base1 + vindex] * (ivalue);

vindex = polyon->vertex_index[2];

vertex_array[index + 2] = md2->vertex_array[base0 + vindex] * (1 – ivalue) + md2->vertex_array[base1 + vindex] * (ivalue);

5.計算三角形法線

Normal_VECTOR3D(&normal_array[pindex],&vertex_array[index + 0], &vertex_array[index + 1], &vertex_array[index + 2]);

}

}

}

 

載入MD2模型.讓MODEL3D

void Load_MD2_MODEL3D(MODEL3D_PTR model3D,MD2_PTR md2,int texture_index){

1.清零

memset(model3D, 0, sizeof(MODEL3D));

2.指向md2模型

model3D->md2 = md2;

3.分配每幀多邊形頂點記憶體

model3D->vertex_array = (VECTOR3D_PTR)malloc(md2->polygon_num * 3*sizeof(VECTOR3D));

4.分配每幀多邊形法線記憶體

model3D->normal_array = (VECTOR3D_PTR)malloc(md2->polygon_num * 3 * sizeof(VECTOR3D));

5.指向紋理

if (texture_index < md2->texture_num){

texture_index = md2->texture_num – 1;

model3D->texture = &md2->texture_array[texture_index];

}

}

 

設置模型動畫MD2有20個不同動作

void Set_Animation_MODEL3D(MODEL3D_PTR model3D,int anim,bool loop){

1.讀取MD2模型

MD2_PTR  md2= model3D->md2;

2.設定當前動畫索引

model3D->anim = anim;

3.動畫是否循環播放

model3D->loop = loop;

4.動畫播放標記設為未完成

model3D->complete = false;

5.動畫

MD2_ANIMATION_PTR  animation = md2->animation_array;

6.插幀頻率(0.0f~1.0f),1.0f表示不插幀

model3D->irate = animation[anim].irate;

7.當前幀

model3D->frame = animation[anim].start;

8.速度

model3D->speed = animation[anim].speed;

9.插幀計數器

model3D->count = 0;

10.提取動畫幀

Extract_Frame_MD2(model3D->md2,model3D->vertex_array,  model3D->normal_array,model3D->frame);

}

 

在遊戲引擎中你需要更新動畫, time為時間間隔通過Get_Counter_FPS(&fps);獲得

void Update_MODEL3D(MODEL3D_PTR model3D,float time){

MD2_PTR md2 = model3D->md2;

MD2_ANIMATION_PTR animation = md2->animation_array;

1.計算動畫有幾多幀

int   frame_num = animation->end – animation->start + 1;

2.計算每幀速度

float frame_speed = (float)frame_num / model3D->speed ;

3.當前幀帶小數以進行插值

model3D->frame = model3D->frame + frame_speed * time;

4.幀計數器控制插值

model3D->count = model3D->count + frame_speed * time;

5.判斷動畫是否播放完畢

if (model3D->frame > animation[model3D->anim].end)   {

if (model3D->loop == MD2_LOOP)  {// 循環播放動畫

model3D->count = 0;

model3D->frame = animation[model3D->anim].start;// 啟動幀

}

else{// 單次播放動畫

model3D->frame = animation[model3D->anim].end;// 結束幀

model3D->complete = true;// 以完成動作

}

}

  1. irate為插幀頻率(0.0f~1.0f), 1.0f表示不插幀

if (model3D->count >= model3D->irate || model3D->count == 0){

model3D->count = 0;//清零

8.提取動畫幀

Extract_Frame_MD2(model3D->md2, model3D->vertex_array,model3D->normal_array,model3D->frame);

}

}

 

每幀都要對3D模型進行渲染.這裡使用頂點數組進行渲染.當前你可以逐個三角形進行渲染但是會較慢.

void Render_MODEL3D(MODEL3D_PTR model3D){

1.當前矩陣堆棧壓棧

glPushMatrix();

  1. 移動多邊形位置並旋旋

glTranslatef(model3D->pos.x, model3D->pos.y, model3D->pos.z);// 移動

glRotatef(model3D->rot.x, 1.0f, 0.0f, 0.0f); // 繞X軸旋轉

glRotatef(model3D->rot.y, 0.0f, 1.0f, 0.0f); // 繞Y軸旋轉

glRotatef(model3D->rot.z, 0.0f, 0.0f, 1.0f); // 繞Z軸旋轉

3.壓入當前屬性

glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT);

4.提取MD2模型

MD2_PTR md2 = model3D->md2;

5.綁定紋理

TEXTURE_PTR texture = model3D->texture;

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, texture->ID);

5.啟用頂點數組

glEnableClientState(GL_VERTEX_ARRAY);

6.啟用紋理座標數組

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

7.啟用法線數組

glEnableClientState(GL_NORMAL_ARRAY);

8.指定頂點數組

glVertexPointer(3, GL_FLOAT, 0, model3D->vertex_array);

9.紋理座標

glTexCoordPointer(2, GL_FLOAT, 0, md2->textcoord_array);

10.三角形法線

glNormalPointer(GL_FLOAT,0, model3D->normal_array);

11.繪畫所有當前以啟用的頂點數組

glDrawArrays(GL_TRIANGLES, 0, md2->polygon_num * 3 );

12.啟用頂點數組

glDisableClientState(GL_VERTEX_ARRAY);

14.啟用紋理座標數組

glDisableClientState(GL_TEXTURE_COORD_ARRAY);

15.啟用法線數組

glDisableClientState(GL_NORMAL_ARRAY);

16.彈出當前屬性

glPopAttrib();

17.當前矩陣堆棧出棧

glPopMatrix();

}

MD2載入器程式:下載

  1. 可分別載入『角色』與『武器』MD2模型
  2. 紋理載入支持 『.PCX』『.BMP』『.TGA』
  3. 按ALT鍵彈出『MENU』點『ANIMATION』選切換不同的動畫.

WAV音檔分釋與讀取

WAV音檔分釋與讀取

WAV音頻格式由Electronic Arts(電子協會)創建.它基於.IFF(Interchange File Format)對等交換文件格式.允許不同音檔格式通過鑲套技術進行編碼.

上兩編文章『DirectSound』與『DirectSound3D』均從WAV音檔中讀音頻數據.WAV音檔好再於結構簡單無需解壓. 我建議先把所有WAV音檔數據格式統一. 改為『單聲道Mono』(因為你只有一個咪頭)、採樣頻率11025Hz、採樣精度8bit.後再由遊戲引擎載入.

.WAV數據由以下三部份組成:

數據 簡序
RIFF .IFF標記
FORMAT 音頻格式
DATA 音頻數據

 

RIFF數據 所占空間(BYTE) 簡序
chunkID 4 塊ID,必須為’RIFF’
chunkSize 4 塊長度(不包含chunkID[4]和chunkSize所占空間)

 

FORMAT數據 所占空間(BYTE) 簡序
waveID 4 WAVE ID,必須為’WAVE’
chunkID 4 塊ID,必須為’fmt ‘
chunkSize 4 塊長度(不包含chunkID[4]和chunkSize所占空間)
wFormatTag 2 壓縮(格式)標誌默認WAVE_FORMAT_PCM脈衝編碼格式
wChannels 2 聲道:『單聲道Mono』或『雙聲道Stereo』
dwSamplesPerSec 4 採樣頻率(11025Hz,22050Hz,44100Hz)
dwAvgBytesPerSec 4 每秒播放字節數(SamplesPerSec * BlockAlign)
wBlockAlign 2 字節對齊,單聲道8bit占1byte.雙聲道16bit占4yte.
wBitsPerSample 2 每個採樣點位精度.有8bit、16bit、24bit、32bit
information 2 附加信息(未必有此數據)

 

數據塊 所占空間(BYTE) 簡序
chunkID 4 塊ID,必須為’data’
chunkSize 4 音頻數據長度
Data chunkSize 音頻數據

讀取WAV數據.wav指向文檔數據.size為文檔長度

bool Load_WAV(SOUND3D_PTR sound3D, PBYTE wav, int size){

1.數格塊

RIFF_PTR          riff;

WAVE_FORMAT_PTR   format;

WAVE_DATA_PTR     data;

2.讀取RIFF數據

riff = (RIFF_PTR)wav;

3.判斷是否RIFF

if (riff->id != WAVE_RIFF_ID)

return false;        // 返回出錯!

4.讀取WAVE_FORMAT_CHUNK

format = (WAVE_FORMAT_PTR)((PBYTE)wav + sizeof(RIFF));

if (format->waveID != WAVE_ID ||

format->chunkID != WAVE_FMT_ID ||

format->wFormatTag != WAVE_FORMAT_PCM)

return(false);   // 返回出錯!

5.讀取數據塊

data = (WAVE_DATA_PTR)((PBYTE)wav + sizeof(RIFF) + (int)format->chunkSize + sizeof(WAVE_CHUNK));

if (data->chunkID != WAVE_DATA_ID)

return(false);   // 返回出錯!

6.獲取音頻數據

PBYTE audio = (PBYTE)malloc(data->chunkSize);

memcpy(audio, &data->data, data->chunkSize);

return true;

}

DirectSound之3D狼嚎

DirectSound之3D狼嚎

經多日努力終於可以在3D空間中添加中添加3D效果,所帶來極強真實感效果有時比3D渲染更好.而且x86和x64均正確運行.在演示程式中.『狼嚎』放置於山地中心.隨著按方向鍵移動相機聲音產生變化.單這點很多書本例程均不正確. 3D狼嚎程式:下載

而DirectSound如何模擬3D效果.3D即有xyz三軸笛卡爾座標,以此定義聲源和聽者位置、速度、方向.要注意Z軸是指向屏幕內部.所以3D效果需單聲道.

typdef struct{

IDirectSoundBuffer       * buffer;  // 輔助緩存

IDirectSound3DBuffer8    * buffer3D;// 3D緩存

float x,y,z;// 音源3D位置

PBYTE data;// 音頻數據

int size;// 所占空間

}SOUND3D;

SOUND3D sound3D;

而一般我地有左右兩個音箱,最多四角四個音箱.要模擬3D感知效果要素如下:

  1. 響度:當聲源逐漸遠離聽者,聲源會逐漸衰減
  2. 兩耳聲響差:若聲源位於聽者右側,則右耳聽到聲響比左耳響.
  3. 兩耳時間差:若聲源位於聽者右側,則右耳比左耳早聽到一毫秒.
  4. 兩耳朝向:若聲源在前邊則比後邊響
  5. 聲音最大距離:大於此距離聲音不再衰減.默認為DS3D_DEFAULTMAXDISTANCE(109 m)
  6. 聲音最小距離:大於此距離聲音不再遞增.默認為DS3D_DEFAULTMINDISTANCE(1m)
  7. 默認模式: DS3DMODE_NORMAL使用3D座標設定音源位置
  8. 頭部模式: DS3DMODE_HEADRELATIVE.可以設定音源位置,但監聽位置總為(0,0,0)
  9. 禁用3D模式:DS3DMODE_DISABLE 禁用3D聲音

WAV數據載入』後交與DirectSound代碼如下:

1.創建聲卡接口,NULL為與默認聲卡鏈接

IDirectSound8 * direct_sound3D = NULL;// 默認聲卡接口

DirectSoundCreate8(NULL,&direct_sound3D,NULL);

2.設置為優先等級,允許設定主緩存音頻數據格式並獲取IDirectSound3DListener8接口

direct_sound3D->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);// 優先級別

3.初始化Directsound主緩存,主緩存代表聲卡,並設置格式

DSBUFFERDESC buffer;// 緩存格式描述

ZeroMemory(&buffer, sizeof(DSBUFFERDESC));// 清零

buffer.dwSize = sizeof(DSBUFFERDESC);// 所占空間

buffer.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;//3D緩存並使用聲卡緩存

4.創建默認聲卡緩存接口

IDirectSoundBuffer * DirectSound3D_Buffer = NULL;

direct_sound3D->CreateSoundBuffer(&buffer, &DirectSound3D_Buffer, NULL);

5.獲取3D空間『聽者』

IDirectSound3DListener8  * DirectSound3D_Listener;// 3D聽者

DirectSound3D_Buffer->QueryInterface(IID_IDirectSound3DListener, (VOID**)&DirectSound3D_Listener);

6.設置緩存數據格式

WAVEFORMATEX format;//緩存格式描敘

memset(&format, 0, sizeof(WAVEFORMATEX));

format.cbSize = 0;//總是0.

format.wFormatTag = WAVE_FORMAT_PCM;  //脈衝編碼格式

format.nChannels = 1;   //(聲道數目)1為單聲道回放

format.nSamplesPerSec = 11025;//(每秒的樣本)總是這個速度

format.wBitsPerSample = 8;//聲音為8Bit

format.nBlockAlign = 1;//字節對齊,單聲道8bit占1byte.雙聲道16bit占4yte.

format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; //每秒播放字節數

DirectSound3D_Buffer->SetFormat(&format); //設置主緩存音頻格式

7.現在建立輔助聲音緩存

DSBUFFERDESC desc;// 緩存格式描敘

memset(&desc,0,sizeof(DSBUFFERDESC));

desc.dwSize = sizeof(DSBUFFERDESC);//dsbd的大小

desc.dwFlags = DSBCAPS_CTRL3D |// 請求3D BUFFER

DSBCAPS_CTRLVOLUME | // 緩存擁有音量控制功能

DSBCAPS_CTRLFREQUENCY | // 緩存擁有頻率控制功能

DSBCAPS_GLOBALFOCUS |

DSBCAPS_CTRLPOSITIONNOTIFY |  // 3D位置

DSBCAPS_GETCURRENTPOSITION2; // 播放位置

desc.dwBufferBytes = sound3D->size;//聲音數據的大小.

desc.guid3DAlgorithm = DS3DALG_DEFAULT;// 主緩沖區在 DSBCAPS_CTRL3D 模式下可以啟用這個選項

desc.lpwfxFormat = &format;// 緩存數據格式

8.創建輔助聲音緩衝器

direct_sound3D->CreateSoundBuffer(&desc, &sound3D->buffer, NULL);

9.獲取3D空間緩存

sound3D->buffer->QueryInterface(IID_IDirectSound3DBuffer, (VOID**)&sound3D->buffer3D);

10.把數據寫入聲音輔助緩存,輔助(二級)聲音緩存是自循環

UCHAR  audio_ptr_1 = NULL,audio_ptr_2 = NULL; //指向緩存第一與第二部分

DWORD   audio_len_1 = 0, audio_len_2 = 0;  //第一與第二緩存長度

11.鎖住空間

sound3D->buffer->Lock(0,//寫入指針指向位置

sound3D->size,//要鎖定空間大小.

(void **)&audio_ptr_1,//第一個部分開始地址.

&audio_len_1,//第一個部分長度

(void **)&audio_ptr_2,//第二個部分開始地址.

&audio_len_2,//第二個部分長度

DSBLOCK_FROMWRITECURS );

11.複製數據到聲音緩沖存儲器,拷貝到圓形緩沖存儲器的第一段中

memcpy(audio_ptr_1, sound3D->data, audio_len_1);

12.拷貝到圓形緩沖存儲器的第二段中

memcpy(audio_ptr_2, (sound3D->data + audio_len_1), audio_len_2);

13.解鎖

sound3D->buffer->Unlock(audio_ptr_1, audio_len_1,audio_ptr_2, audio_len_2);

14.設置音源3D位置與距離

DS3DBUFFER  param;

memset(&param, 0, sizeof(DS3DBUFFER));

param.dwSize = sizeof(DS3DBUFFER);

sound3D->buffer3D->GetAllParameters(&param);

param.flMaxDistance = sound3D->max;// 聲音最大距離 DS3D_DEFAULTMAXDISTANCE

param.flMinDistance = sound3D->min;// 聲音最小距離 DS3D_DEFAULTMINDISTANCE

param.vPosition.x = x; //音源3D位置

param.vPosition.y = y;

param.vPosition.z = -z;// Z軸反轉

param.dwMode = DS3DMODE_NORMAL;// 默認,使用3D座標設定音源位置和

sound3D->buffer3D->SetAllParameters(&param, DS3D_IMMEDIATE);// 立即設定

15.正確設置音源因子與3D聽者位置,關鍵部分是不要設置DS3DMODE_HEADRELATIVE(頭部模式)

DS3DLISTENER  param;

memset(&param, 0, sizeof(DS3DLISTENER));

param.dwSize = sizeof(DS3DLISTENER);

DirectSound3D_Listener->GetAllParameters(&param);

param.flDopplerFactor = 0; //多譜勒頻移,一般設為0

param.flRolloffFactor = 0.1f; // 衰減率,此直越大衰減越快.一般設為0.1f~0.5f

param.vPosition.x = x; //聽者3D位置

param.vPosition.y = y;

param.vPosition.z = -z;

DirectSound3D_Listener->SetAllParameters(&param, DS3D_IMMEDIATE);

16.循環播放音頻數據

sound3D->buffer->Play(0,0,DSBPLAY_LOOPING);

17.單次播放

sound3D->buffer->Play(0, 0, 0);

18.停止音頻播放

sound3D->buffer->Stop();

19.釋放DirectSound3D

sound3D->buffer3D->Release();// 釋放3D緩存

sound3D->buffer->Release();// 釋放輔助緩存

DirectSound3D_Buffer->Release(); // 釋放聲卡主緩存

direct_sound3D->Release();// 釋放聲卡

 

DirectMusic之播放MIDI

DirectMusic之播放MIDI

DirectMusic主要用於播放midi數據,而且你無需寫分析器.DirectMusic自動完成所有操作,本.演示程式按ALT鍵彈出MENU再Open打開midi音檔.按+/-鍵控制音量.本想編譯X64版本卻出現『REGDB_E_CLASSNOTREG Class not registered.』所以只有x86程式下載:

DirectMusic是建基於DirectSound,不過DirectMusic會在Init()中自啟動它.DirectMusic是Dirext中第一個完全COM化組件,所以DirectMusic無需LIB庫.只需以下幾個頭文檔.

#include “..\DirectX\Include\dmplugin.h”

#include “..\DirectX\Include\dmksctrl.h”

#include “..\DirectX\Include\dmusici.h”

#include “..\DirectX\Include\dmusicc.h”

#include “..\DirectX\Include\dmusicf.h”

但安裝最新DXSDK安裝包(DX11),以上幾文件頭文件據然穩唔倒.我只可在舊版SDK拷貝過來工程文檔下.如果你把文檔拷貝到工程檔案下還需要小小修改#include <dmplugin.h>改為#include “dmplugin.h”

而編譯時DirectMusic有大量GUID無法鏈接.出現LNK2001下錯誤.

“錯誤  LNK2001        無法解析的外部符號 _CLSID_DirectMusicPerformance”

古計是從”dxguid.lib”刪除左.無需執著這些小問題.只要自已加上即可.你需要初此化GUID宏指令

#define INIT_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)   const GUID name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}

並把下面GUID複製到全域變量

INIT_GUID(CLSID_DirectMusicPerformance, 0xd2ac2881, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicSegment,     0xd2ac2882, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicSegmentState,0xd2ac2883, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicGraph,       0xd2ac2884, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicStyle,       0xd2ac288a, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicChordMap,    0xd2ac288f, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicComposer,    0xd2ac2890, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicLoader,      0xd2ac2892, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(CLSID_DirectMusicBand,        0x79ba9e00, 0xb6ee, 0x11d1, 0x86, 0xbe, 0x0, 0xc0, 0x4f, 0xbf, 0x8f, 0xef);

INIT_GUID(GUID_Download,            0xd2ac28a7,0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_Unload,              0xd2ac28a8,  0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_StandardMIDIFile,    0x6621075, 0xe92e, 0x11d1, 0xa8, 0xc5, 0x0, 0xc0, 0x4f, 0xa3, 0x72, 0x6e);

INIT_GUID(GUID_DirectMusicAllTypes, 0xd2ac2893, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(GUID_PerfMasterVolume,    0xd2ac28b1, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicLoader, 0x2ffaaca2, 0x5dca, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);

INIT_GUID(IID_IDirectMusicGetLoader, 0x68a04844, 0xd13d, 0x11d1, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);

INIT_GUID(IID_IDirectMusicObject, 0xd2ac28b5, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicSegment, 0xf96029a2, 0x4282, 0x11d2, 0x87, 0x17, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicSegmentState, 0xa3afdcc7, 0xd3ee, 0x11d1, 0xbc, 0x8d, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xeb);

INIT_GUID(IID_IDirectMusicPerformance, 0x7d43d03, 0x6523, 0x11d2, 0x87, 0x1d, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicGraph, 0x2befc277, 0x5497, 0x11d2, 0xbc, 0xcb, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xeb);

INIT_GUID(IID_IDirectMusicStyle, 0xd2ac28bd, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicChordMap, 0xd2ac28be, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicComposer, 0xd2ac28bf, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicBand, 0xd2ac28c0, 0xb39b, 0x11d1, 0x87, 0x4, 0x0, 0x60, 0x8, 0x93, 0xb1, 0xbd);

INIT_GUID(IID_IDirectMusicLoader8, 0x19e7c08c, 0xa44, 0x4e6a, 0xa1, 0x16, 0x59, 0x5a, 0x7c, 0xd5, 0xde, 0x8c);

INIT_GUID(IID_IDirectMusicPerformance8, 0x679c4137, 0xc62e, 0x4147, 0xb2, 0xb4, 0x9d, 0x56, 0x9a, 0xcb, 0x25, 0x4c);

INIT_GUID(IID_IDirectMusicSegment8, 0xc6784488, 0x41a3, 0x418f, 0xaa, 0x15, 0xb3, 0x50, 0x93, 0xba, 0x42, 0xd4);

INIT_GUID(IID_IDirectMusicSegmentState8, 0xa50e4730, 0xae4, 0x48a7, 0x98, 0x39, 0xbc, 0x4, 0xbf, 0xe0, 0x77, 0x72);

INIT_GUID(IID_IDirectMusicStyle8, 0xfd24ad8a, 0xa260, 0x453d, 0xbf, 0x50, 0x6f, 0x93, 0x84, 0xf7, 0x9, 0x85);

接口對像 簡介
IDirectMusic 你無需自已創建DirectMusic,因為她會自動創建
IDirectMusicPerformance 對MIDI播放進行控制,構建該對象時同時創建IDirectMusic
IDirectMusicLoader 用於加載音頻文檔,如MIDI.這樣你以擁有MIDI的載入器
IDirectMusicSegment Segment代表音樂數據塊
IDirectMusicSegmentState 數據塊狀態
IDirectMusicProt MIDI音樂輸出硬件設備.微軟合成器
IDirectMusic 擁有一個DSP(Digital Signal Processing)MIDI數字實時轉換器

下面是IDirectMusic播放MIDI代碼簡介:

1.首先你必須初此化COM

CoInitialize(NULL);

2.創建DirectMusicPerformance接口

IDirectMusicPerformance    *DirectMusic_Performance = NULL;

CoCreateInstance(CLSID_DirectMusicPerformance,NULL, CLSCTX_INPROC,             IID_IDirectMusicPerformance,(LPVOID*)&DirectMusic_Performance);

3.初此化 DirectMusicPerformance設置為audiopath,自動構建IDirectMusic和IDirectSound接口

DirectMusic_Performance->Init(NULL, NULL, hWnd);

4.增加數字輸出播放端口,使用微軟合成器作為默認設備

DirectMusic_Performance->AddPort(NULL);

5.創建MIDI載入器

IDirectMusicLoader  * DirectMusic_Loader = NULL;

CoCreateInstance(CLSID_DirectMusicLoader,NULL,CLSCTX_INPROC,                   IID_IDirectMusicLoader8,(LPVOID*)&DirectMusic_Loader);

6.你需要在初此化時設定音量

long volume = DMUS_VOLUME_MAX;

DirectMusic_Performance->SetGlobalParam(GUID_PerfMasterVolume, &volume, sizeof(long));

7.載入midi音檔. DirectMusic非常聰面.你只需給出『路徑』和『文檔名』即可完成MIDI載入

DMUS_OBJECTDESC objdesc;

memset(&objdesc, 0, sizeof(DMUS_OBJECTDESC));// 清零

objdesc.dwSize = sizeof(DMUS_OBJECTDESC);

objdesc.guidClass = CLSID_DirectMusicSegment;

objdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;

8.Utf8轉換為UNICODE寬字符

MultiByteToWideChar(CP_UTF8, NULL, filename, (int)strlen(filename), objdesc.wszFileName, DMUS_MAX_FILENAME);//文檔名

MultiByteToWideChar(CP_UTF8, NULL, category, (int)strlen(category), objdesc.wszCategory, DMUS_MAX_CATEGORY);//路徑

9.設置當前搜索目錄

hResult = DirectMusic_Loader->SetSearchDirectory(GUID_DirectMusicAllTypes, objdesc.wszCategory, false);

10.如果MIDI音檔編譯進資源文檔(resource)或者自已讀取MIDI音檔數據

objdesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY;

objdesc.pbMemData = data;//指向midi數據

objdesc.llMemLength = size;// 數據長度

11.獲取IDirectMusicSegment音色庫接口,並自動載入midi數據

IDirectMusicSegment      * segment;

DirectMusic_Loader->GetObject(&objdesc,IID_IDirectMusicSegment,(void**)&segment);

12.指定音色庫segment

segment->SetParam(GUID_StandardMIDIFile,-1,0,0,DirectMusic_Performance);

13.將音色庫segment載入到IDirectMusicPerformance

segment->SetParam(GUID_Download, -1, 0, 0, DirectMusic_Performance);

14.設置循環播放

segment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);

15.單次循環播放

midi->segment->SetRepeats(0);

16.終於可以播放MIDI音檔

IDirectMusicSegmentState * segstate;

DirectMusic_Performance->PlaySegment(segment,0,0, &segstate);

17.停止MIDI播放

DirectMusic_Performance->Stop(segment, NULL, 0, 0);

18.禦載DLS樂器段

segment->SetParam(GUID_Unload, -1, 0, 0, (void*)DirectMusic_Performance);

19.釋放指定MIDI音樂段.

segment->Release();

20.停止播放所有的MIDI音樂段

DirectMusic_Performance->Stop(NULL,NULL, 0, 0);

21.關閉IDirectMusicPerformance對像.

DirectMusic_Performance->CloseDown();

22.釋放IDirectMusicPerformance對像.

DirectMusic_Performance->Release();

23.釋放載入器.

DirectMusic_Loader->Release();

DirectSound之播放聲音

DirectSound之播放聲音

DirectSound顧明思義可以讓你控制聲卡播放聲音和音樂. DirectSound由許多模塊和接口組成,編譯時需要庫文檔DSOUND.LIB和頭文檔DSOUND.H

WAV音檔播放演示程式,按+/-鍵控制聲音音量.按Open載入WAV音檔:下載

1.啟動DirectSound

IDirectSound接口:DirectSound的主COM對象.它代表聲卡硬件.若你裝有多個聲卡,則每個DirectSound對象代表一個聲卡.創建主聲卡對象,輸入NULL與默認聲卡鏈接

LPDIRECTSOUND       direct_sound;

DirectSoundCreate(NULL,&direct_sound,NULL);

if (direct_sound == NULL)

return false;

2.設置默認協作等級

設定遊戲控制聲卡的等級.一般設定為DSSCL_NORMAL即可.當程式具有焦點時,即可播放聲音.但並不妨礙其它程式. DirectSound自動創建22kHz,立體聲,8bit主緩存(默認).hWnd為當前窗口句柄

direct_sound->SetCooperativeLevel(hWnd,DSSCL_NORMAL);

參數 簡介
DSSCL_NORMAL 默認等級
DSSCL_PRIORITY 優先等級.允許設定主緩存數據格式
DSSCL_EXCLUSIVE 當窗口在前臺時擁有優先等級
DSSCL_WRITEPRIMARY 完全控制主緩存

3.輔助(二級)音頻緩存

『輔助緩存』即代表你想播放的音頻.但聲卡上SRAM卻容量有限,而且我印象中只在ISA版CREATIVE(創新)聲卡有出現.『輔助緩存』分為『靜態緩存』與『動態緩存』.『靜態緩存』用於存儲經常播放短聲音.『動態緩存』用於存儲較長聲音.DirectSound不斷播放不斷把音頻數據寫入『輔助緩存』.使用稱為Circular Buffering(環形緩存)結構,一個指針寫入音頻數據,另一個指針讀取音頻數據.創建輔助聲音緩衝器

HRESULT IDirectSound::CreateSoundBuffer(

LPCDSBUFFERDESC lpcDSBufferDesc,

LPLPDIRECTSOUNDBUFFER lplpDirectSoundBuffer,

IUnknown FAR* pUnkOuter

);

4.設置緩存格式描敘,單聲道11kHz,8bit

AVEFORMATEX format;

format.cbSize = 0;//高級,總是0.

format.wFormatTag = WAVE_FORMAT_PCM;//PCM編碼

format.nChannels = 1;//(聲道數目)1為單聲道播放

format.nSamplesPerSec = 11025;// 樣本速度11kHz

//設置字節對齊.

//單聲道乘以1字節(8Bit), 它將是1字節字節對齊.

//立體聲它將是雙聲道8Bit, 它將是2字節字節對齊.

//如果它是立體聲系統和16Bit它將是4字節對齊.

format.nBlockAlign     = 1;

format.wBitsPerSample = 8;//聲言位數為8BIT

format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;// 每秒字節

3.設定聲音緩沖存儲器描敘表

DSBUFFERDESC buffer;// 描述結構

memset(&buffer,0,sizeof(DSBUFFERDESC));// 清零

buffer.dwSize = sizeof(DSBUFFERDESC);// 結構所占空間

//設置DirectSoundBuffer的描述結構的標誌

buffer.dwFlags = DSBCAPS_CTRLPAN | // 緩存擁有總控制功能

DSBCAPS_CTRLVOLUME | // 緩存擁有音量控制功能

DSBCAPS_CTRLFREQUENCY; // 緩存擁有頻率控制功能

buffer.dwBufferBytes = sound_size;//聲音數據大小.

buffer.lpwfxFormat  = &format;//”WAV文件格式描述結構”

4.創建輔助(二級)音頻緩存

LPDIRECTSOUNDBUFFER sound_buffer;

direct_sound->CreateSoundBuffer(&buffer, &sound_buffer, NULL);

if (sound_buffer == NULL)

return false;

5.鎖住緩存

UCHAR *audio_ptr_1 = NULL, //指向緩存第一部分

UCHAR *audio_ptr_2 = NULL; //指向緩存第二部分

DWORD  audio_len_1 = 0,  //第一緩存長度

DWORD  audio_len_2 = 0;  //第二緩存長度

sound_buffer->Lock(0,//寫入指針指向位置

sound_size,//要鎖定大小,音頻數據長度.

(void **)&audio_ptr_1,//第一個部分開始地址.

&audio_len_1,//第一個部分長度

(void **)&audio_ptr_2,//第二個部分開始地址.

&audio_len_2,//第二個部分長度

DSBLOCK_FROMWRITECURSOR);// 緩存當前寫入點將被鎖住.

6.拷貝到環形緩存第一部分, sound_size為音頻數據

memcpy(audio_ptr_1, sound_data, audio_len_1);

7.拷貝到環形緩存第二段中

memcpy(audio_ptr_2, (sound_data + audio_len_1), audio_len_2);

8.解鎖

sound_buffer->Unlock(audio_ptr_1,audio_len_1,audio_ptr_2,audio_len_2);

9.循環播放聲音

sound_buffer->Play(0,0,DSBPLAY_LOOPING);

10.若單次播放聲音

sound_buffer->Play(0, 0, 0);

11.當退出遊戲時需要釋放緩存和聲卡

sound_buffer->Release();

direct_sound->Release();

小米MAX2

小米MAX2

台『小米2S』經常報空間不足,點刪APP都一樣.而且可部機跌過落地,影相經常花屏.是時侯要換個台手機.睇來睇去都是『小米MAX2』擁有『128GB固態硬盤』『4GB記憶體』先是1699紋.喂一不足時款式以落後.拿到手後想通過『一鍵換機』將電話簿『連絡人』移到『小米MAX2』點知按『我是新手機』WIFI馬上關閉.你需要重新『開啟WLAN』.後來通過『同步』『連絡人』,支持快充台機只是微微發熱.

 

DirectInput之查詢滑鼠

DirectInput之查詢滑鼠

在windows你可以通過WM_MOUSEMOVE被動(消息驅動)接受滑鼠當前位置.但若在遊戲中若想得到滑鼠『偏移量』和『按扭』(是否按下)DirectInput則可以幫助你你重新獲得滑鼠每次『偏移量』和『按扭』信息(主動讀取).然後讓你可以重新繪畫新『指標』. 而無需Windows自帶『指標』.若你有多個滑鼠.則所有『偏移量』合併到單一設備中.

滑鼠移動演示程式:下載

演示代碼.在Init()中調用

1.調用DirectInputCreate()生成IDirectInput接口

IDirectInput * direct_input;

DirectInputCreate(GetModuleHandle(NULL),DIRECTINPUT_VERSION, &direct_input,NULL);

2.獲取滑鼠GUID_SysMouse為全域主滑鼠設備ID碼

direct_input->CreateDevice(GUID_SysMouse, &direct_mouse, NULL);

3.設置滑鼠協作等級(前臺) 非獨占訪問,訪問滑鼠時不會干涉其它應用程序訪問該滑鼠.當應用程序在前臺和後臺時都能時用該滑鼠.

direct_mouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE |                                             DISCL_BACKGROUND);

4.設置數據格式. c_dfDIMouse為DirectInput通用滑鼠全域常量

direct_mouse->SetDataFormat(&c_dfDIMouse);

5.從DirectInput獲取滑鼠

direct_mouse->Acquire();

6.載入滑鼠紋理,並且背景透明

mouse->texture.IsAlpha = true;

Load_File_Texture(&mouse->texture, hInstance, filename);

7.綁定紋理

Bind_Image_Texture(&mouse->texture,GL_REPEAT, GL_REPEAT,GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,GL_REPLACE);

8.滑鼠尺寸

mouse_size = 20;

9.現在你可以從滑鼠中獲取輸入.在你Update()更新中獲取滑鼠狀態.若你失去設備鍵盤,需要重新從DirectInput獲取鍵盤.

DIMOUSESTATE  state;

while (direct_mouse->GetDeviceState(sizeof(DIMOUSESTATE), &state) == DIERR_INPUTLOST)//

{         // 已失去設備,重新從DirectInput獲取鍵盤

if (direct_mouse->Acquire()==S_OK)

return false;

}

10.定義鼠標結構

typedef struct DIMOUSE_TYP{

int x, y;// 滑鼠座標

int screen_width, screen_height;// 窗口寬和高

float size;// 鼠標尺寸

bool button_left, button_right, button_middle; // 滑鼠按鈕,若true則為按下

TEXTURE texture;// 指標紋理

}DIMOUSE, *DIMOUSE_PTR;

DIMOUSE mouse;

11.重新計算滑鼠位置

mouse->x = mouse->x + state.lX;

mouse->y = mouse->y + state.lY;

12.判斷是否超出窗口邊界

if (mouse->x < 0)

mouse->x = 0;

if (mouse->x >= screen_width)

mouse->x = screen_width-1;

if (mouse->y < 0)

mouse->y = 0;

if (mouse->y >= screen_height)

mouse->y = screen_height – 1;

13.提取滑鼠按鈕

mouse->button_left = state.rgbButtons[0] & 0x80; //滑鼠左鍵

mouse->button_right = state.rgbButtons[1] & 0x80; // 滑鼠右鍵

mouse->button_middle = state.rgbButtons[2] & 0x80;// 滑鼠中鍵

14.當你退出遊戲時.需要釋放滑鼠和DirectInput對像

direct_mouse->Release();

direct_input->Release();

 

DirectInput之查詢鍵盤

DirectInput之查詢鍵盤

DirectInput是DirectX COM組件之一. 它讓你無需理會『鍵盤』硬件驅動程式. DirectInput為輸入設備提供統一接口,當然輸入設備需已安裝硬件驅動.而且你需要安裝『DirectX SDK』並設定include和LIB搜索路徑,或者加入工程項目中.若你想讀取鍵盤狀態.用法與GetAsyncKeyState()使用基本相似.但它的KEY鍵並非是ASCII碼.若你有多個鍵盤.則所有的輸入合併到單一設備中.

演示程式按鍵盤在屏幕中顯示對應字母:下載

演示代碼.在Init()中調用當然你需檢測返回值HRESULT

1.調用DirectInputCreate()生成IDirectInput接口

IDirectInput * direct_input;

DirectInputCreate(GetModuleHandle(NULL),DIRECTINPUT_VERSION, &direct_input,NULL);

2.獲取鍵盤, GUID_SysKeyboard為全域主鍵盤設備ID碼

IDirectInputDevice * direct_keyboard;

direct_input->CreateDevice(GUID_SysKeyboard, &direct_keyboard, NULL);

3.設置鍵盤協作等級, 非獨占訪問,訪問該設備時不會干涉其它應用程序訪問該設備. 並且當應用程序在前臺和後臺時都能時用該設備.

direct_keyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE |DISCL_BACKGROUND);

4.設置數據格式. c_dfDIKeyboard為DirectInput通用鍵盤全域常量

direct_keyboard->SetDataFormat(&c_dfDIKeyboard);

5.從DirectInput獲取鍵盤.

direct_keyboard->Acquire();

6.你現在以可以從鍵盤中獲取輸入.在你Update()更新中獲取鍵盤狀態.若你失去設備鍵盤,需要重新從DirectInput獲取鍵盤

while(direct_keyboard->GetDeviceState(sizeof(UCHAR) * 256, direct_keystate)== DIERR_INPUTLOST)

{

if(direct_keyboard->Acquire() != S_OK)

return false;

}

7.當你退出遊戲時.需要釋放鍵盤和DirectInput對像

direct_keyboard->Release();

direct_input->Release();

OpenGL之陰影

OpenGL陰影

『陰影』有很多種方法,而『投射陰影』技術已被大量的應用於遊戲,從光源位置透視投影3D物體,從而將陰影投射到平面. 而且視覺效果佳.算法思路如下:

  1. 啟用『模板緩存』,將陰影繪畫限制在相應的區域中
  2. 構造投影矩陣,此矩陣根據『光源位置』和被『投影平面』位置來設定,並且與模型視圖矩陣相乘.
  3. 禁用光照和紋理,並設置顏色為黑色.
  4. 禁用深度測試.
  5. 啟用透明混合
  6. 繪畫陰影

落雪陰影演示下載:

構造投影矩陣

void Build_Matrix_Shadow(MATRIX4X4_PTR destMat, VECTOR4D_PTR lightPos, PLANE3D_PTR plane)

{

float dot;// 點積

dot = plane->M[0] * lightPos->M[0] +

plane->M[1] * lightPos->M[1] +

plane->M[2] * lightPos->M[2] +

plane->M[3] * lightPos->M[3];

// 第一列

destMat->_M[0] = dot – lightPos->M[0] * plane->M[0];

destMat->_M[4] = 0.0f – lightPos->M[0] * plane->M[1];

destMat->_M[8] = 0.0f – lightPos->M[0] * plane->M[2];

destMat->_M[12] = 0.0f – lightPos->M[0] * plane->M[3];

// 第二列

destMat->_M[1] = 0.0f – lightPos->M[1] * plane->M[0];

destMat->_M[5] = dot – lightPos->M[1] * plane->M[1];

destMat->_M[9] = 0.0f – lightPos->M[1] * plane->M[2];

destMat->_M[13] = 0.0f – lightPos->M[1] * plane->M[3];

// 第三列

destMat->_M[2] = 0.0f – lightPos->M[2] * plane->M[0];

destMat->_M[6] = 0.0f – lightPos->M[2] * plane->M[1];

destMat->_M[10] = dot – lightPos->M[2] * plane->M[2];

destMat->_M[14] = 0.0f – lightPos->M[2] * plane->M[3];

// 第四列

destMat->_M[3] = 0.0f – lightPos->M[3] * plane->M[0];

destMat->_M[7] = 0.0f – lightPos->M[3] * plane->M[1];

destMat->_M[11] = 0.0f – lightPos->M[3] * plane->M[2];

destMat->_M[15] = dot – lightPos->M[3] * plane->M[3];

}

MATRIX4X4 shadow_matrix; // 投影矩陣

GLfloat   light_pos[4] = { 150.0, 150.0, 150.0, 1.0 };// 『光照位置』

PLANE3D plane  = { 0.0, 1.0, 0.0, 0.0 };// 『投影平面』

構造投影矩陣

Build_Matrix_Shadow(&shadow_matrix,(VECTOR4D_PTR)&Light_Pos,(PLANE3D_PTR) &plane);

渲染陰影示例代碼

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 以黑色繪製陰影,將其與相應表面進行混合,無光照不進行深度測試

glPushMatrix();

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_TEXTURE_2D);// 禁用2D紋理

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_DEPTH_TEST);// 禁用深度測試

glEnable(GL_BLEND);// 啟用混合

// 確保不在任何一個光柵位置處進行重複繪製

glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);

// 黑色陰影

glColor4f(0.0f, 0.0f, 0.0f, blend);

// 以陰影進行投影

glMultMatrixf((float*)&shadow_matrix);// 投影矩陣

// 繪畫粒子暴風雪陰影

Draw_Snowstorm(&snowstorm, &Camera3D, &billboard);

glEnable(GL_TEXTURE_2D);// 禁用2D紋理

glEnable(GL_LIGHTING);// 禁用光照

glEnable(GL_DEPTH_TEST);// 禁用深度測試

glDisable(GL_BLEND);// 啟用混合

glPopAttrib();

glPopMatrix();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

// 繪畫粒子暴風雪

Draw_Snowstorm();

 

OpenGL之鏡像

OpenGL鏡像

『鏡像』並非是真實世界中由光粒子所產生.而式通過模擬『鏡像』技術.工作原理如下:

  1. 禁用顏色和深度緩存
  2. 啟用『模板緩存』後繪畫地板,對『模板緩存』進行寫操作
  3. 然後反轉『平面軸』渲染3D多邊形物體『鏡像』.
  4. 在3D空間中渲染平面
  5. 在平面的正面渲染3D多邊形物體

『鏡像』演示程式下載:

『鏡像』示例代碼:

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//繪畫正方體

Draw_Cube();

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

//繪畫正方體

Draw_Cube();

OpenGL之迷霧

OpenGL之迷霧

『迷霧』通過模糊遠端之物,而近端清晰.給3D世界帶來霧感.而且可以減小進場境中多邊形數量,從而提高渲染速度.演示程式按『+/-』鍵加減『迷霧』密度.下載『迷霧

OpenGL直接提供『迷霧』支持:

1.首先需要啟用『迷霧』

glEnable(GL_FOG);

2.將像素與『迷霧』顏色進行混合而實現,其混合因子的設定跟據其視點距離, 『迷霧密度』和『迷霧模式』,設定『迷霧』可通過以下函式:

void glFogf(GLenum pname,GLfloat param);

void glFogfv(GLenum pname,const GLfloat*params);

void glFogi(GLenum pname,GLint param);

void glFogiv(GLenum pname,const GLint*params);

Pname(參數名) Params(參數值)
GL_FOG_MODE 『迷霧』混合方程式,默認為GL_EXP

GL_LINEAR:factor=(end-z)/(end-start)

GL_EXP:factor=e(-density*depth)

GL_EXP2: factor=e(-density*depth)* (-density*depth)

GL_FOG_DENSITY 迷霧密度默認為1.0f
GL_FOG_START 迷霧離視點近端默認為0
GL_FOG_END 迷霧離視點遠端默認為1
GL_FOG_INDEX 迷霧顏色索引值默認為0
GL_FOG_COLOR 迷霧顏色默認為黑色(0,0,0,0).

啟用迷霧函是代碼

void Enable_Fog()

{

float fogColor[] = { 0.5, 0.5, 0.5, 1.0 };// 灰色

glEnable(GL_FOG);// 啟用霧

glFogfv(GL_FOG_COLOR, fogColor);// 霧色

glFogf(GL_FOG_MODE, GL_EXP2);// 顏色模式

glFogf(GL_FOG_START, 200);// 霧近端範圍

glFogf(GL_FOG_END, 1000);// 霧遠端範圍

glFogf(GL_FOG_DENSITY, 0.06f);// 密度

}

 

OpenGL之粒子系統

OpenGL之粒子系統

『粒子系統』由多個『粒子』,每個『粒子』都有其獨立『屬性』如『尺寸』『顏色』『速度』,每個『粒子』都可獨立運作.當大量『粒子』同時運作便可產生其特的3D效果.『飄雪』『流星』『爆炸』『飛機引擎火焰』.

『飄雪』演示程式在天空落雪,時用『廣告牌』技術另廣告牌總是對這鏡頭.下載『飄雪

粒子系統大約分為

1.初此化粒子系統

2.生成粒子

3.更新粒子系統

4.渲染粒子系統

基本『粒子』屬性 簡介
位置 3D空間中的座標
速度 基本上粒子都會移動,你需要方向向量再乘以時間而求得速度
尺寸 粒子的尺寸可能變化
重量 外力對粒子影響
顏色 為粒子設定渲染顏色
形態 常見『點』『直線』『紋理四邊形』
宿主 粒子擁有者
生命週期 粒子存活時間

定義粒子結構

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float energy;// 生命週期(秒)

float size;// 尺寸

float weight;// 重量

float color[4];// 顏色

}PARTICLE,*PARTICLE_PTR;

『粒子系統』屬性 簡介
粒子隊列 要管理的所有粒子
數量 粒子量
位置 活動範圍中心位置
範圍 粒子活動範圍
屬性 常見『點』『直線』『紋理四邊形』
紋理 把紋理存儲在『粒子系統』

Particle(粒子系統)

typedef struct PARTICLE_SYSTEM_TYP{

int          attr;// 屬性

PARTICLE_PTR array;// 數組

int          count;// 實制個數

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     velocity_variation;// 速度變量

float        energy;// 生命週期(秒)

//有效範圍

float        height;// 高

float        width; // 寬

float        depth; // 深

float        size;// 尺寸

TEXTURE_PTR  texture;// 紋理

}PARTICLE_SYSTEM, *PARTICLE_SYSTEM_PTR;

1.初此化粒子系統

bool Init_Particle(PARTICLE_SYSTEM_PTR system, int count,

VECTOR3D_PTR origin, VECTOR3D_PTR velocity, VECTOR3D_PTR variation,

float height, float width, float depth,float size,float energy,

TEXTURE_PTR texture)// 紋理

{

if (system == NULL)

return false;

memset(system,0,sizeof(PARTICLE_SYSTEM));// 清空

system->count = count; // 粒子數量

system->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

system->texture = texture;// 紋理

system->size    = size;// 尺寸

system->height  = height;// 高

system->width   = width;// 寬

system->depth   = depth;// 深

Copy_VECTOR3D(&system->origin, origin);// 原點

Copy_VECTOR3D(&system->velocity, velocity);//  速度與方向

Copy_VECTOR3D(&system->velocity_variation, variation);//  速度變量

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i)

{

particle = &system->array[i];// 粒子

Buid_Particle(system, particle);

}

return true;

}

2.生成粒子

void Buid_Particle(PARTICLE_SYSTEM_PTR  system, PARTICLE_PTR  particle)

{

particle->energy = system->energy;// 生命週期(秒)

particle->size   = system->size;// 尺寸

// 粒子位置

particle->pos.x = system->origin.x + FRAND_RANGE1() * (system->width /2.0f);// x

particle->pos.y = system->origin.y + FRAND_RANGE1() * (system->height/2.0f);// y

particle->pos.z = system->origin.z + FRAND_RANGE1() * (system->depth /2.0f);// z

// 粒子速度

particle->velocity.x = system->velocity.x  ;

particle->velocity.y = system->velocity.y  ;

particle->velocity.z = system->velocity.z  ;

}

3.更新粒子系統

void Update_Particle(PARTICLE_SYSTEM_PTR  system,float time)

{

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

VECTOR3D vector;

Scale_VECTOR3D(&vector, &particle->velocity, time);

Add_VECTOR3D(&particle->pos, &particle->pos, &vector);// 移動粒子

// 判斷有無超出返圍

particle->energy = particle->energy – time;// 計算生命週期

if (particle->energy < 0 && particle->pos.y < 0)// 生命週期

Buid_Particle(system, particle);

}

}

4.繪畫貝粒子系統(紋理)

bool Draw_Texture_Particle(PARTICLE_SYSTEM_PTR system,

CAMERA3D_PTR camera, BILLBOARD_PTR billboard)

{

glPushMatrix();// 壓入矩陣

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_LIGHT0);// 禁用0號燈光

glEnable(GL_DEPTH_TEST);// 啟用深度測試

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_ALPHA_TEST);// 啟用透面測試

glAlphaFunc(GL_GREATER, 0);// 透面色

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

//檢查3D球體是否為於視錐體內

if (Compute_Sphere_In_Frustum(&camera->frustum, &particle->pos, particle->size) == true)

{   // 繪畫Billboard廣告牌

Draw_Billboard(billboard, &particle->pos, particle->size);

}

}

glPopAttrib();// 彈出屬性

glPopMatrix();// 彈出矩陣

return true;

}

 

OpenGL之廣告牌

OpenGL之廣告牌

當我地在游中渲染大量3D模型時,如果3D模型離視口很遠時,在屏幕上繪畫可能只是幾個像素,其中解卻方法是3D模型轉為2D圖像,然後作為紋理映射到四邊形.當視口靠近時再切換為3D模型.廣告牌作用是確保2D圖像此終面向視口.演示程式中按前後鍵移動相機,按左右鍵旋轉相機,仙人掌此終面向視口:下載

1.獲取當前『模型視圖矩陣』

typedef struct MATRIX4X4_TYP{

float M[4][4]; //以數組形式儲存

} MATRIX4X4, *MATRIX4X4_PTR;

glGetFloatv(GL_MODELVIEW_MATRIX, (float*)matrix.M);

2.提取視口右則向量

VECTOR3D  right;

Init_VECTOR3D(&right, matrix.M[0][0], matrix.M[1][0], matrix.M[2][0]);

3.提取視口右則向量

VECTOR3D  up;

Init_VECTOR3D(&up,matrix.M[0][1],matrix.M[1][1], matrix.M[2][1]);

4.移動廣告牌(四邊形)中心位置

glPushMatrix();

glTranslatef(position->x, position->y, position->z);

5.準備繪畫廣告牌(四邊形)

glBegin(GL_QUADS);

6.法線朝向

glNormal3f(0.0f, 0.0f, 1.0f);

7.重新計算廣告牌(四邊形)頂點位置,以實現廣告牌法線此終朝向視口

VECTOR3D  Vertex;

glTexCoord2f(0.0f, 0.0f); glVertex3fv(((right + up) * -size).M); // 左上角

glTexCoord2f(1.0f, 0.0f); glVertex3fv(((right – up) * size).M); // 右下角

glTexCoord2f(1.0f, 1.0f); glVertex3fv(((right + up) * size).M); // 右上角

glTexCoord2f(0.0f, 1.0f); glVertex3fv(((up – right) * size).M); // 左上角

8.完成繪畫廣告牌(四邊形)

glEnd();

glPopMatrix();// 彈出矩陣

OpenGL之NURBS(非均勻有理B樣條)

OpenGL之NURBS(非均勻有理B樣條)

貝賽爾(Bezier)曲線』(下面簡稱為『曲線』)跟據其『控制點』數目分類.擁有三個控制點『曲線』稱為『二次曲線』. 擁有四個控制點『曲線』稱為『三次曲線』.

隨著『曲線』控制點數目增加.曲線的平滑度可能會被大量控制點拉扯而被破壞.

所以引入NURBS(非均勻有理B樣條).『B樣條』其實就是『曲線』,只不過『B樣條』分為多個『曲線段』. 每個『曲線段』擁有四個控制點.將整條『曲線』分為若干『曲線段』,等於將多個『三次曲線』合併為『B樣條』.

『B樣條』有四個『控制點』,而每個『控制點』擁有兩個『節點』.其值可以取u和v域範圍任意值.『節點』影響『控制點』對『曲線』影響程度.這也是Bezier與NURBS區別之處.NURBS演示程式下載:

1.生成NURBS對象

GLUnurbsObj * object = gluNewNurbsRenderer();

2.定義八個『節點』

float knots[8] = { 0,0, 0,0, 1,1, 1,1 };

3.生成NURBS四個『控制點』

float nurbs[4][4][3] ;

4.設置NURBS曲面控制點,形成坨峰形

for (int u = 0; u < 4; ++u){

for (int v = 0; v < 4; ++v){

nurbs[u][v][0] = 3.0f*((float)u – 1.5f);

nurbs[u][v][1] = 2.0f*((float)v – 1.5f);

if ((u == 1 || u == 2) && (v == 1 || v == 2))

nurbs[u][v][2] = 3.0f;

else

nurbs[u][v][2] = -1.0f;

}

}

5.設置多邊形最大長度

gluNurbsProperty(object,GLU_SAMPLING_TOLERANCE,30.0f);

6.以多邊形繪製曲面

gluNurbsProperty(object,GLU_DISPLAY_MODE,GLU_FILL);

gluNurbsProperty()用於設置採樣以及如何繪畫NURBS

void gluNurbsProperty(GLUnurbs *nobj,GLenum property,GLfloat value);

參數 數值 簡介
nobj GLUnurbsObj * object; NURBS對象
property GLU_SAMPLING_TOLERANCE: 設置多邊形最大長度
GLU_DISPLAY_MODE: 多邊形渲染模式GLU_FILL:多變形實體填充
value   依property而定

7.設置NURBS曲面

gluBeginSurface(object);

8.準備繪製NURBS曲面

gluNurbsSurface(object,8,knots,8, knots,43,3,(GLfloat)&nurbs[0][0][0],4,4, GL_MAP2_VERTEX_3);

函式定義

void gluNurbsSurface(GLUnurbsnobj,GLint sknot_count,floatsknot,GLint  tknot_count,GLfloattknot,GLint s_stride,GLint t_stride,GLfloatctlarray,GLint sorder,GLint torder,GLenum type);

參數 簡介
nobj NURBS對象
sknot_count u節點個數
sknot u節點
tknot_count v節點個數
tknot v節點
s_stride u控制點距離
t_stride v控制點距離
ctlarray 指向控制點
sorder u方向控制點個數
torder v方向控制點個數
type 曲面類型

9.完成渲染

gluEndSurface(object);

 

 

OpenGL之貝賽爾(Bezier)曲面

OpenGL貝賽爾(Bezier)曲面

貝賽爾(Bezier)『曲面』與『曲線』相比不同之處在於多咗v空間域.曲面函式s(u,v).曲面演示程式按空格鍵在線框、填充、紋理模式中切換:下載

1.對曲面進行柵格映射

void glMap2f(GLenum target,GLfloat u1,GLfloat u2,GLint ustride,GLint uorder,GLfloat v1,GLfloat v2,GLint vstride,GLint vorder,const GLfloat *points);

函式示例:

glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3,u,  0,1, 3v,3,  (GLfloat)bezier->control_array);

參數 簡介
target 控制點類型
u1,u2 u空間域參數的定義域(u1~u2)
ustride u空間域頂點跨度
uorder u空間域頂點個數
v1, v2 v空間域參數的定義域(u1~u2)
vstride v空間域頂點跨度
vorder v空間域頂點個數
points 定點數組

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用貝賽爾(Bezier)曲面

glEnable(GL_MAP2_VERTEX_3);

3.定義定義均勻的柵格,並設置間隔個數

void glMapGrid2f(GLint un,GLfloat u1,GLfloat u2,GLint vn,GLfloat v1,GLfloat v2);

函式示例:

glMapGrid2f(10,0, 10, 10, 0, 10);

參數 簡介
un u域[u1~u2]間隔個數,必須為正
u1 u1=0
u2 u2=un
vn v域[v1~v2]間隔個數,必須為正
v1 v1=0
v2 v2=vn
  1. 根據柵格設置繪製曲面

void glEvalMesh2(GLenum mode,GLint i1,GLint i2,GLint j1,GLint j2);

函式示例:

glEvalMesh2(GL_FILL, 0, 10, 0, 10);

參數 簡介
mode 繪畫模式

GL_POINT:點

GL_LINE:直線

GL_FILL:填充

i1,i2 u空間域的範圍
j1,j2 v空間域的範圍

5.為曲面生成法線用於光照

glEnable(GL_AUTO_NORMAL);

6.對曲面進行紋理映射

glMap2f(GL_MAP2_TEXTURE_COORD_2, 0,1,2, 2,0,1, 4,2,(GLfloat*)bezier->texture_coord);

對曲面進行紋理映射glMap2f() 簡介
target 紋理座標(s,t):GL_MAP2_TEXTURE_COORD_2(睇上表)
u1,u2 s空間域:[0~1]
ustride s紋理座標頂點跨度:2
uorder s空間域頂點個數:2
v1,v2 t空間域:[0~1]
vstride t紋理座標頂點跨度:4
vorder t空間域頂點個數:2
points 紋理座標:{0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {1.0, 1.0}

4.啟用曲面紋理座標

glEnable(GL_MAP2_TEXTURE_COORD_2);

OpenGL之貝賽爾(Bezier)曲線

OpenGL貝賽爾(Bezier)曲線

OpenGL支持貝賽爾(Bezier)曲線繪畫,貝賽爾曲線由起點,終點和控制點組成,並且具有平滑的運動軌跡.控制點用於定義曲線的形狀,像磁石將曲線向控制點的位置吸引.上圖的演示程式通過滑鼠點選屏幕放置『控制點』繪畫貝賽爾(Bezier)曲線.ESC鍵清空『控制點』:下載

1.設置貝賽爾(Bezier)曲線

void glMap1f(GLenum target,GLfloat u1,GLfloat u2,GLint stride,GLint order,const GLfloat *points);

參數 簡介
target 控制點類型
u1, u2 u參數的定義域(0~1)
stride 頂點跨度
order 曲線頂點個數(由起點,終點和控制點組成)
points 指向曲線頂點數組由(起點,終點和控制點組成)

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用控制點

glEnable(GL_MAP1_VERTEX_3);

3.現在可以繪畫曲線,100條直線繪畫貝賽爾(Bezier)曲線,以均等間隔繪畫.

glBegin(GL_LINE_STRIP);

for (i = 0; i <= 100; ++i)

glEvalCoord1f((float)i / 100.0f);

glEnd();

上面的代碼可以簡化為

glMapGrid1d(100, 0.0f, 1.0f);// 定義定義均勻的間格

glEvalMesh1(GL_LINE,0, 100);// 繪製曲線

4.完成繪畫後需要繪畫控制點

glPointSize(5);

glBegin(GL_POINTS);

for (i = 0; i < bezier->countol_count; ++i)

           glVertex3fv(bezier->control_array[i].M);

glEnd();

 

OpenGL之二次曲面

OpenGL之二次曲面

如果要繪製『球體』,『圓柱』,『圓錐』,『圓盤』除了可以自已編寫圖形庫.還可以時用OpenGL Utility Library(OpenGL實用庫)提供的高級圖形支持.簡稱為GLUT.要下載可到官網下載: https://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip 但windows版只支持x86.演示程式:下載

1.創建二次曲面對象,成功返回指向二次曲面的指針,否則返回NULL

GLUquadric*gluNewQuadric(void);

2.設置繪製風格

void gluQuadricDrawStyle(GLUquadric *quadObject,GLenum drawStyle);

drawStyle(繪畫樣式) 簡介
GLU_FILL 實體填充渲染
GLU_LINE 以線框模式渲染
GLU_SILHOUETTE 以輪框模式渲染,排除多邊形的共線.
GLU_POINT 以頂點模式渲染

3.設置法線生成

法線狀態用於設置二次曲面的表面法線的生成方式.

void WINAPI gluQuadricNormals(GLUquadric *quadObject,GLenum normals);

Normals(法線狀態) 簡介
GLU_NONE 不生成法線
GLU_FLAT 每個多邊形生成一個法線(單調明暗處理)
GLU_SMOOTH 每個頂點生成一個法線(平滑明暗處理) (默認值)

3.設置法線方向

設置生成法線的方向,內部還是外部.

Void gluQuadricOrientation(GLUquadric *quadObject,GLenum orientation);

Orientation(方向) 簡介
GLU_OUTSIDE 法線指向外側(默認值)
GLU_INSIDE 法線指向內側

4.設置紋理座標

紋理狀態是否為圖形生成紋理座標

void gluQuadricTexture(GLUquadric *quadObject,GLboolean textureCoords);

textureCoords 簡介
GL_TRUE 生成紋理座標
GL_FALSE 不生成紋理座標(默認值)

5.當不使用二次曲面後需要釋放資源

void gluDeleteQuadric(GLUquadricObj *state);

 

二次曲面繪畫

1.繪畫偏平的圓盤,中間可以有個圓孔

void gluDisk(GLUquadric *qobj,GLdouble innerRadius,GLdouble outerRadius,GLint slices,GLint loops);

繪畫偏平的扇形圓盤

void gluPartialDisk(GLUquadric *qobj,GLdouble innerRadius,GLdouble outerRadius,GLint slices,GLint loops,GLdouble startAngle,GLdouble sweepAngle);

參數 簡介
qobj 二次曲面對象
innerRadius 內圓直徑,若為0則是閉孔,大於零則中心有個孔
outerRadius 外圓直徑
Slices 繞z軸的細分數.此值越大圓盤越圓
Loops 同軸圓盤的邊形細分數
startAngle 扇形起此角度
sweepAngle 扇形圓弧角度

2.繪畫圓錐和圓柱

void gluCylinder(GLUquadric *qobj,GLdouble baseRadius,GLdouble topRadius,GLdouble height,GLint slices,GLint stacks);

分別有底座半徑與柱頂半徑若其一為0為圓錐,若兩值不相等則為錐形圓柱,若兩值相等則為圓柱. 底座圓面與柱頂圓面是繪畫的,你需要使用gluDisk()另外繪畫

圓柱沿Z軸繪畫,底座為於Z=0, 柱頂位於Z=height(高度)

參數 簡介
qobj 二次曲面對象
baseRadius 底座半徑 z=0
topRadius 柱頂半徑z=height
height 圓柱的高度
slices 繞Z軸的細分數.此值越大圓柱越圓
stacks 延Z軸的細分數.此值越高返射光的效果越好.

3.繪畫球體,按給定半徑以原點為中心進行繪製

void gluSphere(GLUquadric *qobj,GLdouble radius,Glint slices,GLint stacks);

參數 簡介
qobj 二次曲面對象
radius 球體的半徑
slices 繞z軸的細分數(相當於經線)
stacks 沿z軸的細分數(相當於緯線)

 

Windows10之滑鼠衝突

Windows10之滑鼠衝突

近日發現Windows10下的滑鼠出現奇怪問題,如『開始畫面』點選無反應(重新關開滑鼠可恢復).瀏覽器的tab分頁自動切換(跳轉).這種現象又唔似中毒,霖左好幾日.因為貪方便裝左兩隻無線滑鼠問題才出現的.果斷拔掉一隻滑鼠所有問題都自動消失.原來是兩隻無線滑鼠(Logitech&Microsoft)衝突引起.

仿生蜘蛛

仿生蜘蛛
仿生蜘蛛

之前介紹『六腳類蜘蛛』機器人雖然可以行走並翻滾,但它的關節非常粗.與德國festo(費斯托) 的『仿生蜘蛛』(BionicWheelBot)相比就差太遠.『仿生蜘蛛』通過模仿摩洛哥後翻蜘蛛(cebrennus rechenbergi)的轉身與翻滾.通過wifi使用平板電腦進行控制.

身驅和長腿使用尼龍進行3D打印,但細睇之下有點唔似.因為尼龍的熔點比ABS更好,收縮比ABS更大,打印較大尺寸效果唔是很好.而且光潔度極高睇唔到線材的紋路.似模具內噴砂.

『仿生蜘蛛』共有8只腳,每只腳的膝關節和肩關節都裝有彈簧,與15個小型馬達.行走與轉身時只用六隻尖腳,中間的一對支腳收在腹下.每走一步三隻尖腳支撐並穩定身體,另外三隻尖腳提高並前移然後落地.轉身時中間一對尖腳作為支撐,另外四隻尖腳離地完成轉身.但行走速度有待提高.

如果『仿生蜘蛛』要進行翻滾動作,將六隻尖腳收起變為『車輪』,將平時收在腹下的只支腳伸出,通過不斷伸腿進行翻滾,向前伸就後翻,向後伸就前翻.甚至在一定坡度(5度)下翻滾,速度要比行走快得多.

但『仿生蜘蛛』結構比較複雜,即使量產售價也比較高.

特幀 數值
身長 展開570mm
身高 企高238mm
身寬 展開796mm
腳長 344mm
Gauge 164 mm
車輪直徑 267mm
關節 15個馬達
材料:身驅和腿 使用尼龍進行3D打印
電池 鋰電池7.4伏1000毫安
無線電模塊 866 MHz(WIFI)
無線電遙控 平板電腦
處理器 STM32F4
驅動器 14×自動鎖緊蝸輪單元
傳感器 1×BNO055絕對定向傳感器

 

仿生狐蝠

仿生狐蝠
仿生狐蝠

德國festo(費斯托) 的『仿生海鷗』通氦氣的浮力和兩翼的拍打在空中飛行.而最新的『仿生狐蝠』更無需注入氦氣.通過模擬『狐蝠』的特徵倒掉在半空,然後展翼並跳下.接著震翼在空中自主飛翔

狐蝠(Flying Fox)也是一種蝙蝠.而蝙蝠的特徵是具有彈性的翼膜,翼膜從指骨一直延伸至腳骨.在飛行時用指骨控制翼膜的曲率,即使在慢速飛行時也能獲得足夠最大爬升力『仿生狐蝠』通過『馬達』與『變速箱』不斷震動兩翼以此提供足夠爬升力.

festo(費斯托)研製出薄輕但極有韌性織物,由兩塊氣密薄膜和一塊氨綸織物組成.通過蜂窩結構織造的翼膜有45000個織造點.即使翼膜出現裂紋也不會擴大.即使翼膜出現部分損傷『仿生狐蝠』也能繼續飛行.而且由於翼膜具有極強彈性,即使收起雙翼時也不會褶皺.而且可以分別對單翼進行折疊.

喂一不足的是需通過地面的2部紅外攝像機,捕足兩翼與後肢來識別『仿生狐蝠』的運動.然後計算飛行路徑並引導『仿生狐蝠』的整個飛行過程

『仿生狐蝠』兩翼展開228cm,體長87cm,而體重僅580g.兩米多翼展而體重只有一斤多一點. 這樣『仿生狐蝠』兩翼負載即低而且結構簡單. 如果festo(費斯托)量產『仿生狐蝠』其銷量肯定會超過航拍無人機.

結構 參數
翼幅 228 cm
體長 87 cm
體重 580g
雙翼財料 碳纖維
翼展面積 蜂窩結構織物
機身外殼 泡沫
馬達 40瓦無刷電機
運動跟蹤系統 2部紅外攝像機

 

 

OpenGL之模板緩存

OpenGL之模板緩存

使用『模板緩存』可以將屏幕中的某些部分從視野中屏蔽.其中的特效是『鏡像』效果.鏡像(倒影)演示程式:下載

1.設置像素格式時對PIXELFORMATDESCRIPTOR對cStencilBits設置為 16bit『模板緩存』.

PIXELFORMATDESCRIPTOR pdf;

pfd.cStencilBits = 16;

2.要啟用『模板緩存』

glEnable(GL_STENCIL_TEST);

3.設置模板操作

void glStencilOp(GLenum fail,GLenum zfail,GLenum zpass);

fail:模板測試失敗

zfail:模板測試通過,但深度測試失敗

zpass:模板測試與深度測試均通過

操作 簡介
GL_KEEP 保持當前值
GL_ZERO 將模板緩存的值設定為0
GL_REPLACE 將模板緩存的值設定為glStencilFunc()的參考值ref.
GL_INCR 增加當前模板的緩存值
GL_DECR 減小當前模板的緩存值
GL_INVERT 將模板緩存的值反轉

4.設置模板比較程序

void glStencilFunc(GLenum func,GLint ref,GLuint mask);

func:比較程式

ref:參考值

mask:模板掩碼

比較程式 簡介
GL_NEVER 總是失敗
GL_LESS 條件比較:if (ref & mask) < (stencil & mask).
GL_LEQUAL 條件比較:if (ref & mask) ≤ (stencil & mask).
GL_GREATER 條件比較:if (ref & mask) > (stencil & mask).
GL_GEQUAL 條件比較: if (ref & mask) ≥ (stencil & mask).
GL_EQUAL 條件比較: if (ref & mask) = (stencil & mask).
GL_NOTEQUAL 條件比較: if (ref & mask) ≠ (stencil & mask).
GL_ALWAYS 總是允許

繪畫鏡像函式代碼演示:

1.禁用深度測試

glDisable(GL_DEPTH_TEST);

2.禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

3.啟用模板測試

glEnable(GL_STENCIL_TEST);

4.將地板繪製到模板緩存中,引用參考值ref

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

glStencilFunc(GL_ALWAYS, 1, 1);

5.繪畫地板後雖然還未睇到地板,但已在模板緩存中生成孔,在孔中的多邊形將不被繪製

Draw_Floor();

6.啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

7.啟用深度測試

glEnable(GL_DEPTH_TEST);

8.設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 1);

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

9.壓入模型矩陣.準備繪製”鏡像”

glPushMatrix();

10.返轉境像

glScalef(1.0f, -1.0f, 1.0f);

11.繪畫鏡像(倒影)

Draw_Mirror();

11.彈出模型矩陣結束鏡像繪製

PopMatrix();

12.禁用模板測試

glDisable(GL_STENCIL_TEST);

13.以50%繪製透明地板, 啟用混合

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

glColor4f(1.0f,1.0f,1.0f,0.5f);

14.繪畫地板

Draw_Floor();

15.禁用混合

glDisable(GL_BLEND);

 

波士頓動力之Atlas

波士頓動力之Atlas
波士頓動力之Atlas

『Atlas』是波士頓動力研發雙足仿真機器人.其控制系統通過協調手臂,軀幹和雙腿,實現『搬箱』(唔可以太重最多搬起一包米)『跳上』『跳落』『空中360度轉體』『後空翻』等動作. (後空翻好多人都做唔到)

『Atlas』通過3D打印技術使其更輕更緊湊,讓液壓系統置於腿部.並重新設計更輕巧的步進馬達.使其具有高強度外殼和低重量比.通過『立體視覺』,『距離感應』和其他傳感器使『Atlas』能在其崎嶇地面上步行.並在『推』『撞』下保持平衡.就算跌低都可以爬起身.

參數 數值
高度 1.5m
體重 75kg
負重 11kg
動力 電動
驅動 液壓
感知 激光雷達與三維視覺系統
關節 28個

 

 

波士頓動力之Handle

波士頓動力之Handle
波士頓動力之Handle

『Handle』是美國波士頓動力種製作的機器人,它將兩個車輪裝在雙腿,結合波士頓動力之前製作的『四足動物』和『兩足機器人』的動力學、平衡和移動操縱原理.它的結構非常簡單只有10個驅動關節.車輪在崎嶇地表都能高速走動『落斜』『落樓梯』『自轉』『單邊橋』『跳欄』.雙腿幾乎可以在任何地方行走.比起通過雙腿或四足走動的機器人更高效.通過雙腿與車輪結合『Handle』具有兩全其美的優點.充滿電後大約可行使24km.
『Handle』通過液壓制動可以拿起超過45KG(100磅)的負重,但雙腿卻很細可以在狹小的空間內移動.所有關節都自動進行了協調,實現高難度動作.如果量產搬運工要失業.

參數 數值
高度 2m
體重 105kg
速度 14.5km/h
跳高 1.2米
負重 45kg
動力 電池
驅動 液壓與電動
感知 深度相機
關節 10個

 

 

波士頓動力之SpotMini

波士頓動力之SpotMini
波士頓動力之SpotMini

SpotMini是波士頓動力公司設計的小型四足機器人,可在家居和辦公環境靈活走動.它有兩個版本,無頭版只重25kg,裝上『雞頭』也只是30kg.完全使用電池作為動力,是波士頓動力目前最安靜的計器人.完全充電後可以活動90分鐘.

SpotMini繼承了它的大佬SPOT的所有機動性, 而且增加五軸自由活動的手臂.和增強型傳感器拾取物體和處理物體的能力.傳感器裝置包括『立體相機』、『深度相機』、『姿態測量器』以及裝在四肢的『位置傳感器』與『力量傳感器』.這些傳感器都有助於加強SpotMini導航和移動的操控能力.

更示範『無頭SpotMini』因開唔到門而Call『雞頭SpotMini』開門的過程.然後一起通過.

參數 數值
高度 0.84m
體重 30kg
負重 14kg
動力 電池
驅動 電動
感知 三維視覺系統
關節 17個

 

OpenGL之文本渲染

OpenGL之文本渲染

在OpenGL進行文本渲染.有以下幾種通用方法:

1.點陣字體

2.位圖字體,利用透面的背景色進行渲染.此方法更通用

4.2D位圖字體(OpenGL自動生成位圖,只支持Windows)

5.3D輪廓字體(OpenGL自動3D字體模型,只支持Windows)

6.3D輪廓字體支持紋理映射(OpenGL自動紋理座標,只支持Windows)

 

OpenGL之紋理映射字體

OpenGL之紋理映射字體

因為『3D輪廓字體』只是填充純色,但可以對『3D輪廓字體』應用紋理映射,以增強其外觀效果.紋理座標可以讓OpenGL自動生成.演示程式下載:

1.讓OpenGL為文本生成紋理座標,並固定於3D模型

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

2.載入紋理

Load_File_Texture(&font3D->texture, hInstance, filename);

3.綁定紋理

glBindTexture(GL_TEXTURE_2D, texture->texID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

OpenGL之3D輪廓字體

OpenGL之3D輪廓字體

之前一直使用Windows自帶字體輸出文本,但只限2D的『位圖字體』.在Windows下有獨有輪廓字體函式.讓文本具有一定的厚度.讓所有的Windows字體轉換成3D字體.演示程式可輸入英文字符,下載程式:

1.存儲輪廓字體位置和方向,生成256個GLYPHMETRICSFLOAT變量

GLYPHMETRICSFLOAT gmf[256];

2.創建大小為256個字符的顯示列表,對所有256個ASCII碼支持.

UINT base;// 顯示列表的BASE ID

base = glGenLists(256);

3.創建字體句柄

HFONT font;

font=CreateFont(1,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,NTIALIASED_QUALITY,FF_DONTCARE|DEFAULT_PITCH,”Arial”);

4.為字體選擇設備環境

SelectObject(hDC,font);

5.創建3D輪廓字體,每個ASCII對應一個輪廓字體

wglUseFontOutlines(hDC,0,256,base,0.0f,FONT3D_DEPTH,WGL_FONT_POLYGONS,gmf);

6.複製當前矩陣並壓棧

glPushMatrix();

7.將文本定位於場景空間的中央.計算文本的長度

length = (int)strlen(text);

for(int i=0;i<length; ++i)

{

char  c = text[i];

width = width + gmf[c].gmfCellIncX;

if(gmf[c].gmfCellIncX > height )

height = gmf[c].gmfCellIncX;

}

8.設定文本座標

glTranslatef(posX – (width0.1f)/2.0f,posY – (height0.1f)/2.0f ,posZ + (FONT3D_DEPTH*0.1f)/2.0f);

9.顯示列表屬性壓棧

glPushAttrib(GL_LIST_BIT);

10.載入顯示列表ID

glListBase(base);

11.渲染文本

glCallLists((int)strlen(text),GL_UNSIGNED_BYTE,text);

12.顯示列表屬性出棧

glPopAttrib();

OpenGL之位圖字體

OpenGL之位圖字體

要在屏幕上顯示文本可以使用Windows自帶字體進行渲染.比起之前使用『點陣字體』方便簡潔很多,但此方法只式用於Windows.演示程式可輸入英文字符,下載程式:

1.創建96個顯示列表IDs,存儲生成的字符位圖

UINT base;// 顯示列表的BASE ID

base = glGenLists(96);//創建大小為96BYTE的顯示列表

2.創建顯示列表後,使用CreateFont()創建字體

HFONT font;// 字體句柄

font=CreateFont(12,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,FF_DONTCARE | DEFAULT_PITCH,”Courier”);

3.為字體選擇一個設備環境

SelectObject(hDC,font);

4.從系統載入字體文檔並生成位圖.

wglUseFontBitmaps(hDC,32,96,base);

5.設定屏幕位置

glRasterPos2i(xPos,yPos);

7.顯示列表屬性壓棧

glPushAttrib(GL_LIST_BIT);

6.載入顯示列表ID

glListBase(base-32);

7.渲染文本

glCallLists((int)strlen(text),GL_UNSIGNED_BYTE,text);

8.顯示列表屬性出棧

glPopAttrib();

USB3.0前置分線器

USB3.0前置分線器

以前買過幾款USB分線器質量都不太好.在網上購得這款USB3.0分線器.支持一拖四.需要主板內置USB3.0/3.1(SuperSpeed USB)端口(19針)與SATA-5V電源.佔用3.5寸FDD(軟驅位).芯片是臺灣的創惟Genesys Logic的GL3520.經測試我的Seagete移動硬碟插主板的後置USB是拖唔起的(除非換成雙USB供電).但這個USB3.0分線器帶有5V電源所以供電充足.喂一的缺點是不支持USB3.1

OpenGL之視錐體裁剪

OpenGL之視錐體裁剪

圖形引擎繪製任何睇不見(視錐截體之外)的任何物體,都會浪費保貴GPU資源.相機的視錐體定義你所能夠梯到的3D世界空間.視錐體由相機的位置和視野組成.視錐體由六個平面組成.只要進入視錐體內的3D模型都應被渲染.(被其它3D模型遮檔除外).位於視錐體之外則不必渲染.

OpenGL會自動丟棄在視錐體之外的三角形.但OpenGL是逐個三角形進行視錐體裁剪.當你有大量(幾萬個)的三角形進入OpenGL裁剪隊列時.會大大地降低程式性能.

最簡單的優化方式是定義3D球形容器,用於包裹你的3D模型.然後使用視錐體與3D球形進行測試.如果3D球形完全在視錐體之外.即可放棄渲染3D模型.如果3D模型部分或全部進入視錐體內,則交由OpenGL繼序進行三角形裁剪

演示程式中有幾百個隨機移動球體,按空格鍵啟用或禁用『視錐體裁剪』.未啟用之前不到200幀.一但啟動『視錐體裁剪』則超過1000幀.性能大幅提高五倍(視渲染球體的個數而定).程式下載:

 

定義3D球體

typedef struct SPHERE3D{

float    x, y, z;// 中心點

float    radius;// 球體半徑

}SPHERE3D,* SPHERE3D_PTR;

 

定義3D平面

typedef struct PLANE3D_TYP{

float A, B, C;// 平面法線向量

float D;//平面到原點最近距離

}PLANE3D,*PLANE3D_PTR;

 

定義視錐體

typedef struct FRUSTUM_TYP {

PLANE3D planes[6];//6個平面

}FRUSTUM, *FRUSTUM_PTR;

 

1.平面方程式定義:

Ax + By + Cz + D = 0

A,B,C:平面的法線向量

D:從平面到原點的距離

x,y,z:平面上的任意點

結果為零:該點落在平面上

結果為正:該點為于平面的前方

結果為負:該點為於平面的後方

 

2.確定視錐體尺寸,獲取投影矩陣和模型視口矩陣

glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

 

3.投影矩陣乘以模型視口矩陣

glPushMatrix();

glLoadMatrixf((GLfloat*)&projection);

glMultMatrixf((GLfloat*)&modelview);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

glPopMatrix();

 

4.提取視錐體的六個平面

int scale = (row < 0) ? -1 : 1;

row = abs(row) – 1;

plane->A = mat->M[0][3] + scale * mat->M[0][row];

plane->B = mat->M[1][3] + scale * mat->M[1][row];

plane->C = mat->M[2][3] + scale * mat->M[2][row];

plane->D = mat->M[3][3] + scale * mat->M[3][row];

平面(plane) 行(row)
左則 1
右則 -1
底部 2
頂部 -2
近端 3
遠端 -3

 

5.平面歸一化

float length = sqrtf(plane->A * plane->A + plane->B * plane->B + plane->C * plane->C);

plane->A /= length;

plane->B /= length;

plane->C /= length;

plane->D /= length;

 

6.檢查3D球體是否為於視錐體內

frustum:視錐體

sphere:球體

返回值:若在視錐體內返回TRUE,否則反回FALSE

bool Compute_Sphere_In_Frustum(FRUSTUM_PTR frustum, SPHERE3D_PTR sphere)

{

float dist;

for (int i=0;i<6;++i)

{// 判斷球體是否為與六個平面之前

PLANE3D_PTR plane = &frustum->planes[i];

// 計算點與平面的距離,若為正在視錐體內,若為負在視錐體外

dist = plane->A * sphere->x + plane->B * sphere->y + plane->C * sphere->z + plane->D;

if (dist <= -sphere->radius)

return false;// 在視錐體外

}

return true; // 在視錐體內

}

 

OpenGL之數組鎖定

OpenGL之數組鎖定

OpenGL允許鎖定(lock)與解鎖(unlock)數組.當鎖定(lock)數組後,數據便不能更改.當存在大量共用的頂點,並且進行多次操作.能大大地提高運行速度

 

數據初此化後鎖定數組

void glLockArraysEXT(GLint first, GLsizei count);

first:頂點索引

count:頂點個數

 

程序退出時對數組解鎖

void glUnlockArraysEXT(void);

 

演示程式中,因為地形數據不會改變,對其進行鎖定.按空格鍵可切換鎖定(lock)與解鎖(unlock).幀數大約提升10%.只是兩行代碼就有不小的性能改進.下載:

OpenGL之頂點數組與多紋理映射

OpenGL之頂點數組與多紋理映射
將頂點數組『VertexArray』應用於多紋理『Multitexture』映射,由於每個紋理單元都有其自已的狀態.要將每個紋理單元都單度地啟用頂點數組,並且為其每個紋理單元設定紋理座標的頂點數組.通過常試可以將多個2D紋理(GL_TEXTURE_2D)進行映射,但2D紋理與1D紋理(GL_TEXTURE_1D)多次償試都無法將『頂點數組』應用『紋理數組』映射暈.演示程式按空格鍵切換『多紋理映射』草地紋理與高度紋理.兩者幀數相約.下載:

 

要讓紋理單元使用頂點數組,必須先激活它.

void glClientActiveTexture(GLenum texture);

texture:紋理單元索引GL_TEXTURE0_ARB~GL_TEXTURE31_ARB

 

啟用紋理單元頂點座標數組

void glEnableClientState(GLenum array);

禁用紋理單元頂點座標數組

void glDisableClientState(GLenum array);

array:GL_TEXTURE_COORD_ARRAY 紋理座標數組

 

為紋理單元設定指定數組

void WINAPI glTexCoordPointer(Glint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

 

設置兩個紋理單元設置頂點數組

glClientActiveTexture(GL_TEXTURE0_ARB);// 激活紋理0 

glEnableClientState(GL_VERTEX_ARRAY); // 啟用頂點數組

glEnableClientState(GL_TEXTURE_COORD_ARRAY); // 啟用紋理座標數組

glVertexPointer(3, GL_FLOAT, 0, terrain->vertex_array);// 頂點數組

glTexCoordPointer(3, GL_FLOAT, 0, terrain->grass_texcoord);// 紋理數組

glClientActiveTexture(GL_TEXTURE1_ARB);// 激活紋理1

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glTexCoordPointer(3, GL_FLOAT,0, terrain->height_texcoord);// 紋理數組

glDrawArrays(GL_TRIANGLES, 0, polygon_num);// 繪畫所有當前以啟用的頂點數組

glDisable(GL_TEXTURE_1D);// 禁用紋理單元1

glClientActiveTexture(GL_TEXTURE0_ARB);// 重新激活紋理0,這個很重要否則會影響其它紋理貼圖

glDisableClientState(GL_VERTEX_ARRAY); // 禁用頂點數組

glDisableClientState(GL_TEXTURE_COORD_ARRAY); // 禁用紋理座標數組

glDisableClientState(GL_NORMAL_ARRAY);// 禁用法線數組

3D打印之平房

3D打印之平房
3D打印之平房
3D打印之平房

美德薩斯州 ICON公司利用大型 3D 打印機和水泥漿為源料興建房屋.從圖片梯到3D打印的只是牆體部分.水平平臺並非打印,而木制屋頂更是預製件.水泥噴嘴孔徑從圖片可以梯出應不小50mm.牆體的光潔度你就不要追求啦,牆身在3米以下應該可以保持垂直.牆體如果在加高肯定會有問題(Y軸平行).所以我講是平房.牆身並非實心(填充密度).這個問題不大只要牆身夠厚,剛性有一定保證.但牆體內無鋼筋唔知是否能抵禦颱風和地震.半日內建成能完成牆體打印.只要台大型 3D 打印機能快速部署.技術純熟加上廉價肯定會有市場.

FPS

FPS

FPS全稱為Frames Per Second.用於統計遊戲與影片每秒的渲染畫面(幀)次數.此值越高畫面越流暢,電影以每秒24格菲林進行播放.所以你的遊戲要流暢無停頓感.需要不低於24幀最好高於30幀.當然幀數越高越好.

FPS算法如下:

FPS = 100 * Frequency / (currentTime – startTime);

Frequency為時鐘頻率. currentTime與 startTime為前後兩次時鐘

 

Windows下你需要高精度計數器:

返回硬件級高精度時鐘頻率,若返回0代表系統不支持.

BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

返回硬件級高精度計數器,若返回0代表系統不支持.

BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);

LARGE_INTEGER:為64BIT結構

 

此兩個函式需要winbase.h頭文檔和Kernel32.LIB庫

#include <winbase.h>

#pragma comment(lib, “Kernel32.LIB”)

 

定義如下FPS結構:

typedef struct FPS_TYP {

LARGE_INTEGER  Frequency;// 計數器的頻率

LARGE_INTEGER  startTime;// 啟動時鐘

float Frames;// 每秒渲染幀數

int n;// 臨時幀計數器

}FPS,*FPS_PTR;

 

初此化高精度定時器

bool Init_FPS(FPS_PTR fps)

{

// 返回硬件支持的高精度計數器的頻率

if (QueryPerformanceFrequency(&fps->Frequency) == false)

return false;

// 獲取啟動時鐘

QueryPerformanceCounter(&fps->startTime);//

return true;

}

 

計算每秒渲染幀數,每100幀進行一次計算

float Get_FPS(FPS_PTR fps)

{

++fps->n;

if (fps->n > 100)

{

LARGE_INTEGER currentTime;

// 返回高精度計數器

QueryPerformanceCounter(&currentTime);

fps->Frames = (float)100 * (float)fps->Frequency.QuadPart / ((float)currentTime.QuadPart – (float)fps->startTime.QuadPart);

fps->startTime = currentTime;// 重置時間

fps->n = 0;

}

return fps->Frames;

}

 

计算两次测量所花费时间

float Get_Counter_FPS(FPS_PTR fps){

LARGE_INTEGER currentTime;//当前时钟

// 返回高精度计数器

QueryPerformanceCounter(&currentTime);

float seconds = ((float)currentTime.QuadPart – (float)fps->startTime.QuadPart) / (float)fps->Frequency.QuadPart;

fps->startTime = currentTime;

return seconds;

}

 

OpenGL頂點數組

OpenGL頂點數組

遊戲的實際的開發中將會大量頻繁地處理頂點,在簡單多邊形可以使用代碼直接生成模型頂點.而真正的遊戲隨便一個模型就可能有幾百或幾千個多邊形.不可能使用代碼直接生成.解決的方法是使用『頂點數組』(Vertex Array).通過建模軟件進形3D建模,然後輸出特定的文本文檔或二進制文檔.可分為以下幾個部驟.

  1. 從磁盤上的模型文檔中載入模型的頂點數據.
  2. 將頂點數據儲存在數組(array),如將頂點座標存入獨立數組,法線數組,顏色數組.
  3. 當OpenGL需要頂點數據時,載入相應的數據.

 

啟用『頂點數組』

void glEnableClientState(GLenum array);

禁用『頂點數組』

void glDisableClientState(GLenum array);

參數 簡介
GL_COLOR_ARRAY 啟用頂點顏色數組
GL_EDGE_FLAG_ARRAY 啟用頂點的邊EdgeFlag數組
GL_INDEX_ARRAY 啟用頂點的調色板索引數組
GL_NORMAL_ARRAY 啟用法線數組
GL_TEXTURE_COORD_ARRAY 啟用紋理座標數組
GL_VERTEX_ARRAY 啟用定點座標數組

 

載入頂點的顏色數組:

void glVertexPointer(GLint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

參數 簡介
size 頂點顏色分量其值只能為3(rgb)或4(rgba)
type 數組的數據類型

GL_BYTE

GL_UNSIGNED_BYTE

GL_SHORT

GL_UNSIGNED_SHORT

GL_INT

GL_UNSIGNED_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個顏色之間的字節數,如果顏色數據是緊湊則填為0
pointer 頂點的顏色數組

 

載入邊(Edge)數組:

void glEdgeFlagPointer(GLsizei stride,const GLvoid *pointer);

參數 簡介
stride 跨度
pointer 多邊形邊(Edge)數組bool類型數值

 

載入調色板顏色索引數組

void glIndexPointer(GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度相鄰索引之間的字節數
pointer 調色板顏色索引數組

 

載入頂點的法線向量,每三個元素組成一個法線向量

void glNormalPointer(GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
type 數組的數據類型

GL_BYTE

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度相鄰法線之間的字節數
pointer 頂點的法線向量數組

載入頂點的紋理座標數組void glTexCoordPointer(Glint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

參數 簡介
size 頂點的座標數,其值只能為1,2,3,4

1為1維紋理(s)

2為2維紋理(s,t)

3與4較小使用

type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個紋理之間的字節數
pointer 頂點紋理座標數組

 

載入頂點座標數組:

void glVertexPointer(Glint size,GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
size 頂點的座標分量:2,3,4
type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個頂點座標之間的字節數,如果是緊湊方置其值為0
pointer 頂點座標數組

繪畫所有當前以啟用的頂點數組void glDrawArrays(GLenum mode,GLint first,GLsizei count);

參數 簡介
mode GL_POINTS:點

GL_LINE_STRIP:相連的直線

GL_LINE_LOOP:閉合的相連直線

GL_LINES:非相連的直線

GL_TRIANGLE_STRIP:相連的三角形 GL_TRIANGLE_FAN:共用頂點三角形

GL_TRIANGLES:度立的三角形

GL_QUAD_STRIP:相連的四邊形

GL_QUADS:四邊形

GL_POLYGON:任意頂點多邊形

first 數組起此索引
count 繪畫的頂點量

 

以任意順序繪畫以啟用的頂點數組void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid *indices);

參數 簡介
mode 與glDrawArrays()的一致
count 索引數組的長度
type 索引的數據類型,只能是無符號整數GL_UNSIGNED_BYTE GL_UNSIGNED_SHORT GL_UNSIGNED_INT
indices: 索引數組

按值定的範圍繪畫以啟用的頂點數組

void glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);

參數 簡介
mode 與glDrawArrays()的一致
start 索引數組開此索引
end 索引數組結束索引
count 索引數組的長度
type 索引的數據類型,只能是無符號整數與glDrawElements()的一致
indices: 索引數組

按數組索引繪畫單一個頂點void glArrayElement(GLint index);

參數 簡介
index 頂點的索引

演示程式中繪畫幾百個球體不斷移動,若不使用頂點數組每秒只有約66幀,而若使用頂點數組性能大幅度提升到每秒有200幀.性能有驚人的提升(不同的計算機性能有所差別).按空格鍵啟用或禁用頂點數組.演示程式下載:

 

OpenGL顯示列表

OpenGL顯示列表

OpenGL支持稱為『顯示列表』(Display List)性能優化,它相當於把OpenGL代碼進行預編譯,並載入顯卡記憶體,從而降低系統開銷,但它對於程式的性能改善並不總是特別明顯,而且不同的圖形卡廠商的實現也有方式各異,最終效果視情況而定,不過最差的情況下也要比不使用好.

 

生成顯示列表, 返回列表索引

GLuint glGenLists(GLsizei range);

range:顯示列表數量

 

判斷顯示列表是否有效,有效則返回GL_TRUE

GLboolean glIsList (GLuint list);

list:列表索引

 

向顯示列表填充命令,把代碼寫在glNewList()與glEndList()

void glNewList(GLuint list,GLenum mode);

list:列表索引

mode:編譯模式分別有GL_COMPILE(編譯)和GL_COMPILE_AND_EXECUTE(編譯並執行)

 

結束填充數據

void glEndList(void);

 

當你擁有顯示列表,可以在任何地方調用顯示列表

void glCallList(GLuint list);

 

如果要一次調用多個顯示列表,依次調用.

void glCallLists(GLsizei num,GLenum type,const GLvoid *lists);

num:顯示列表個數

type:索引的類型

lists:顯示列表索引數組

 

如果想其它索引值開次執行,從offset開此到num-1結束

void glListName(GLuint offset);

offset:索引數組的偏移

 

顯示列表的燒毀,當創建顯示列表需要為其分配記憶體存儲OpenGL代碼,當程式結束時要將記憶體釋放,防止記憶體洩漏

void glDeleteLists(GLuint list,GLsizei range);

list:列表索引

range:顯示列表數量

 

執行顯示列表演示代碼:

if(glIsList(list->ID) == GL_TRUE)

{

glCallList(list->ID);// 調用顯示列表

}

else

{

list->ID = glGenLists(1);// 生成顯示列表

glNewList(list->ID, GL_COMPILE_AND_EXECUTE);

// 填入執行代碼

glEndList();

}

 

演示程式中繪畫幾百個球體不斷移動,若不使用顯示列表每秒大約200幀,而若使用顯示列表性能大幅度提升,每秒去到670幀.真是出乎意料.按空格鍵啟用或禁用顯示列表.下載演示程式:

互聯網之 Cloudflare推出免費DNS(1.1.1.1)

互聯網之 Cloudflare推出免費DNS(1.1.1.1)

互聯網公司Cloudflare推出免費的DNS服務,與APNIC進行合作使用它的IP位址(1.1.1.1), Cloudflare保證你使用它的DNS服務會絕對保證你的隱私,絕不會出售您的數據,或用來定位廣告,更不會記錄您的 IP 位址.

而且Cloudflare聲稱它的DNS服務比其它公司的DNS服務速度更快,比起GOOGLE的快最小兩倍.

DNS 響應速度
1.1.1.1 14.8ms
Cisco OpenDNS 20.6ms
Google Public DNS 34.7ms
Average ISP 68.23ms

在Windows10設定DNS伺服器IP位址

  1. Win+E打開『檔案總管』
  2. 點選打開『控制台\網路和網際網路\網路和共用中心』
  3. 點選『乙太網絡』
  4. 點選『內容』
  5. 雙擊打開『網際網路通信協定第4版(TCP/IPV4)』 勾選『使用下列的DNS伺服器位址』
  6. IPV4『慣用DNS伺服器』填1.1.1
  7. IPV4『其它DNS伺服器』填0.0.1
  8. 雙擊打開『網際網路通信協定第6版(TCP/IPV6)』勾選『使用下列的DNS伺服器位址』
  9. IPV6『慣用DNS伺服器』填 2606:4700:4700::1111
  10. IPV6『其它DNS伺服器』填 2606:4700:4700::1001
  11. 重啟瀏覽器

OpenGL之圖像合成器

OpenGL之圖像合成器

對圖像(紋理)合成你可以得到圖像變換的動畫效果,如『燈火』.通過讀取兩張圖像,然後對其進行插值運算,最後生成平滑過渡的效果.演示程式下載:

對兩個或者多個紋理進行組合:

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COMBINE);

紋理平滑過渡

GLfloat texEnvColor[] = {0.0f,0.0f,0.0f,combiner->interpol };

glActiveTexture(GL_TEXTURE1);

glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR, texEnvColor);

載入紋理單元0

Load_File_Texture(&combiner->texture0, hInstance, filename0);

激活紋理單元0

glActiveTexture(GL_TEXTURE0);

綁定紋理0

Bind_Image_Texture(&combiner->texture0);

glEnable(GL_TEXTURE_2D);

將紋理傳遞到下一個單元

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

載入紋理1

Load_File_Texture(&combiner->texture1, hInstance, filename1);

激活紋理單元1

glActiveTexture(GL_TEXTURE1);

綁定紋理1

Bind_Image_Texture(&combiner->texture1);

glEnable(GL_TEXTURE_2D);

設置紋理組合模式

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COMBINE);

使用插值組合函式

glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);

設置成紋理單元0的輸出

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);

設置成當前紋理圖像

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);

為紋理單元設置ARG2

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);

使用alpha常數修改RGB分量

glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

Windows10之功能更新版本1709錯誤碼0xc1900205

Windows10之功能更新版本1709錯誤碼0xc1900205

近日Windows10更新版本1709時總會彈出錯誤碼0xc1900205,使用『Windows Update』與『Windows10更新小幫手』均出錯並彈出下面的信息:

Microsoft 無法在您的電腦上安裝重要的安全性更新。

請連絡 Microsoft 支援服務以獲得解決此錯誤的協助。

請將此錯誤碼提供給支援人員: 0xc1900205

通過如下方法即可修復:

  1. 新增『文字文件.txt』輸入下面命令,並保存為『cmd』.(此方法適合大量類似問題)
  2. 以系統管理員身份執行CMD
net stop wuauserv

Dism.exe /online /Cleanup-Image /StartComponentCleanup

Dism.exe /online /Cleanup-Image /RestoreHealth

net start wuauserv

3.我已生成cmd文檔可以直接下載:

 

Windows10之以系統管理員身份執行命令視窗CMD

Windows10之以系統管理員身份執行命令視窗CMD

在Windows10下很多命令需要『系統管理員』Administrator的『權限』才能執行,有4中方法打開

  1. 快捷鍵『WIN+X+A』打開命令視窗(系統管理員:命令提示字元),此方法成功較低不建議使用
  2. 快捷鍵『WIN+E』打開『檔案總管』,按住『shift』鍵以鼠標右鍵點擊任意『檔案資料夾』彈出菜單『在此處開啟命令視窗』此方法無系統管理員權限
  3. 快捷鍵『WIN+E』打開『檔案總管』定位『C:\Windows\System32\cmd.exe』以鼠標右鍵點擊彈出菜單『以系統管理員身份執行』打開命令視窗
  4. 新增『文字文件』寫入你要執行的命令並保存,將『副檔名』改為『cmd』.以鼠標右鍵點擊彈出菜單『以系統管理員身份執行』打開命令視窗,此方法最為方便

 

OpenGL之環境映射

OpenGL之環境映射

環境映射即在表面『映射』其它物體,如湖面上『映射』出『樹、雲、天、人』,而OpenGL並不是真正地對環境進行反射獲得的效果.演示函式下載:

  1. 創建物體表面的紋理圖
  2. 使用OpenGL自動生成相應的紋理座標,可以使用GL_SPHERE_MAP(球體映射)和GL_REFLECTION_MAP(反射光映射)
  3. 移動鏡頭到物體並指向視點
  4. 隨著物體的移動而生成新的紋理座標,使得環境反射效果隨著物體移動而變化,使用glCopyTexImage2D()和glCopyTexSubImage2D()從屏幕上拷貝並生成紋理
  5. 如果要填充多邊形顏色,你要禁用紋理glDisable(GL_TEXTURE_2D);才能正確渲染.

 

紋理座標生成函式:

void glTexGenf(GLenum coord,GLenum pname, GLenum param)

glTexGenf()函式coord: 簡介
GL_S s紋理坐標
GL_T t紋理坐標
GL_R r紋理坐標
GL_Q q紋理坐標

 

glTexGenf()函式pname: param簡介
GL_TEXTURE_GEN_MODE GL_SPHERE_MAP

GL_REFLECTION_MAP

GL_OBJECT_LINEAR

GL_EYE_LINEAR

GL_NORMAL_MAP

GL_OBJECT_PLANE Param指向生成紋理坐標所需的4個元素,配合GL_OBJECT_LINEAR使用
GL_EYE_PLANE Param指向生成紋理坐標所需的4個元素,配合GL_EYE_LINEAR使用

 

生成環境映射紋理代碼

glGenTextures(1, &texture.ID);

glBindTexture(GL_TEXTURE_CUBE_MAP, texture.ID);

設定紋理參數代碼

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

將紋理坐標生成模式設置為環境映射(反射光)函式

glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

 

移動鏡頭到物體並指向視點,設置屏幕以匹配環境映射的大小

glViewport(0, 0, 256,256);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

設置90度視口

gluPerspective(90, 1, 0.1, 500);

glMatrixMode(GL_MODELVIEW);

glClear(GL_DEPTH_BUFFER_BIT);

載入單位矩陣

glLoadIdentity();

設定相機

gluLookAt(0.0, 0.0, 0.0,posx,posy,posz,neggx,negy,negz);

通過glCopyTexImage2D()和glCopyTexSubImage2D()從屏幕上拷貝並生成環鏡映射紋理

從緩衝區中拷貝像素創建紋理函式

typedef void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);

從緩衝區中拷貝部分像素創建紋理函式

typedef void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);

 

渲染環鏡映射代碼

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

glEnable(GL_TEXTURE_GEN_R);

使用立方體映射

glEnable(GL_TEXTURE_CUBE_MAP);

glBindTexture(GL_TEXTURE_CUBE_MAP, texture.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

gluSphere(quadric, 1.0, 64, 64);// 繪畫大球球體

glDisable(GL_TEXTURE_CUBE_MAP);

glDisable(GL_TEXTURE_GEN_S);

glDisable(GL_TEXTURE_GEN_T);

glDisable(GL_TEXTURE_GEN_R);

星海爭霸 II之AI對戰延遲問題

星海爭霸 II之AI對戰延遲問題

很多網友都發現『星海爭霸II』的對戰AI非常嚴重延遲,每格幾秒就出現停頓,遊戲運行『延遲』無非是三個原因:

  1. 硬件問題:
  2. 網絡延遲:
  3. 3D模型多邊形複雜度:

而星海爭霸 II 的AI對戰是需要聯網的『UPD連接』,如果網絡延遲較大則造成運行網絡,你需要重新選擇鏈接的『伺服器』

  1. 啟動net(無需切換帳號)
  2. 地區/帳號:分別有『美洲』,『歐洲』,『亞洲』.選擇速度快的『伺服器』
  3. 再啟動遊戲

OpenGL之多紋理地形

OpenGL之多紋理地形

多紋理地形使用『草地紋理(2D紋理)』與『高度紋理(1D紋理)』相結合,根據海平面的高度為地形進行著色.『高度紋理(1D紋理)』即只有1行的紋理,載入後需按以下代碼載入紋理

Load_File_Texture(&terrain->height_texture, NULL, “height.tga”);// 載入”高度”紋理

Bind_Image_Texture(&terrain->height_texture); // 綁定”高度”紋理

glGenTextures(1, &texture->ID);// 生成紋理

glBindTexture(GL_TEXTURE_1D, texture->ID);// 綁定紋理

glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);//夾持紋理

glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, texture->width, 0,GL_RGB,GL_UNSIGNED_BYTE,texture->image); // 載入紋理

// 紋理單元1

glActiveTexture(GL_TEXTURE1);// 激活紋理單元1

glEnable(GL_TEXTURE_GEN_S);// S座標

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);//紋理坐標的生成模式

GLfloat waterPlane[] = { 0.0, 1.0, 0.0, -TERRAIN_WATER_HEIGHT };

glTexGenfv(GL_S, GL_OBJECT_PLANE, waterPlane);//紋理坐標的生成

// 紋理單元0

glActiveTexture(GL_TEXTURE0);// 激活紋理單元0

glEnable(GL_DEPTH_TEST);// 深度測試

glEnable(GL_TEXTURE_2D);// 啟用2D紋理映射

 

繪畫紋理地形的函是代碼

void Draw_Height_Terrain(TERRAIN_PTR terrain)

{

// 激活紋理單元0

glActiveTexture(GL_TEXTURE0);

glEnable(GL_TEXTURE_2D);// 2D紋理

glBindTexture(GL_TEXTURE_2D, terrain->grass.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);// 平鋪效果

// 激活紋理單元1

glActiveTexture(GL_TEXTURE1);

glEnable(GL_TEXTURE_1D);// 1D紋理(單行)

glBindTexture(GL_TEXTURE_1D, terrain->height_texture.ID);//高度紋理綁定到它上面

glMatrixMode(GL_TEXTURE);// 切換到紋理矩陣

glLoadIdentity();

glScalef(1.0f / TERRAIN_MAX_HEIGHT, 1.0, 1.0);// 設定s坐標比例尺.

glMatrixMode(GL_MODELVIEW);// 切換到視口矩陣

// 繪畫地形

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

for (int z = 0; z < terrain->width – 1; ++z)

{

glBegin(GL_TRIANGLE_STRIP);

for (int x = 0; x < terrain->width; ++x)

{

GLfloat scaledHeight = terrain->data[z * terrain->width + x] / terrain->scale;

GLfloat nextScaledHeight = terrain->data[(z + 1)*terrain->width + x] / terrain->scale;

// 指定紋理單元0的紋理座標

glMultiTexCoord2f(GL_TEXTURE0, (GLfloat)x / terrain->width * 8.0f , (GLfloat)z / terrain->width * 8.0f);

glVertex3f((GLfloat)x – terrain->width / 2.0f, scaledHeight, (GLfloat)z – terrain->width / 2.0f);

// 指定紋理單元0的紋理座標

glMultiTexCoord2f(GL_TEXTURE0, (GLfloat)x / terrain->width * 8.0f, (GLfloat)(z + 1) / terrain->width * 8.0f );

glVertex3f((GLfloat)x – terrain->width / 2.0f, nextScaledHeight, (GLfloat)(z + 1) – terrain->width / 2.0f);

}

glEnd();

}

glDisable(GL_TEXTURE_1D);// 禁用紋理單元1

glActiveTexture(GL_TEXTURE0);// 激活紋理單元0

//繪畫水面

glBindTexture(GL_TEXTURE_2D, terrain->water.ID);// 水面紋理

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glBegin(GL_QUADS);

glTexCoord2f(0.0, 0.0);

glVertex3f(-terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);

glTexCoord2f(terrain->width / 4.0f, 0.0);

glVertex3f(terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);

glTexCoord2f(terrain->width / 4.0f, terrain->width / 4.0f);

glVertex3f(terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);

glTexCoord2f(0.0, terrain->width / 4.0f);

glVertex3f(-terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);

glEnd();

}

演示程式下載:

 

OpenGL之多紋理映射

OpenGL之多紋理映射

OpenGL可同時對多邊形映射多個紋理,稱為『多紋理映射』. 它是OpenGL的可選擴展實現,並非所有的OpenGL都有實現.『OpenGL API的所有擴展必須得到ARB(OpenGL體系評審委員會)的認可』,在進行多紋理映射時,每個紋理單元都將映射結果傳遞給下個紋理單元,直至所有的紋理的紋理單元都進行映射(最終結果)演示程式:下载

多紋理映射分為四個步驟:
1.判斷是否支持『多紋理映射』
2.讀取擴展函式的指針
3.建立紋理單元
4.設置紋理坐標

定義多紋理映射結構

typedef struct MULTITEXTURE_TYP {

TEXTURE texture0;

TEXTURE texture1;

}MULTITEXTURE,*MULTITEXTURE_PTR;

1.驗證當前版本的OpenGL是否支持『多紋理映射』

函式 簡介
glGetString(GL_EXTENSIONS) 獲取OpenGL所支持的所有擴展函式

返回以空格隔開的字符串列表

GL_ARB_multitexture 查找(GL_ARB_multitexture)判斷是否支持『多紋理映射』

判斷是否支持某一擴展函式

bool Extensions_OpenGL(const char * name)

{

char * extensions;

int index = 0;

int length,n;

extensions = (char*)glGetString(GL_EXTENSIONS);// 擴展函式

length = strlen(name);// 長度

while (extensions[index] != NULL)// 循環查找

{

n = strcspn(extensions + index, ” “);// 查找空格

if (n == length && strncmp(extensions + index, name, length) == 0)// 比較

return true;

index = index + n + 1; // 跳過空格

}

return false;

}

2.讀取擴展函式的指針

函式 簡介
PROC wglGetProcAddress(LPCSTR lpszProc); 返回擴展函式的地址

 

擴展函式定義 簡介
typedef void (APIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); 為多紋理映射設置紋理座標
typedef void (APIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum texture); 設置當前的紋理單元
typedef void (APIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); 設置選擇當前的紋理單位,以便用頂點數組指定紋理坐標數據

初此化多紋理映射

PFNGLMULTITEXCOORD2FARBPROC     glMultiTexCoord2fARB;

PFNGLACTIVETEXTUREARBPROC       glActiveTextureARB;

PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;

void Init_Multitexture()

{// 判斷是否支持某一擴展函式

if( Extensions_OpenGL(“GL_ARB_multitexture”) )

{

glMultiTexCoord2fARB=(PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress(“glMultiTexCoord2fARB”);

glActiveTextureARB=(PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress(“glActiveTextureARB”);

glClientActiveTextureARB=(PFNGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress(“glClientActiveTextureARB”);

}

}

3.建立紋理單元

載入紋理並綁定 簡介
『.bmp』 『.tga』 『.pcx』 載入紋理文件
glGenTextures(1, &texture->ID);

glBindTexture(GL_TEXTURE_2D, texture->ID);

生成紋理單元綁定紋理

激活多紋理映射

void Active_Multitexture(MULTITEXTURE_PTR multitexture)

{

glActiveTextureARB(GL_TEXTURE0_ARB);

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, multitexture->texture0.ID);// 綁定紋理

glActiveTextureARB(GL_TEXTURE1_ARB);

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, multitexture->texture1.ID);// 綁定紋理

}

OpenGL最多支持32個紋理『GL_TEXTURE0_ARB』~『GL_TEXTURE31_ARB』

但最後通過查詢當前版本支持的最大單元量

int maxTexUnits;

glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&maxTexUnits);

 

4.設置紋理坐標

  簡介
void glMultiTexCoord2fARB(GLenum texUnit,float coords); 設置紋理坐標
texUnit: GL_TEXTURE0_ARB~GL_TEXTURE31_ARB 紋理單元索引
Coords: 紋理坐標

 

Windows之Microsoft屏幕放大鏡

Windows之Microsoft屏幕放大鏡

Windows其中一個最好用的工具是『Microsoft屏幕放大鏡』,通過win鍵與+鍵啟動.如果你有『Microsoft鼠標』可以通過安裝『IntelliPoint8.2』激活母指鍵啟動『Microsoft屏幕放大鏡』.安裝後要重啟電腦. 通過『檔案總管\控制台\所有控制台項目\滑鼠』設定『母指鍵』.在『全屏幕』下按『母指鍵』+『鼠標滑輪』用於梯相最好用.

快捷鍵 簡介
Win鍵與+鍵 方大/啟動放大鏡
Win鍵與-鍵 縮小
Win鍵與ESC鍵 關閉放大鏡
鼠標前母指鍵 啟動放大鏡/關閉放大鏡
鼠標前母指鍵+鼠標滑輪 方大/縮小(這個最好用)
CTRL+ALT+F 全屏幕
CTRL+ALT+L 透鏡
CTRL+ALT+D 以連接擴充座(分屏)
CTRL+ALT+SPACE 預覽全屏幕
CTRL+ALT+I 反色

 

Microsoft 無線霸雷鯊7000鼠標修復

Microsoft 無線霸雷鯊7000鼠標修復

鼠標輕觸開關是最容易損耗的,當鼠標出現連擊時,鼠標要準備退役,即使是最貴的鼠標使用壽命也相差唔多,就算鼠標有五年保養也不會幫你更換輕觸開關,唔信你可以試下拿它返廠.

如果你很喜歡你的滑鼠可以通過更換滑鼠的輕觸開關(如上圖),延長滑鼠使用壽命.因位兩層上錫要將它熔焊有D難度.因為『霸雷鯊7000』只有兩隻腳所以無分正負,貼緊上錫即可.

如果『霸雷鯊7000』出現斷幀可以償試拔掉其它USB設備.