本节书摘来自异步社区《Android 应用案例开发大全(第3版)》一书中的第2章,第2.6节绘制相关类,作者 吴亚峰 , 苏亚光 , 于复兴,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.6 绘制相关类
前面详细介绍了百纳水族馆辅助绘制类的开发过程,下面将对百纳水族馆中的绘制相关类进行详细介绍,主要包括气泡绘制相关类、群鱼绘制相关类、鱼群绘制相关类以及鱼食绘制相关类,从而使读者对百纳水族馆的开发有一个更加深刻地理解。

2.6.1 气泡绘制相关类
真实的水族馆中时常会冒出一些气泡,这样使壁纸显得更加真实,更加具有观赏性。下面将详细介绍绘制气泡相关类,绘制气泡相关类分为气泡控制类BubbleControl,以及用来控制所有气泡的绘制和单个气泡绘制类SingleBubble。单个气泡绘制类SingleBubble用来对单个气泡进行绘制,开发步骤如下所示。

(1)首先介绍单个气泡绘制类SingleBubble。在单个气泡绘制类中给出了气泡的初始位置,随机设置了气泡上升的最大高度等。绘制气泡时用到了混合技术,对对象的绘制顺序是有严格要求的,即绘制顺序是由远及近的,所以,在绘制气泡之前要根据气泡的位置进行排序,具体代码如下所示。

![{37}](/api/storage/getbykey/screenshow?key=151178a4d1d9e6d6e470)代码位置:见随书光盘源代码/第2章/wyf/lxg/bubble/目录下的SingleBubble.Java。
1 package wyf.lxg.bubble;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class SingleBubble implements Comparable<SingleBubble>{
4  Bubble bubble;         //气泡对象
5  float cuerrentX=0;        //气泡当前X位置
6  float cuerrentY=1;        //气泡当前Y位置
7  float cuerrentZ=0;        //气泡当前Z位置
8  float border;         //气泡的最大高度
9  int TexId;          //纹理ID
10  public SingleBubble(MySurfaceView mySurfaceView,int TexId){
11   this.TexId=TexId;
12   bubble=new Bubble(mySurfaceView);      //创建气泡
13   newposition(-1);       //初始气泡的位置
14  }
15  public void drawSelf(){
16   MatrixState.pushMatrix();      //保护矩阵
17   MatrixState.translate(cuerrentX, cuerrentY, cuerrentZ); //移动
18   bubble.drawSelf(TexId);      //绘制气泡
19   MatrixState.popMatrix()          //恢复矩阵
20  }
21  public void bubbleMove(float x,float y) {
22   this.cuerrentY += Constant.BubbleMoveDistance; //气泡上下移动
23   this.cuerrentX +=(float)(0.01*y);    //气泡左右晃动
24   this.cuerrentZ +=(float)(0.015*y)+0.1;   //越来越大效果
25   if (this.cuerrentY > border) {
26    newposition(x);       //重置气泡位置
27  }}
28  public void newposition(float x) {
29   if(x==-1){         //第一处气泡的初始位置
30    cuerrentX = ;      //X位置
31    cuerrentY = ;       //Y位置
32    cuerrentZ = ;      //Z位置
33   }else if(x==1){        //第二处气泡的初始位置
34    cuerrentX = ;       //X位置
35    cuerrentY = ;       //Y位置
36    cuerrentZ = ;       //Z位置
37   }else if(x==0){        //第三处气泡的初始位置
38    cuerrentX = ;       //X位置
39    cuerrentY = ;       //Y位置
40    cuerrentZ = ;       //Z位置
41   }
42   border = (float) (2 * Math.random() + 3);  //气泡上升的最大高度
43  }
44  public int compareTo(SingleBubble another){//重写的比较两个气泡与摄像机距离的方法
45  return ((this.cuerrentZ-another.cuerrentZ)==0)?0:((this.cuerrentZ-another.   cuerrentZ)>0)?1:-1;
46 }}

第1~14行首先声明相关变量,包括气泡的当前位置、气泡的纹理ID、气泡的最大高度、气泡对象等,然后在构造器中第一次调用newposition方法,初始化百纳水族馆中三处气泡的位置,并且随机设置气泡的上升最大高度。
第15~27行首先是根据气泡的当前位置绘制气泡,设置气泡的移动速度,并修改气泡的位置,尤其是Z位置,Z位置离摄像机越来越近,气泡会就产生越来越大的效果。判断气泡的位置是否大于气泡的最大高度border,如果大于border,则调用newposition方法重新设置气泡的位置。
第28~46行首先根据接收到的x值确定气泡出现的位置,其次随机产生气泡上升的最大高度,气泡每次消失后都会重新调用newposition方法,然后重写compareTo方法根据气泡的位置对气泡列表中的气泡对象进行排序。
(2)上面已经对单个气泡绘制类SingleBubble类进行了详细介绍,接下来就应该对所有气泡控制类BubbleControl进行介绍。气泡控制类中包括创建单个气泡对象,创建并且启动气泡移动线程,绘制气泡等。具体代码如下所示。

代码位置:见随书光盘源代码/第2章/wyf/lxg/bubble/目录下的BubbleControl.Java。

1 package wyf.lxg.bubble;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class BubbleControl {
4    public ArrayList<SingleBubble> BubbleSingle=new ArrayList<SingleBubble>();  //气泡类的列表
5  int texld;           //气泡的纹理ID
6     MySurfaceView mv;         //场景渲染器
7  public BubbleControl(MySurfaceView mv,int texld ) {
8   this.mv=mv;
9   this.texld = texld;        //获取ID
10   for (int i = 0; i <Constant.BUBBLE_NUM; i++) {  //创建多个气泡
11         BubbleSingle.add(new SingleBubble(mv,this.texld));//添加到列表
12   }
13   BubbleThread Bgt = new BubbleThread(this);   //创建气泡移动线程
14   Bgt.start();         //启动气泡移动线程
15  }
16  public void drawSelf() {
17   try {
18    Collections.sort(this.BubbleSingle);   //对气泡排序
19    for (int i = 0; i < this.BubbleSingle.size(); i++) {
20     MatrixState.pushMatrix();    //保护矩阵
21     BubbleSingle.get(i).drawSelf();   //绘制气泡
22     MatrixState.popMatrix();    //恢复矩阵
23   }} catch (Exception e) {
24    e.printStackTrace();       //打印异常栈信息
25 }}}
``
第1~15行首先声明包名,创建气泡类列表,声明气泡纹理ID和场景渲染器,在构造器中获取纹理ID,创建多个气泡并且将其添加到气泡列表中,然后创建并且启动气泡移动线程。其中省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码。
第16~25行的主要作用是绘制气泡,因为采用混合技术,所以在绘制气泡之前,要先对气泡列表中的气泡进行排序,之后遍历气泡列表,保护现场,进行气泡绘制,然后恢复现场。**2.6.2 群鱼绘制相关类**
群鱼是百纳水族馆的主要元素,下面将详细介绍群鱼绘制相关类。群鱼绘制相关类分为单条鱼绘制类SingleFish,用来对单条鱼进行绘制;以及群鱼控制类FishControl,用来控制群鱼里所有鱼的绘制,当然也包括乌龟和珍珠贝的绘制。开发步骤如下所示。(1)首先进行的是单条鱼绘制类SingleFish的开发介绍。在SingleFish类中设置了群鱼(包括乌龟)中每个鱼的位置、速度、质量(力的缩放比)、所受到的外力、鱼食对鱼的吸引力、旋转角度等。其中动态修改鱼游动的方法fishMove代码较长,将在后面详细介绍。具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fish/目录下的SingleFish.Java。

1 package wyf.lxg.fish;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class SingleFish {
4 .....//此处省略相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码
5 public SingleFish(MS3DModel mx,int texid,
6 Vector Position, Vector Speed, Vector force,Vector attractforce, float
weight) {
7 this.md=mx; //鱼的模型
8 this.texid=texid; //鱼的纹理ID
9 this.position = Position; //鱼的位置
10 this.speed = Speed; //鱼的速度
11 this.force = force; //鱼所受外力
12 this.attractforce = attractforce; //鱼食对鱼的吸引力
13 this.weight = weight; //鱼的质量
14 }
15 public void drawSelf() {
16 MatrixState.pushMatrix(); //保护矩阵
17 MatrixState.translate(this.position.x,this.position.y,this.position.z);
//平移
18 MatrixState.rotate(yAngle, 0, 1, 0); //Y轴旋转
19 MatrixState.rotate(zAngle, 0, 0, 1); //Z轴旋转
20 if (md != null) {
21 this.md.animate(time,texid); //绘制鱼
22 }
23 MatrixState.popMatrix(); //恢复矩阵
24 time += ; //更新模型动画时间
25 if(time > this.md.getTotalTime()) {//若当前播放时间大于总的动画时间
26 time = time - this.md.getTotalTime();

       //播放时间等于当前播放时间减去总的动画时间

27 }}
28 public void fishMove() {
29 .....//此处省略动态修改鱼游动的代码,将在后面详细介绍
30 }}

第1~14行首先声明包名,通过构造器接收传递过来的鱼的纹理ID、鱼的位置、鱼的速度、鱼所受的外力、鱼食对鱼的吸引力等参数信息。其中此处省略了部分类和包的导入代码以及相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码。
第15~30行是鱼类绘制方法,当然也包括乌龟的绘制。绘制之前先保护变换矩阵再将鱼平移到指定位置,并让坐标轴旋转相应的角度,从而使鱼能以一个正确的姿态显示在屏幕上,然后再进行鱼的绘制。其中骨骼动画播放时间若大于总的动画时间,则实际播放时间等于当前播放时间减去总的动画时间。
(2)接下来详细介绍前面省略的fishMove方法,该方法的作用是根据鱼的速度矢量确定出鱼的朝向,然后计算出坐标轴相应的旋转角度;根据鱼所受到的外力和食物吸引力的作用,动态修改鱼的速度。每次计算每条鱼的受力之后,把所受的力置零。具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fish/目录下的SingleFish.Java。

1 public void fishMove() {
2 float fz = (speed.x speed.x + speed.y 0 + speed.z * speed.z); //分子
3 float fm = (float) (Math.sqrt(speed.x speed.x + speed.y speed.y//分母
4 + speed.z speed.z) Math.sqrt(speed.x speed.x + speed.z speed.z));
5 float angle = fz / fm; //cos值
6 tempZ = (float)( / Math.PI) * (float) Math.acos(angle);//绕Z轴的旋转角度
7 fz = (speed.x Constant.initialize.x + speed.z Constant.initialize.z);//分子
8 fm = (float) (Math.sqrt(Constant.initialize.x * Constant.initialize.x+//分母
9 Constant.initialize.zConstant.initialize.z)Math.sqrt(speed.x*speed.x+
speed.z*speed.z));
10 angle = fz / fm; //cos值
11 tempY = (float) ( / Math.PI) * (float) Math.acos(angle);//绕Y轴的旋转角度
12 if (speed.y <= 0) { //获取夹角,根据Speed.y的正负性来确定夹角的正负性
13 zAngle = tempZ;
14 } else { //上述计算得出的角度均为正值
15 zAngle = -tempZ;
16 }
17 if (speed.z > 0) { //获取夹角,根据Speed.z的正负性来确定夹角的正负性
18 yAngle = tempY;
19 } else { //上述计算得出的角度均为正值
20 yAngle = -tempY;
21 }
22 if (Math.abs(speed.x + force.x) < Constant.MaxSpeed) {
23 speed.x += force.x; //动态修改鱼X方向的速度
24 }
25 if (Math.abs(speed.y + force.y) < Constant.MaxSpeed){
26 speed.y += force.y; //动态修改鱼Y方向的速度
27 }
28 if (Math.abs(speed.z + force.z) < Constant.MaxSpeed) {
29 speed.z += force.z; //动态修改鱼Z方向的速度
30 }
31 if (Math.abs(speed.x + attractforce.x) < Constant.MaxSpeed) {
32 speed.x += attractforce.x; //动态修改鱼X方向的速度
33 }
34 if (Math.abs(speed.y + attractforce.y) < Constant.MaxSpeed) {
35 speed.y += attractforce.y; //动态修改鱼Y方向的速度
36 }
37 if (Math.abs(speed.z + attractforce.z) < Constant.MaxSpeed) {
38 speed.z += attractforce.z; //动态修改鱼Z方向的速度
39 }
40 position.plus(speed); //改变鱼的位置
41 this.force.x = 0; //外力置为零
42 this.force.y = 0;
43 this.force.z = 0;
44 attractforce.x = 0; //鱼食对鱼的吸引力置为零
45 attractforce.y = 0;
46 attractforce.z = 0;
47 }

第1~21行为计算坐标轴相应旋转角度的方法。根据鱼的速度矢量确定出鱼的朝向,利用初等函数计算出坐标轴相应的旋转角度。因为计算出来的角度均为正值,所以需要根据Speed.y和Speed.z的正负性来确定夹角的正负性。
第22~39行为动态修改鱼速度的方法,鱼可能会受到外力(排斥力)和食物吸引力的作用,力会改变鱼的速度,当鱼的速度超过阈值时,速度不再增加。将鱼的速度矢量和位移矢量相加就会得到鱼的新位移矢量。
第40~47行改变鱼的位置,防止鱼穿过地面,然后每次计算每条鱼的受力之后,把所受的力置零。因为每次都要重新计算鱼的受力,因此当鱼不受到力的作用时,鱼的速度就不会再改变,鱼将沿着当前的速度方向游动。
(3)前面已经对单条鱼绘制类SingleFish进行了介绍,下面将进行鱼群控制类FishControl的开发介绍。在该类中将创建群鱼列表,创建并且启动群鱼的游动线程,然后遍历群鱼列表对除鱼群以外的单条鱼(包括乌龟)进行绘制。具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fish/目录下的FishControl.Java。

1 package wyf.lxg.fish;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class FishControl {
4 public ArrayList fishAl; //群鱼列表
5 FishGoThread fgt; //鱼Go线程
6 public MySurfaceView My; //渲染器
7 public FishControl(ArrayList fishAl,MySurfaceView my){
8 this.fishAl = fishAl; //群鱼列表
9 this.My=my;
10 fgt= new FishGoThread(this); //创建鱼移动线程对象
11 fgt.start(); //启动鱼的移动线程
12 }
13 public void drawSelf(){
14 try {
15 for(int i=0;i16 MatrixState.pushMatrix(); //保护矩阵
17 fishAl.get(i).drawSelf(); //绘制鱼
18 MatrixState.popMatrix(); //恢复矩阵
19 }}catch (Exception e) {
20 e.printStackTrace(); //打印异常栈信息
21 }}}

第1~12行为声明群鱼列表、鱼游动线程以及场景渲染器等相关变量,通过构造方法接收群鱼列表,创建鱼游动线程对象并且启动鱼的游动线程。此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码。
第13~21行为绘制鱼的方法。首先保护变换矩阵,其次遍历群鱼列表,循环绘制除鱼群以外的单条鱼以及乌龟,最后恢复变换矩阵,进行异常处理。**2.6.3 鱼群绘制相关类**
前面已经详细介绍了群鱼的绘制相关类,下面将对鱼群绘制相关类的开发进行详细介绍。鱼群绘制相关类分为单条鱼绘制类SingleFishSchool,用来对鱼群中单条鱼进行绘制;以及鱼群控制类FishSchoolControl,用来控制鱼群里所有鱼的绘制。开发步骤如下所示。(1)首先对鱼群中单个鱼的绘制类SingleFishSchool的开发进行详细的介绍。在单个鱼绘制类中设置鱼的位置,鱼的速度,鱼的质量,鱼受到的外力,鱼受到的向心力(第一条鱼不受到向心力作用)以及鱼的旋转角度,具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fishschool/目录下的SingleFishSchool.Java。

1 package wyf.lxg.fishschool;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class SingleFishSchool {
4 .....//此处省略相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码
5 public SingleFishSchool(MS3DModel md,int texid,Vector Position,
6 Vector Speed, Vector force,Vector ConstantForce, float weight) {
7 this.texid=texid; //鱼的纹理ID
8 this.position = Position; //鱼的位置
9 this.speed = Speed; //鱼的速度
10 this.force = force; //鱼所受到的外力
11 this.weight = weight; //鱼的质量
12 this.mt=md; //鱼的模型
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 }
18 public void drawSelf() {
19 MatrixState.pushMatrix(); //保护矩阵
20 MatrixState.translate(this.position.x, this.position.y, this.

position.z);

21 MatrixState.rotate(yAngle, 0, 1, 0); //绕Y轴旋转相应角度
22 MatrixState.rotate(zAngle, 0, 0, 1); //绕Z轴旋转相应角度
23 if (mt != null) {
24 this.mt.animate(time,texid); //播放动画,绘制鱼
25 }
26 MatrixState.popMatrix(); //恢复矩阵
27 .....//此处省略动画更新时间的代码,请读者自行查阅随书附带光盘的源代码
28 }
29 public void fishschoolMove() { //根据鱼类的位置以及速度来计算鱼的下一个位置
30 if (speed.x == 0 && speed.z == 0 && speed.y > 0){ //Y轴速度大于0
31 tempZ = -90;
32 tempY = 0;
33 } else if (speed.x == 0 && speed.z == 0 && speed.y < 0){//Y轴速度小于0
34 tempZ = 90;
35 tempY = 0;
36 } else if (speed.x == 0 && speed.z == 0 && speed.y == 0){//Y轴速度等于0
37 tempZ = 90;
38 tempY = 0;
39 } else {
40 .....//此处与前面的fishMove方法相似,故省略,请读者自行查阅随书附带光盘的源代码
41 }}

第1~17行通过构造器接收鱼的纹理ID,鱼的初始位置,鱼的初始速度,鱼所受到的外力,鱼受到的向心力等。此处省略了部分类和包的导入代码以及相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码。
第18~41行为鱼的绘制方法以及修改鱼速度的方法。首先平移到指定的位置,绕Y轴、Z轴旋转相应的角度,从而使鱼能以一个正确的姿态显示在屏幕上,其次进行鱼的绘制。然后根据鱼类的位置以及速度来计算鱼的下一个位置。
说明
第40行省略的代码与群鱼绘制时单条鱼绘制类中的fishMove方法相似,请读者自行查阅随书附带光盘的源代码。但是鱼群中第一条鱼是不会受到向心力的作用的,且只有其他鱼超出阈值之后才会受到向心力的作用。
(2)前面已经完成了对鱼群中单个鱼的绘制类SingleFishSchool的详细介绍,接下来进行鱼群控制类FishSchoolControl的开发介绍。在鱼群控制类中创建了鱼群列表,并将单条鱼对象以及单条鱼的相关信息添加进鱼群列表,同时创建了鱼群游动线程并启动。具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fishschool/目录下的FishSchoolControl.Java。

1 package wyf.lxg.fishschool;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class FishSchoolControl {
4 .....//此处省略相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码
5 public FishSchoolControl(MS3DModel md,int texid,
6 MySurfaceView tr,Vector weizhi,Vector sudu,float weight) {
7 this.Tr = tr; //场景渲染器
8 this.texid=texid; //纹理ID
9 if(sudu.x>0){ //根据第一条鱼的速度
10 x=sudu.x; //计算鱼的X、Z方向速度
11 }else{
12 x=sudu.x+; //计算鱼的X、Z方向速度
13 }
14 fishSchool.add(new SingleFishSchool(md,this.texid, //第一条鱼
15 weizhi, sudu,new Vector(0, 0, 0), new Vector(0, 0, 0), weight));
16 fishSchool.add(new SingleFishSchool(md,this.texid, //第二条鱼
17 new Vector(weizhi.x, weizhi.y, -Constant.Radius), new Vector(x,
18 , x), new Vector(0, 0, 0), new Vector(0,0, 0), weight));

//方向吸引力重力

19 fishSchool.add(new SingleFishSchool(md,this.texid, //第三条鱼
20 new Vector(Constant.Radius, weizhi.y, weizhi.z), new Vector(x,
21 , x), new Vector(0, 0, 0), new Vector(0,0, 0), weight));
22 fishSchool.add(new SingleFishSchool(md,this.texid, //第四条鱼
23 new Vector(weizhi.x, weizhi.y, Constant.Radius), new Vector(x,
24 , x), new Vector(0, 0, 0), new Vector(0,0, 0), weight));
25 Thread = new FishschoolThread(this); //创建鱼群游动线程
26 hread.start(); //启动鱼群游动线程
27 }
28 public void drawSelf() {
29 try {
30 for (int i = 0; i < this.fishSchool.size(); i++){
31 MatrixState.pushMatrix(); //保护矩阵
32 fishSchool.get(i).drawSelf(); //绘制鱼群
33 MatrixState.popMatrix(); //恢复矩阵
34 }} catch (Exception e){
35 e.printStackTrace(); //打印异常栈信息
36 }}}

第1~13行通过构造器接收鱼类的纹理ID、场景渲染器,并且同过接收到的第一条鱼的速度计算出其余3条鱼的X方向和Z方向速度。此处省略部分类和包的导入代码以及相关成员变量的声明代码,请读者自行查阅随书附带光盘的源代码。
第14~27行向鱼群列表中添加鱼对象,同时创建并启动鱼群游动线程。鱼群列表里的第一条鱼不受其他任何鱼的作用力,只受到墙壁的作用力,并且鱼群里面的鱼(不包括第一条鱼)在特定的条件下受到从该鱼本身指向某个位置的向心力作用。
第28~36行为绘制鱼群的方法。首先循环遍历fishSchool列表,绘制鱼群里面的鱼。在绘制之前要先保护变换矩阵,然后再进行鱼群的绘制,最后恢复变换矩阵。**2.6.4 鱼食绘制相关类**
本小节详细介绍鱼食绘制相关类。在百纳水族馆中可以对游动的鱼进行喂食,点击地面,鱼食就会下落。并且点击地面远点时食物会相对小一些,点击地面近点时食物会相对大一些,从而产生近大远小的效果。其中鱼食的绘制相关类包括鱼食绘制类SingleFood,用来绘制鱼食;以及鱼食控制类FeedFish,用来控制鱼食的绘制以及鱼食的下落速度等。开发步骤如下所示。(1)首先介绍单个鱼食的绘制类SingleFood的开发。SingleFood类具体包括实例化食物移动线程和食物吸引力线程,动态改变食物的Y位置和X位置,并且启动食物移动线程和食物吸引力线程,然后进行食物的绘制等。具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fishfood/目录下的SingleFood.Java。

1 package wyf.lxg.fishfood;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class SingleFood {
4 public FoodThread Ft; //食物移动线程
5 public AttractThread At; //吸引力线程
6 public MySurfaceView mv; //场景渲染器
7 public float Ypositon =Constant.FoodPositionMax_Y; //获取Ypositon
8 LoadedObjectVertexNormalTexture fishFoods; //创建鱼食对象
9 int texld; //纹理ID
10 public SingleFood(int texld,LoadedObjectVertexNormalTexture fishfoods,
MySurfaceView mv){
11 this.texld=texld; //获取纹理ID
12 this.mv = mv;
13 fishFoods = fishfoods; //实例化食物

 Ft = new FoodThread(this);      //实例化食物移动线程

15 At = new AttractThread(this); //实例化吸引力线程
16 }
17 public void StartFeed(){
Ft.start(); //启动鱼食移动线程
19 At.start(); //启动吸引力线程
20 }
21 public void drawSelf() {
22 MatrixState.pushMatrix(); //保护矩阵
23 MatrixState.translate(mv.Xposition,this.Ypositon,mv.Zposition);//平移
24 fishFoods.drawSelf(texld); //绘制鱼食
25 MatrixState.popMatrix(); //恢复矩阵
26 }}

第1~16行主要获取鱼食的Ypositon,创建鱼食对象,声明食物移动线程和吸引力线程,同时通过构造器实例化食物移动线程和吸引力线程。此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码。
第17~26行首先是StartFeed方法,用来启动鱼食移动线程和吸引力线程。然后是drawSelf方法,进行鱼食的绘制,先平移到指定的位置,从而使鱼食能以一个正确的姿态显示在屏幕上,再进行鱼食的绘制。
(2)下面将对鱼食控制类FeedFish进行详细的介绍,具体包括设置鱼食的初始位置,根据地面的高度算出t值,根据t计算出拾取射线与近平面和远平面的交点坐标,更改鱼食移动线程和吸引力线程的标志位等,具体代码如下所示。代码位置:见随书光盘源代码/第2章/wyf/lxg/fishfood/目录下的FeedFish.Java。

1 package wyf.lxg.fishfood;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class FeedFish {
4 MySurfaceView Tr; //场景渲染器
5 boolean start; //启动移动食物线程标志位
6 public FeedFish(MySurfaceView tr) {
7 start = true; //启动移动食物线程
8 this.Tr = tr;
9 }
10 public void startFeed(Vector Start,Vector End) {
11 Vector dv=End.cutPc(Start); //喂食的位置
12 float t=(Constant.Y_HEIGHT -Start.y)/dv.y; //根据地面的高度算出t值
13 float xd=Start.x+t*dv.x; //根据t计算出交点的X坐标值
14 float zd=Start.z+t*dv.z; //根据t计算出交点的Z坐标值
15 if(zd<=Constant.ZTouch_Min ||zd>Constant.ZTouch_Max){
16 Constant.isFeed=true; //超出一定范围鱼食的大小不改变
17 return; //并且位置不改变,食物不重置
18 }
19 Tr.Xposition = xd; //食物的位置
20 Tr.Zposition = zd;
21 Tr.Fooddraw = true; //绘制食物的标志位
22 Tr.singlefood.Ft.Fresit = true; //把重置Yposition的标志位变为true
23 Tr.singlefood.At.Go = true; //将吸引力线程标志位设为true
24 Tr.singlefood.Ft.Go = true; //将喂食线程标志位设为true
25 if (start) { //调用此方法开始移动食物的方法
26 Tr.singlefood.StartFeed(); //开始喂食
27 start = false; //标志位设为false
28 }}}

第1~14行为计算屏幕触控点的位置的算法。首先声明启动食物移动线程的标志位,再根据地面的高度算出t值,然后根据t计算出近平面和远平面与地面的交点的X、Z坐标值(根据3点共线求出与地面平面的交点)。

《Android 应用案例开发大全(第3版)》——第2章,第2.6节绘制相关类相关推荐

  1. 《Android 应用案例开发大全(第二版)》——2.9节壁纸的优化与改进

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.9节壁纸的优化与改进 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区" ...

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

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.6节绘制相关类 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区"异步 ...

  3. 《Android 应用案例开发大全(第二版)》——6.1节Android系统的信使:Intent

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第6章,第6.1节Android系统的信使:Intent ,作者李宁,更多章节内容可以访问云栖社区"异步 ...

  4. 《Android 应用案例开发大全(第二版)》——导读

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的目录 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区"异步社区"公众号查 ...

  5. 《Android 应用案例开发大全(第二版)》——2.3节案例的基本框架

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.3节案例的基本框架 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区" ...

  6. 《Android 应用案例开发大全(第二版)》——1.5节第一个Android程序——Hello Android...

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第1章,第1.5节第一个Android程序--Hello Android ,作者 吴亚峰 , 于复兴 , 杜化美, ...

  7. 《Android 应用案例开发大全(第二版)》——2.4节壁纸的实现

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.4节壁纸的实现,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区"异步社 ...

  8. 《Android 应用案例开发大全(第二版)》——2.2节壁纸策划及准备工作

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.2节壁纸策划及准备工作,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区" ...

  9. 《Android 应用案例开发大全(第二版)》——2.5节辅助绘制类

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.5节辅助绘制类,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区"异步社 ...

  10. 《Android 应用案例开发大全(第二版)》——2.1节案例背景及功能概述

    本节书摘来自异步社区<Android 应用案例开发大全(第二版)>一书中的第2章,第2.1节案例背景及功能概述 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区&quo ...

最新文章

  1. 开发商微信选房后不退认筹金_认筹金贸然转定金退不回 购房人认栽?
  2. 将txt文件内容通过cgi和apache显示在网页上
  3. Little Sub and AA
  4. sso和oauth2.0的简单了解学习
  5. Android注册BroadcastReceiver的两种办法及其区别
  6. js-js的全局变量和局部变量
  7. c语言除法加括号么,算不了除法,为什么?
  8. 如何估算网站日承受最大访问PV
  9. 面试题-为什么索引使用B+树、而不是B树?或者其他红黑树,二叉树
  10. 影视App下载页源码
  11. java.awt.Image转 com.lowagie.text.Image
  12. JindoFS解析 - 云上大数据高性能数据湖存储方案
  13. 「JCVI教程」使用JCVI进行基因组共线性分析(上)
  14. 详解:字符转换函数(大写转小写,小写转大写)
  15. 网站域名被微信/QQ红了(被封锁、被屏蔽、被和谐)后最好的解决方法
  16. 全球及中国DIN 2353压缩配件行业研究及十四五规划分析报告
  17. AirServer最新Win64位个人版投屏软件
  18. 【web前端】js json转数组,数组转json,字符串转数字
  19. 计算机重启无法关机,电脑无法关机或重启是为什么?
  20. oracle数据库归档模式修改ARCHIVELOG

热门文章

  1. 小米手机5c怎么刷入开发版获得ROOT权限
  2. gdt描述_获取全局描述符表GDT的内容 | 学步园
  3. CentOS7 VS CentOS8
  4. Windows 8 RTM强势出击
  5. out.println()方法与%表达式
  6. python爬虫之xpath的使用
  7. 买5000到5500的计算机的配置,这样的电脑配置单报价5500元,客户都嫌贵:劝您还是再看看吧!...
  8. Vue源码 Vue Router(三)matcher 路由匹配器
  9. 最短路径问题---Dijkstra算法详解
  10. C#编写的QQ中转站文件续期工具 - 基于mshtml