《Android 应用案例开发大全(第二版)》——2.6节绘制相关类

    xiaoxiao2024-07-24  23

    本节书摘来自异步社区《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 package com.bn.ld.FishSchool; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class SingleFishSchool { 4 ……//此处省略相关成员变量的声明代码,读者可自行查阅光盘中的源代码 5 LoadedObjectVertexNormalTexture fishSchool; // 创建鱼对象 6 public SingleFishSchool(int texld, TDRender TDRender, String fname, 7 Vector3f Position, Vector3f Speed, Vector3f force, 8 Vector3f ConstantForce, float weight) { 9 this.position = Position; // 鱼的初始位置 10 this.speed = Speed; // 鱼的初始速度 11 this.force = force; // 鱼的初始受力 12 this.weight = weight; // 鱼的质量 13 this.ConstantPosition.x = Position.x; // 不动鱼的_x_位置 14 this.ConstantPosition.y = Position.y; // 不动鱼的_y_位置 15 this.ConstantPosition.z = Position.z; // 不动鱼的_z_位置 16 this.ConstantForce = ConstantForce; // 鱼受到的向心力 17 fishSchool = LoadUtil.loadFromFileVertexOnly("fish3.obj", 18 TDRender.getResources()); // 加载鱼模型 19 } 20 public void drawSelf(GL10 gl) { 21 gl.glPushMatrix(); // 保护矩阵 22 gl.glTranslatef(position.x, position.y, position.z); // 平移 23 gl.glRotatef(yAngle, 0, 1, 0); // 绕_y_轴旋转 24 gl.glRotatef(zAngle, 0, 0, 1); // 绕_z_轴旋转 25 if (fishSchool != null) { // 绘制鱼群 26 fishSchool.drawSelf(gl,texld); 27 } 28 gl.glPopMatrix(); // 恢复矩阵 29 } 30 public void fishschoolMove() { // 根据鱼类位置以及速度来计算鱼的下一个位置 31 if (speed.x == 0 && speed.z == 0 && speed.y > 0) { // _y_轴速度大于0 32 tempZ = -90; 33 tempY = 0; 34 } else if (speed.x == 0 && speed.z == 0 && speed.y < 0) { // _y_轴速度小于0 35 tempZ = 90; 36 tempY = 0; 37 } else if (speed.x == 0 && speed.z == 0 && speed.y == 0) { // _y_轴速度等于0 38 tempZ = 90; 39 tempY = 0; 40 } else { 41 float fz = (speed.x * speed.x + speed.y * 0 + speed.z * speed.z); //分子 42 float fm = (float) (Math.sqrt(speed.x * speed.x + speed.y * speed.y 43 + speed.z * speed.z) * Math.sqrt(speed.x * speed.x 44 + speed.z * speed.z)); // 分母 45 float zhi = fz / fm; // cos值 46 tempZ = (float) (180f / Math.PI) * (float) Math.acos(zhi); // 反三角函数求得旋转角度 47 fz = (speed.x * Constant.initialize.x + speed.z 48 * Constant.initialize.z); //分子 49 fm = (float) (Math.sqrt(Constant.initialize.x 50 * Constant.initialize.x + Constant.initialize.z 51 * Constant.initialize.z) * Math.sqrt(speed.x * speed.x 52 + speed.z * speed.z)); // 分母 53 zhi = fz / fm; // cos值 54 tempY = (float) (180f / Math.PI) * (float) Math.acos(zhi); // 反三角函数求得旋转角度 55 } 56 if (speed.y <= 0) { // 获取夹角根据speed.y的正负性来确定夹角的正负性 57 zAngle = tempZ; 58 } else{ 59 zAngle = -tempZ; //_z_轴旋转角度 60 } 61 if (speed.z > 0){ // 获取夹角根据speed.z的正负性来确定夹角的正负性 62 yAngle = tempY; 63 } else { 64 yAngle = -tempY; // _y_轴旋转角度 65 } 66 if (Math.abs(speed.x + force.x) <Constant.SchoolMaxspeed) { // 动态修改鱼的速度 67 speed.x += force.x; // 外力作用下_x_轴上的速度 68 } 69 if (Math.abs(speed.y + force.y) <Constant.SchoolMaxspeed){ 70 speed.y += force.y; // 外力作用下_y_轴上的速度 71 } 72 if (Math.abs(speed.z + force.z) < Constant.SchoolMaxspeed){ 73 speed.z += force.z; // 外力作用下_z_轴上的速度 74 } 75 if (Math.abs(speed.x + ConstantForce.x) < Constant.SchoolMaxspeed){ // 恒力的作用 76 speed.x += ConstantForce.x; // 恒力作用下_x_轴上的速度 77 } 78 if (Math.abs(speed.y + ConstantForce.y) < Constant.SchoolMaxspeed){ 79 speed.y += ConstantForce.y; // 恒力作用下_y_轴上的速度 80 } 81 if (Math.abs(speed.z + ConstantForce.z) < Constant.SchoolMaxspeed) { 82 speed.z += ConstantForce.z; // 恒力作用下_z_轴上的速度 83 } 84 position.plus(speed); // 鱼群里面鱼的新位移 85 this.force.x = 0; // 将受到的外力置为零 86 this.force.y = 0; 87 this.force.z = 0; 88 }}

    第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以及启动线程的方法和食物的绘制等,具体代码如下所示。

    1 package com.bn.ld.FeedFood; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class SingleFood { 4 public FoodThread Ft; // 食物移动线程 5 public AttractThread At; // 吸引力线程 6 public TDRender Tr; 7 public float Ypositon = Constant.FoodPositionMax_Y; // 获取Yposition 8 LoadedObjectVertexNormalTexture fishFoods; // 创建鱼食的对象 9 public SingleFood (LoadedObjectVertexNormalTexture fishfoods, TDRender Tr) { 10 this.Tr = Tr; 11 fishFoods = fishfoods; // 实例化食物 12 Ft = new FoodThread(this); // 实例化线程 13 At = new AttractThread(this); // 实例化线程 14 } 15 public void startFeed() { // 触控之后就调用这个方法开始线程,食物的位置开始变化 16 Ft.start(); // 启动鱼食移动线程 17 At.start(); // 启动吸引力线程 18 } 19 public void drawSelf(GL10 gl){ 20 gl.glPushMatrix(); // 保护矩阵 21 gl.glTranslatef(Tr.Xposition,this.Ypositon, Tr.Zposition); // 平移 22 fishFoods.drawSelf(gl,this.texld); // 绘制 23 gl.glPopMatrix(); // 恢复矩阵 24 }}

    说明此类主要作用是初始化鱼食的Yposition,创建鱼食移动线程和吸引力线程,当调用此类的startFeed方法时会启动这两个线程,最后对鱼食进行绘制。

    相关资源:Android 3D游戏开发技术详解与典型案例 (吴亚峰,苏亚光) pdf扫描版
    最新回复(0)