《Android 应用案例开发大全(第二版)》——2.6节绘制相关类
本節書摘來自異步社區《Android 應用案例開發大全(第二版)》一書中的第2章,第2.6節繪制相關類 ,作者 吳亞峰 , 于復興 , 杜化美,更多章節內容可以訪問云棲社區“異步社區”公眾號查看
2.6 繪制相關類
Android 應用案例開發大全(第二版)
上一節完成了水族館輔助繪制類開發過程的介紹,這一節將對本案例中的繪制相關類進行詳細介紹。主要包括氣泡繪制相關類、群魚繪制相關類、魚群繪制相關類和魚食繪制相關類,從而使讀者對本案例的開發有一個更加深刻的理解。下面就對這些繪制相關類進行詳細介紹。
2.6.1 繪制氣泡相關類
真實的水族館中時常會冒出一些氣泡,所以,在該壁紙中加入了透明氣泡元素,從而達到仿真、酷炫的效果。最后本案例的運行效果是魚在水族館里面游,透明的氣泡從屏幕下方不斷冒出,上升到一定高度后氣泡突然消失,這樣能使壁紙顯得更加真實,更加具有可觀賞性。
本小節主要介紹繪制氣泡相關類,繪制氣泡相關類分為氣泡控制類BubbleControl,用來控制所有氣泡的繪制和單個氣泡繪制類SingleBubble,用來對單個氣泡進行繪制,開發步驟如下所示。
(1) 下面首先對氣泡控制類BubbleControl的開發進行介紹。氣泡控制類中包括創建氣泡對象,創建并啟動氣泡移動線程,繪制氣泡等。具體代碼如下所示。
1 package com.bn.ld.Bubbles; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class BubbleControl { 4 public ArrayList<SingleBubble> bubbleSingle = new ArrayList<SingleBubble>(); // 氣泡類的列表 5 int texld; // 氣泡的紋理ID 6 public BubbleControl(int texld) { 7 this.texld = texld; // 拿到ID 8 for (int i = 0; i < Constant.BUBBLE_NUM; i++) { // 創建多個氣泡 9 bubbleSingle.add(new SingleBubble(this.texld)); // 將氣泡加入氣泡列表 10 } 11 BubbleThread Bgt = new BubbleThread(this); // 創建氣泡移動線程 12 Bgt.start(); // 啟動氣泡移動線程 13 } 14 public void drawSelf(GL10 gl) { 15 try { 16 Collections.sort(this.BubbleSingle); // 對氣泡排序 17 for (int i = 0; i < this.BubbleSingle.size(); i++) { // 繪制氣泡 18 gl.glPushMatrix(); // 保護矩陣 19 BubbleSingle.get(i).drawSelf(gl); // 繪制氣泡 20 gl.glPopMatrix(); // 恢復矩陣 21 }} catch (Exception e) { // 異常處理 22 e.printStackTrace(); 23 }}}此處包括創建氣泡類列表用來存放氣泡對象,創建氣泡移動線程并啟動,從而控制氣泡的移動。繪制氣泡時要先保護矩陣,然后再繪制氣泡,最后恢復矩陣。
(2)上面已經對氣泡控制類BubbleControl進行了詳細介紹,接下來就應該進入到對單個氣泡繪制類SingleBubble的介紹。在單個氣泡繪制類中隨機設置了氣泡的初始位置、上升的最大高度等。繪制氣泡時用到了混合技術,如果采用了混合技術,對對象的繪制順序是有嚴格要求的,即繪制順序是由遠及近,所以,在繪制氣泡之前要根據氣泡的位置對氣泡列表中的氣泡對象進行排序,具體代碼如下所示。
1 package com.bn.ld.Bubbles; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class SingleBubble { 4 float cuerrentX; // 氣泡當前_x_位置 5 float cuerrentY; // 氣泡當前_y_位置 6 float cuerrentZ; // 氣泡當前_z_位置 7 float border; // 氣泡的最大高度 8 int texld; // 紋理ID 9 Bubble bubbles; // 氣泡對象 10 public SingleBubble(int texld) { 11 newPosition(); // 初始氣泡的位置 12 this.texld = texld; 13 bubbles = new Bubble(texld); // 創建氣泡 14 } 15 public void drawSelf(GL10 gl) { 16 gl.glPushMatrix(); // 保護矩陣 17 gl.glTranslatef(cuerrentX, cuerrentY, cuerrentZ); // 移動 18 bubbles.drawSelf(gl,texld); // 繪制氣泡 19 gl.glPopMatrix(); // 恢復矩陣 20 } 21 public void bubbleMove() { 22 this.cuerrentY += Constant.BubbleMoveDistance; // 氣泡移動 23 if (this.cuerrentY > border) { 24 newPosition(); // 重置氣泡的位置 25 }} 26 public void newPosition() { 27 cuerrentX = (float) ((Math.random() - 0.5) * 10);// 隨機改變氣泡的x位置 28 cuerrentY = (float) (Math.random() - 4); // 隨機改變氣泡的y位置 29 cuerrentZ = (float) ((Math.random() - 1) * 3.5f);// 隨機改變氣泡的z位置 30 border = (float) (2 * Math.random() + 3); // 氣泡上升的最大高度 31 }} 32 public int compareTo(SingleBubble another) { 33 return ((this.cuerrentZ-another.cuerrentZ)==0)?0:((this.cuerrentZ-another. cuerrentZ)>0)?1:-1; 34 }第1~14行為相關變量的聲明,包括氣泡位置、紋理ID等,在構造器中會調用一次newPosition方法,從而指定氣泡的初始位置和最大高度。
第15~34行為根據氣泡當前位置繪制氣泡,設置氣泡移動速度,并修改氣泡位移,同時判斷氣泡位置是否大于氣泡的最大高度border,如果大于border則調用newPosition方法重新指定氣泡的初始位置和最大高度,需要注意的是,氣泡位置和最大高度都是在一定范圍內隨機產生的。最后重寫compareTo方法根據氣泡位置對氣泡進行排序。
2.6.2 繪制群魚相關類
上一小節介紹了繪制氣泡相關類,本小節主要介紹繪制群魚相關類,繪制群魚相關類分為群魚控制類FishControl,用來控制群魚里所有魚的繪制,和單個魚繪制類SingleFish,用來對單個魚進行繪制,開發步驟如下所示。
(1)首先進行的是群魚控制類FishControl的開發介紹。該類中將創建群魚列表,創建群魚的游動線程并啟動,遍歷群魚列表繪制群魚。具體代碼如下所示。
1 package com.bn.ld.Fishs; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class FishControl { 4 public ArrayList<SingleFish> fishAl; // 魚列表 5 FishGoThread fgt; // 魚移動線程 6 public TDRender Tr; // 渲染器 7 public FishControl(ArrayList<SingleFish> fishAl,TDRender tr){ 8 this.fishAl = fishAl; 9 this.Tr=tr; 10 fgt= new FishGoThread(this); // 創建魚游動線程 11 fgt.start(); // 啟動魚游動線程 12 } 13 public void drawSelf(GL10 gl) { 14 try { 15 for(int i=0;i<this.fishAl.size();i++){ // 循環繪制每一條魚 16 gl.glPushMatrix(); // 保護矩陣 17 fishAl.get(i).drawSelf(gl); // 繪制魚 18 gl.glPopMatrix(); // 恢復矩陣 19 }} 20 catch (Exception e) { // 異常處理 21 e.printStackTrace(); 22 }}}此處是創建群魚列表,獲得渲染器TDRender、創建群魚游動線程并啟動。繪制時先保護矩陣,遍歷群魚列表繪制每一條魚,然后恢復矩陣,最后進行異常處理。
(2)上面已經對群魚控制類FishControl進行了介紹,下面將進行單條魚繪制類SingleFish的開發介紹。在該類中設置了群魚中每個魚的位置、速度、質量(力的縮放比)、受到的外力、魚食對魚的吸引力、魚的旋轉角度等。具體代碼如下所示。
1 package com.bn.ld.Fishs; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class SingleFish { 4 ……//此處省略相關成員變量的聲明代碼,讀者可自行查閱光盤中的源代碼 5 LoadedObjectVertexNormalTexture lovn; // 初始化魚類對象 6 public SingleFish(int fish, TDRender TDRender, String name, 7 Vector3f Position, Vector3f Speed, Vector3f force, 8 Vector3f attractforce, float weight) { 9 this.texld=fish; // 魚的紋理ID 10 this.position = Position; // 魚的位置 11 this.speed = Speed; // 魚的速度 12 this.force = force; // 魚受到的外力 13 this.attractforce = attractforce; // 魚受到的吸引力 14 this.weight = weight; // 魚的質量 15 lovn = LoadUtil.loadFromFileVertexOnly(name, 16 TDRender.getResources()); // 加載魚模型 17 } 18 public void drawSelf(GL10 gl) { 19 gl.glPushMatrix(); // 保護矩陣 20 gl.glTranslatef(this.position.x, this.position.y, this.position.z); // 平移 21 gl.glRotatef(yAngle, 0, 1, 0); // 繞_y_軸旋轉 22 gl.glRotatef(zAngle, 0, 0, 1); // 繞_z_軸旋轉 23 if (lovn != null) { // 魚對象不為空就繪制 24 lovn.drawSelf(gl,this.texld); // 繪制魚 25 } 26 gl.glPopMatrix(); // 恢復矩陣 27 } 28 public void fishMove() { // 魚的游動方法 29 float fz = (speed.x * speed.x + speed.y * 0 + speed.z * speed.z); // zAngle的計算 30 float fm = (float) (Math.sqrt(speed.x * speed.x + speed.y * speed.y 31 + speed.z * speed.z) * Math.sqrt(speed.x * speed.x + speed.z* speed.z)); 32 float angle = fz / fm; 33 tempZ = (float) (180f / Math.PI) * (float) Math.acos(angle); // 繞_z_軸的旋轉角度 34 fz = (speed.x * Constant.initialize.x + speed.z * Constant.initialize.z); // yAngle的計算 35 fm = (float) (Math.sqrt(Constant.initialize.x * Constant.initialize.x 36 + Constant.initialize.z * Constant.initialize.z) 37 * Math.sqrt(speed.x * speed.x + speed.z * speed.z)); 38 angle = fz / fm; 39 tempY = (float) (180f / Math.PI) * (float) Math.acos(angle); // 繞_y_軸的旋轉角度 40 if (speed.y <= 0) { // 獲取夾角根據Speed.y的正負性來確定夾角的正負性 41 zAngle = tempZ; // 上面求的角度都是正的 42 } else { 43 zAngle = -tempZ; 44 } 45 if (speed.z > 0) { // 獲取夾角根據Speed.z的正負性來確定夾角的正負性 46 yAngle = tempY; // 上面求的角度都是正的 47 } else { 48 yAngle = -tempY; 49 } 50 if (Math.abs(speed.x + force.x) < Constant.MaxSpeed){ // 動態的修改魚x方向的速度 51 speed.x += force.x; 52 } 53 if (Math.abs(speed.y + force.y) < Constant.MaxSpeed){ // 動態的修改魚y方向的速度 54 speed.y += force.y; 55 } 56 if (Math.abs(speed.z + force.z) < Constant.MaxSpeed) { // 動態的修改魚z方向的速度 57 speed.z += force.z; 58 } 59 if (Math.abs(speed.x + attractforce.x) < Constant.MaxSpeed){ //動態的修改魚x方向的速度 60 speed.x += attractforce.x; 61 } 62 if (Math.abs(speed.y + attractforce.y) < Constant.MaxSpeed) { //動態的修改魚y方向的速度 63 speed.y += attractforce.y; 64 } 65 if (Math.abs(speed.z + attractforce.z) < Constant.MaxSpeed) { //動態的修改魚z方向的速度 66 speed.z += attractforce.z; 67 } 68 position.plus(speed); // 改變魚的位置 69 this.force.x = 0; // 每次計算完每條魚的受力 70 this.force.y = 0; // 把所受的外力置零 71 this.force.z = 0; 72 attractforce.x = 0; // 把魚食對魚的吸引力置零 73 attractforce.y = 0; 74 attractforce.z = 0; 75 }}第1~17行引入部分類和包,聲明相關成員變量,這些代碼在這里省略,讀者可自行查閱光盤中的源代碼,其中第6~17行通過構造器接收傳過來的魚的各種參數的信息,然后通過工具類加載魚模型。
第18~27行為繪制時先保護變換矩陣再將魚平移到指定位置,并讓坐標軸旋轉相應的角度,從而使魚能以一個正確的姿態顯示在屏幕上,然后繪制魚,最后恢復變換矩陣。
第28~49 行為坐標軸旋轉角度的計算方法,具體計算是根據魚的速度矢量確定出魚的朝向,利用初等函數計算出坐標軸相應的旋轉角度。
第50~68行為動態修改魚速度的方法,魚可能會受到外力(排斥力)和食物吸引力的作用,力會改變魚的速度,當魚的速度超過閾值時,速度不再增加。將魚的速度矢量和位移矢量相加得到魚新的位移矢量。
第69~75行是將力置零,因為每次都要重新計算魚的受力,當魚不受到力的作用時,魚的速度不會改變,魚將沿著當前的速度方向游動。
2.6.3 繪制魚群相關類
上一小節已經對繪制群魚相關類進行了詳細介紹,這一小節對繪制魚群相關類的開發進行詳細介紹。繪制魚群相關類分為魚群控制類FishSchoolControl,用來控制魚群里所有魚的繪制,和單個魚繪制類SingleFishSchool,用來對魚群中單個魚進行繪制,開發步驟如下所示。
(1)首先進行魚群控制類FishSchoolControl的開發介紹。在魚群控制類中創建了魚群列表和魚對象,同時創建了魚群游動線程并啟動。具體代碼如下所示。
1 package com.bn.ld. FishSchool; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class FishSchoolControl { 4 public ArrayList<FishSchooldSingleFish> fishSchool = new ArrayList <FishSchooldSingleFish>(); 5 public TDRender Tr; 6 FishschoolThread Thread; // 魚群的移動線程 7 public FishSchoolControl(int texld, TDRender tr) { // 添加魚群,共4條魚 8 this.Tr = tr; 9 fishSchool.add(new FishSchooldSingleFish(texld, Tr, "fish3.obj", // 魚的ID、渲染器、模型 10 new Vector3f(0, 0, 0), // 初始位置 11 new Vector3f(-0.05f, 0.00f, -0.05f),new Vector3f(0, 0, 0), // 初始速度和力 12 new Vector3f(0, 0, 0), 50)); // 吸引力、質量 13 fishSchool.add(new FishSchooldSingleFish(texld, Tr, "fish3.obj", //魚的ID、渲染器、模型 14 new Vector3f(0, 0, -Constant.Radius), // 初始位置 15 new Vector3f(-0.04f,0.00f, -0.04f), new Vector3f(0, 0, 0), // 初始速度和力 16 new Vector3f(0,0, 0), 50)); // 吸引力、質量 17 fishSchool.add(new FishSchooldSingleFish(texld, Tr, "fish3.obj", //魚的ID、渲染器、模型 18 new Vector3f(Constant.Radius, 0, 0), // 初始位置 19 new Vector3f(-0.04f,0.00f, -0.04f), new Vector3f(0, 0, 0), // 初始速度和力 20 new Vector3f(0,0, 0), 50)); // 吸引力、質量 21 fishSchool.add(new FishSchooldSingleFish(texld, Tr, "fish3.obj", // 魚的ID、渲染器、模型 22 new Vector3f(0, 0, Constant.Radius), // 初始位置 23 new Vector3f(-0.04f,0.00f, -0.04f), new Vector3f(0, 0, 0), // 初始速度和力 24 new Vector3f(0,0, 0), 50)); // 吸引力、質量 25 Thread = new FishschoolThread(this); // 定義魚群游動線程 26 Thread.start(); // 啟動魚群游動線程 27 } 28 public void drawSelf(GL10 gl) { 29 try { 30 for (int i = 0; i < this.fishSchool.size(); i++){ 31 gl.glPushMatrix(); // 保護矩陣 32 fishSchool.get(i).drawSelf(gl); // 繪制魚群 33 gl.glPopMatrix(); // 恢復矩陣 34 }} 35 catch (Exception e){ // 異常處理 36 e.printStackTrace(); 37 }}}}第1~27行為創建魚群游動線程,并向魚群列表中添加魚對象,同時啟動魚群游動線程從而實現魚的游動。魚群列表里的第一條魚不受到其他任何魚的作用力,只受墻壁的作用力,并且魚群里面的魚(不包括第一條魚)在特定的條件下受到從該魚本身指向某個位置(以魚群中第一條魚所在的位置為球心,以定長半徑確定的球面上的一個點)的向心力作用。
第28~37行為繪制方法。首先循環遍歷fishSchool列表,繪制魚群里面的魚。在繪制前要先保護矩陣,然后繪制魚群,恢復矩陣,最后進行異常處理。
(2)上面已經完成了對魚群控制類的詳細介紹,下面將對單個魚繪制類SingleFishSchool的開發進行詳細介紹。在單個魚繪制類中設置魚的位置、速度、質量、受到的外力、受到的向心力(第一條魚不受到向心力作用),魚的旋轉角度,具體代碼如下所示。
第1~19行定義了魚群的位置信息、速度信息、受到的外力信息和魚受到的向心力信息等代碼在此處省略,讀者可自行查閱光盤中的源代碼。
第20~29行為繪制時先保護變換矩陣,將魚群平移到指定位置,讓坐標軸旋轉相應的角度,然后繪制魚群,最后恢復變換矩陣。
第30~65行為坐標軸旋轉角度的計算方法,具體角度的計算是根據魚的速度矢量確定出魚的朝向,利用初等函數計算出坐標系中的y軸和z軸的旋轉角度。
第66~88行為改變魚速度的方法,魚可能受到外力(排斥力)和食物吸引力(本案例中魚群里面的魚并沒有受到食物吸引力的作用),通過力的作用來改變魚的速度,當速度超過閾值時,速度不再增加。同時魚的速度矢量和位移矢量相加得到魚的新位移矢量,然后將外力置為零。
2.6.4 繪制魚食相關類
上一小節已經完成了繪制魚群相關類的介紹,這一小節將要完成繪制魚食相關類的開發,這樣在本案例中就可以對游動的魚進行喂食了。給魚喂食要對屏幕進行觸控操作,本案例采用了屏幕拾取技術,讀者在運行本程序時可以感受到,點擊地面遠點時食物會相對小一些,點擊地面近點時食物會相對大一些,從而產生近大遠小的效果,當然,如果點擊地面過遠,則不產生喂食效果。屏幕觸控事件的介紹在前面已經給出,在此就不再介紹,只介紹魚食的繪制。
本小節主要介紹繪制魚食相關類,繪制魚食相關類分為魚食控制類FeedFish,用來控制魚食的繪制,和魚食繪制類SingleFood,用來繪制魚食,開發步驟如下所示。
(1)首先將進行喂食控制類FeedFish的開發工作,具體包括設置魚食的初始位置,更改線程的標志位等,具體代碼如下所示。
1 package com.bn.ld.FeedFood; 2 ……//此處省略部分類和包的引入代碼,讀者可自行查閱光盤中的源代碼 3 public class FeedFish { 4 ……//此處省略相關成員變量的聲明代碼,讀者可自行查閱光盤中的源代碼 5 public FeedFish (TDRender tr) { 6 start = true; 7 this.Tr = tr; 8 } 9 public void startFeed(Vector3f Start,Vector3f End) { 10 Vector3f dv=End.cutPc(Start); // 喂食的位置 11 float t=( Constant.Y_HEIGHT -Start.y)/dv.y; // 根據地面的高度算出_t_值 12 float xd=Start.x+t*dv.x; // 根據_t_計算出交點的_x_坐標值 13 float zd=Start.z+t*dv.z; // 根據_t_計算出交點的_z_坐標值 14 if(zd<= Constant.ZTouch_Min ||zd> Constant.ZTouch_Max) {// 點擊超出范圍不喂食 15 Constant.isFeed=true; // 標志位變為true,能繼續點擊 16 return; 17 } 18 Tr.Xposition = xd; // 食物的_x_位置 19 Tr.Zposition = zd; // 食物的_z_位置 20 Tr.Fooddraw = true; // 繪制食物的標志位 21 Tr.singleFood.Ft.Fresit = true; // 重置的線程變為ture 22 Tr.singleFood.At.Go = true; // 吸引力線程的添加,看到魚食的標志位設為True 23 Tr.singleFood.Ft.Go = true; // 將喂食線程的標志位設為true 24 if (start) { // 調用此方法開始移動食物的方法 25 Tr.singleFood.startFeed(); // 開始喂食 26 start = false; // 把標志位變為false,表示不能再調用此方法 27 }}}第2~13行為計算屏幕觸控位置的算法,是根據地面高度算出遠近平面上兩點與該平面的交點(根據3點共線求出與地面平面交點)。
第14~27行先判斷點擊位置是否在規定范圍內,如果在規定的范圍內將計算的位置賦給Xposition和Zposition,并把食物移動線程和吸引力線程計算的標志位設置為true。如果是第一次點擊地面喂食還會調用singleFood中的startFeed方法,開啟線程,開始喂食,再次點擊的時候此方法不會再被調用,只是不斷地修改線程標志位。
(2)上面已經完成了魚食控制類的詳細介紹,下面將對單個魚食繪制類SingleFood的開發進行介紹,具體包括實例化食物移動線程和食物吸引力線程,設置食物的Yposition以及啟動線程的方法和食物的繪制等,具體代碼如下所示。
說明
此類主要作用是初始化魚食的Yposition,創建魚食移動線程和吸引力線程,當調用此類的startFeed方法時會啟動這兩個線程,最后對魚食進行繪制。
總結
以上是生活随笔為你收集整理的《Android 应用案例开发大全(第二版)》——2.6节绘制相关类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《金蝶ERP—K/3标准财务模拟实训(1
- 下一篇: 《Spark核心技术与高级应用》——1.