当前位置: 首页 > news >正文

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

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

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

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

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

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进行介绍。气泡控制类中包括创建单个气泡对象,创建并且启动气泡移动线程,绘制气泡等。具体代码如下所示。

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代码较长,将在后面详细介绍。具体代码如下所示。

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方法,该方法的作用是根据鱼的速度矢量确定出鱼的朝向,然后计算出坐标轴相应的旋转角度;根据鱼所受到的外力和食物吸引力的作用,动态修改鱼的速度。每次计算每条鱼的受力之后,把所受的力置零。具体代码如下所示。

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.z*Constant.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的开发介绍。在该类中将创建群鱼列表,创建并且启动群鱼的游动线程,然后遍历群鱼列表对除鱼群以外的单条鱼(包括乌龟)进行绘制。具体代码如下所示。

1 package wyf.lxg.fish;
2 .....//此处省略部分类和包的导入代码,请读者自行查阅随书附带光盘的源代码
3 public class FishControl {
4  public ArrayList<SingleFish> fishAl;   //群鱼列表
5  FishGoThread  fgt;      //鱼Go线程
6  public MySurfaceView My;     //渲染器
7  public FishControl(ArrayList<SingleFish> 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;i<this.fishAl.size();i++){ //循环绘制每一条鱼
16    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的开发进行详细的介绍。在单个鱼绘制类中设置鱼的位置,鱼的速度,鱼的质量,鱼受到的外力,鱼受到的向心力(第一条鱼不受到向心力作用)以及鱼的旋转角度,具体代码如下所示。

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的开发介绍。在鱼群控制类中创建了鱼群列表,并将单条鱼对象以及单条鱼的相关信息添加进鱼群列表,同时创建了鱼群游动线程并启动。具体代码如下所示。

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位置,并且启动食物移动线程和食物吸引力线程,然后进行食物的绘制等。具体代码如下所示。

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计算出拾取射线与近平面和远平面的交点坐标,更改鱼食移动线程和吸引力线程的标志位等,具体代码如下所示。

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点共线求出与地面平面的交点)。
第15~27行先判断点击位置是否在规定范围内,如果在规定范围内则将计算的位置赋给Xposition和Zposition,并把食物移动线程和吸引力线程的标志位设为true。如果是第一次点击地面喂食还会调用SingleFood中的StartFeed方法,开启线程,开始喂食,再次点击的时候此方法不会被调用,只是不断地修改线程标志位。

相关文章:

  • 《编写高质量代码:改善c程序代码的125个建议》——建议14-4:尽量避免在同一个数据上执行位操作与算术运算...
  • 《51单片机应用开发范例大全(第3版)》——1.4 【实例19】P1口控制直流电动机实例...
  • 《微信公众平台开发:从零基础到ThinkPHP5高性能框架实践》——2.3 本章小结...
  • 《Adobe Photoshop CS6中文版经典教程(彩色版)》—第1课1.1节开始在Adobe Photoshop中工作...
  • MySQL 大数据量快速插入方法和语句优化
  • 《C++入门经典(第5版•修订版)》——6.8 作业
  • 《Adobe Premiere Pro CC经典教程(彩色版)》——1.4 Premiere Pro界面概述
  • 《树莓派开发实战(第2版)》——2.4 为树莓派配置网络名称
  • 《实施Cisco统一通信管理器(CIPT2)》一1.5 可用性方面面临的挑战
  • 《跨境电商 —— 阿里巴巴速卖通实操全攻略》一一2.4 淘代销
  • 《社会智能与综合集成系统》—第1章1.1节认知科学的发展
  • 《像计算机科学家一样思考Python》——3.13 使用from导入模块
  • 《MySQL DBA修炼之道》——1.2MySQL 的基础架构和版本
  • 定制并发类(七)实现ThreadFactory接口生成自定义的线程给Fork/Join框架
  • 《Python游戏编程快速上手》——2.2 计算表达式
  • JAVA SE 6 GC调优笔记
  • jQuery(一)
  • Kibana配置logstash,报表一体化
  • Nodejs和JavaWeb协助开发
  • underscore源码剖析之整体架构
  • 爱情 北京女病人
  • 机器学习学习笔记一
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 用Python写一份独特的元宵节祝福
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • #laravel 通过手动安装依赖PHPExcel#
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • $L^p$ 调和函数恒为零
  • (0)Nginx 功能特性
  • (ibm)Java 语言的 XPath API
  • (pojstep1.1.2)2654(直叙式模拟)
  • (笔试题)合法字符串
  • (第二周)效能测试
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (三)c52学习之旅-点亮LED灯
  • (三)elasticsearch 源码之启动流程分析
  • (一)appium-desktop定位元素原理
  • (一)Thymeleaf用法——Thymeleaf简介
  • (一)u-boot-nand.bin的下载
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • .apk文件,IIS不支持下载解决
  • .net Application的目录
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET NPOI导出Excel详解
  • .NET 材料检测系统崩溃分析
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET关于 跳过SSL中遇到的问题
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .net下简单快捷的数值高低位切换
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • @Not - Empty-Null-Blank
  • [ JavaScript ] JSON方法
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法