《Android 3D 游戏案例开发大全》——6.5节辅助界面相关类

简介:

本节书摘来自异步社区《Android 3D 游戏案例开发大全》一书中的第6章,第6.5节辅助界面相关类,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区“异步社区”公众号查看

6.5 辅助界面相关类
Android 3D 游戏案例开发大全
前一小节介绍了主控制类TXZActivity,本小节将对该游戏的辅助界面相关类进行介绍,该游戏的辅助界面主要是欢迎界面TXZWelcomeView类、菜单界面TXZMenuView类、设置界面TXZSetView类、帮助界面TXZHelpView类、选关界面TXZSelectView类,以及关于界面TXTAboutView类,下面就对这些类的开发进行详细介绍。

6.5.1 欢迎界面类TXZWelcomeView
欢迎界面是进入游戏的第一个界面。此界面包括两幅图片的闪屏,闪屏的开发是为了加载资源时游戏不至于出现黑屏的情况。

(1)下面先介绍欢迎界面主体框架的开发,开发人员需要根据界面的功能来设计欢迎界面类的方法,其代码如下。

1 package com.bn.txz;         //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class TXZWelcomeView extends SurfaceView implements SurfaceHolder.Callback{
4  static Bitmap[] logos;       //Logo图片数组
5  static boolean loadFlag=false;      //是否加载图片的标志位
6  TXZActivity activity;        //activity的引用
7  Bitmap currentLogo;        //当前Logo图片引用
8  Paint paint;               //画笔
9  int currentAlpha=0;         //当前的不透明值
10  int sleepSpan=100;             //动画的时延(ms)
11  float currentX;               //图片位置
12  float currentY;
13  ScreenScaleResult ssr;
14  public TXZWelcomeView(TXZActivity activity) {
15   super(activity);
16   this.activity = activity;
17   ssr=ScreenScaleUtil.calScale(screenWidth, screenHeight);
18   this.loadBitmap();      //加载图片
19   this.getHolder().addCallback(this);    //设置生命周期回调接口的实现者
20   paint = new Paint();        //创建画笔
21   paint.setAntiAlias(true);      //打开抗锯齿
22  }
23  public void loadBitmap(){     //将图片加载进内存的方法
24   if(loadFlag) {     //若加载图片标志位为true,则直接return
25    return;
26   }
27   loadFlag=true;  
28   logos=new Bitmap[2];
29   //加载图片
30   logos[0]=BitmapFactory.decodeResource(this.getResources(),R.drawable.bnkj);  
31   logos[1]=BitmapFactory.decodeResource(this.getResources(),R.drawable.welcome);  
32  } 
33  ……//此处省略了绘制方法,将在下面进行介绍
34  public void surfaceChanged(SurfaceHolder arg0, 
35    int arg1, int arg2, int arg3){}    //画布改变时调用
36  public void surfaceCreated(SurfaceHolder holder){   //创建时被调用
37   ……//此处省略了创建时的具体代码,将在下面进行介绍
38  }
39  public void surfaceDestroyed(SurfaceHolder arg0){}  //销毁时被调用
40 }

第4-13行是此类中各种成员变量引用的声明。第14-22行为此类的构造器,其中,创建了主控制类对象,加载了图片,设置了生命周期回调接口实现者,创建了画笔并对其进行了设置。
第23-32行是将图片加载进内存的方法。第34-39行为重写界面创建、改变,以及摧毁时执行的方法。
(2)欢迎界面中的绘制方法非常简单,先绘制了黑色填充矩形的背景,之后绘制当前的Logo图片,其代码如下。

1  public void onDraw(Canvas canvas){        //重写onDraw方法
2   canvas.save();          //保存画布
3   canvas.translate(ssr.lucX,ssr.lucY);     //移动画布
4   canvas.scale(ssr.ratio,ssr.ratio);       //缩放画布
5   paint.setColor(Color.BLACK);       //设置画笔颜色
6   paint.setAlpha(255);
7   //绘制黑填充矩形清背景
8   canvas.drawRect(0, 0, screenWidthStandard, screenHeightStandard, paint);
9   if(currentLogo==null)return;
10   paint.setAlpha(currentAlpha);      //设置当前不透明度 
11   canvas.drawBitmap(currentLogo, currentX, currentY, paint); //绘制当前的Logo
12   canvas.restore();         //恢复画布
13  }

第3-4行是设置画布。第5-6设置了画笔的颜色和透明度。第7-8行绘制了黑色填充矩形的背景。第9-11行根据当前的透明度设置了画笔,当前Logo不为空时绘制当前Logo,方法中绘制之前需要保存画布,绘制之后恢复画布。
(3)欢迎界面中界面创建时调用的方法非常重要,其中开启了实时更改图片的透明度,并根据此刻的透明度绘制图片的线程,其代码如下。

1 public void surfaceCreated(SurfaceHolder holder) { //创建时被调用 
2  new Thread(){
3   public void run(){
4    for(int j=0;j<logos.length;j++){  //循环每个闪屏的图片
5     Bitmap bm=logos[j];     //闪屏图片为logos中的第j个
6     currentLogo=bm;       //当前闪屏图片赋值
7     currentX=screenWidthStandard/2-bm.getWidth()/2;  //计算图片位置
8     currentY=screenHeightStandard/2-bm.getHeight()/2; 
9     for(int i=255;i>-10;i=i-10) {  //动态更改图片的透明度值并不断重绘
10      currentAlpha=i;
11      if(currentAlpha<0) {  //当前透明值小于零时
12       currentAlpha=0;   //设置当前透明值为0
13     }
14     SurfaceHolder myholder=TXZWelcomeView.this.getHolder();
15     Canvas canvas = myholder.lockCanvas(); //获取画布
16     try{
17       synchronized(myholder){
18        onDraw(canvas);   //绘制
19       }
20     }catch(Exception e){
21       e.printStackTrace();
22     }finally{
23        if(canvas != null){
24        myholder.unlockCanvasAndPost(canvas); //解锁
25       }
26      }
27      try{
28       if(i==255) {     //若是新图片,多等待一会
29        Thread.sleep(1000);
30       }
31       Thread.sleep(sleepSpan);    //线程休眠指定的时间
32      }catch(Exception e) {
33       e.printStackTrace();
34      }}
35    }
36    //闪屏结束则向Activity发送消息
37    activity.handler.sendEmptyMessage(Constant.COMMAND_GOTO_MENU_VIEW);
38   }}.start();         //启动线程
39 }

第5-8行是设置当前闪屏的图片及图片的位置。第28-31行是设置图片透明度更改间隔的时间。第36-37行是闪屏结束时向Activity发送消息进入菜单界面。

6.5.2 菜单界面类TXZMenuView
菜单界面是欢迎界面结束后进入的界面,其中包括了开始游戏、设置、选关、关于、帮助和退出6个按钮,按下相应按钮之后进入不同的界面。

(1)首先介绍菜单界面主体框架的开发,开发人员需要根据界面的功能来设计菜单界面类的方法,其代码如下。

1 package com.bn.txz;        //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class TXZMenuView extends GLSurfaceView{
4  TXZActivity activity;       //Activity引用
5  TXZMenuView menu;          //菜单界面View引用
6  private SceneRenderer mRenderer;    //场景渲染器     
7      public GameData gdMain=new GameData();   //主数据
8      GameData gdDraw=new GameData();    //绘制数据
9      GameData gdTemp=new GameData();    //临时数据
10      boolean flagn=true;
11      float anglet=0;
12   float anglex=25;
13   boolean flagx=false;      //改变角度标志位
14   boolean color=false;       //改变颜色标志位
15   public TXZMenuView(Context context) {
16   super(context);
17   this.activity=(TXZActivity)context;
18   Constant.MENU_IS_WHILE=true;
19   mRenderer = new SceneRenderer();  //创建场景渲染器
20      setRenderer(mRenderer);    //设置渲染器 
21   //设置渲染模式为主动渲染   
22      setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 
23  }
24  ……//此处省略了触控方法onTouchEvent,将在下面进行介绍
25  private class SceneRenderer implements GLSurfaceView.Renderer {
26   VertexTexture3DObjectForDraw button;   //按钮
27   VertexTexture3DObjectForDraw box;    //正方体
28   VertexTexture3DObjectForDraw bigCelestial;  //星空
29   VertexTexture3DObjectForDraw smallCelestial; //星空
30   float yAngle=0;
31   int boxId;
32   ……//此处省略了类似的声明纹理id的声明代码,读者可以自行查阅随书光盘中的源代码
33       VertexTextureNormal3DObjectForDraw[] lovntArray=new 
34      VertexTextureNormal3DObjectForDraw[6]; 
35   Robot robot;        //机器人引用
36   MenuDoActionThread dat;     //菜单界面执行机器人动画线程引用
37   ……//此处省略了绘制方法,将在下面进行介绍
38   @Override
39   public void onSurfaceChanged(GL10 gl, int width, int height) {
40    gl.glViewport(
41      Constant.screenScaleResult.lucX,              Constant.screenScaleResult.lucY, 
42      (int)(Constant.screenWidthStandard*Constant.screenScale-         Result.ratio), 
43      (int)(Constant.screenHeightStandard*Constant.screenScale-        Result.ratio)
44    );
45    Constant.ratio=Constant.screenWidthStandard/Constant.screenHeight-      Standard; 
46        gl.glEnable(GL10.GL_CULL_FACE);   //设置为打开背面剪裁
47   }
48  ……//此处省略了onSurfaceCreated方法,将在下面进行介绍
49  ……//该处省略了本类中初始化纹理的方法,需要的读者请自行查阅随书光盘中的源代码
50 }

第4-14行是各种引用的声明与标志位的创建。第15-23行是类构造器,其中创建了主控制类对象,创建并设置了渲染器。
第26-37行是声明并初始化开发过程中用到的物体对象及纹理id。第38-47行是回调方法onSurfaceChanged,其在画布改变时调用。
(2)接下来为读者介绍的是绘制方法onDrawFrame,其代码如下

1 public void onDrawFrame(GL10 gl) {
2  //清除深度缓存与颜色缓存
3  gl.glClear(GL10.GL_DEPTH_BUFFER_BIT|GL10.GL_COLOR_BUFFER_BIT);
4  gl.glMatrixMode(GL10.GL_PROJECTION);   //设置当前矩阵为投影矩阵
5  gl.glLoadIdentity();       //设置当前矩阵为单位矩阵
6  gl.glFrustumf(-ratio, ratio, -1, 1, 1, 100); //调用此方法计算产生透视投影矩阵
7  gl.glMatrixMode(GL10.GL_MODELVIEW);   //设置当前矩阵为模式矩阵
8  gl.glLoadIdentity();       //设置当前矩阵为单位矩阵 
9  GLU.gluLookAt (        //可能变形的视角——大视角 
10              gl, 0f,  5f, 5f,      //人眼位置
11               0, 5f, 0,        //人眼球看的点
12               0, 1, 0 );     //up向量 
13      synchronized(gdDraw.dataLock) {    //将绘制数据复制到临时数据
14       gdDraw.copyTo(gdTemp);
15      } 
16      gl.glPushMatrix();
17      gl.glRotatef(yAngle, 0, 1, 0);
18      bigCelestial.drawSelf(gl);     //绘制星空
19  smallCelestial.drawSelf(gl);
20      gl.glPopMatrix();
21      gl.glPushMatrix();
22      gl.glTranslatef(Constant.robotXstar, Constant.robotYstar, Constant.robotZstar); 
23      robot.drawSelfAnother(gl);      //绘制机器人
24      gl.glPopMatrix(); 
25      gl.glPushMatrix();
26      gl.glTranslatef(Constant.xOffset, 2, -8.5f);
27      gl.glScalef(4, 4, 4);
28      box.drawSelf(gl, boxId);     //绘制箱子
29      gl.glPopMatrix(); 
30      gl.glMatrixMode(GL10.GL_PROJECTION);  //设置投影矩阵
31  gl.glLoadIdentity();      //设置当前矩阵为单位矩阵 
32  gl.glOrthof(-ratio, ratio, bottom, top, near, far);//调用此方法计算产生正交投影矩阵
33  GLU.gluLookAt(         //设置摄像机    
34    gl, 0,0,10,
35    0,0,0,
36    0,1,0  ); 
37  gl.glMatrixMode(GL10.GL_MODELVIEW);   //设置模式矩阵
38  gl.glLoadIdentity();       //设置当前矩阵为单位矩阵  
39  if(color) {
40        gl.glPushMatrix();
41    gl.glTranslatef(1.3f, 0.8f, 0.1f);
42    gl.glRotatef(-anglet, 0, 1, 0);
43    gl.glRotatef(-anglex, 1, 0, 0);
44    button.drawSelf(gl, start);  //开始游戏
45    gl.glPopMatrix();
46    ……//此处省略了相似的绘制代码,读者可以自行查阅随书光盘中的源代码
47    gl.glPushMatrix();
48    gl.glTranslatef(1.3f, -0.825f, 0.1f);
49    gl.glRotatef(-anglet, 0, 1, 0);
50    gl.glRotatef(-anglex, 1, 0, 0);
51    button.drawSelf(gl,exit);   //退出
52    gl.glPopMatrix();
53   }else{
54         ……//此处省略了与if语句块中相似的绘制代码,读者可以自行查阅随书光盘中的源代码
55   }}

第3-5行清除深度缓存与颜色缓存、设置当前矩阵为投影矩阵并将当前矩阵设置为单位矩阵。
第6-8行是设置为透视投影、设置当前矩阵为模式矩阵并将当前矩阵设置为单位矩阵。第9-12行是设置摄像机的位置。
第13-15行是将绘制数据复制到临时数据。第16-29行是绘制星空、机器人和箱子。
第30-38行是设置为正交投影、设置摄像机、设置当前矩阵为模式矩阵,并将当前矩阵设置为单位矩阵。第39-54行是根据颜色的不同绘制不同纹理id的按钮。
(3)接下来介绍的是方法onSurfaceCreated的开发,其在画布创建时被调用,其代码如下。

1 @Override
2 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
3   gl.glDisable(GL10.GL_DITHER);     //关闭抗抖动 
4   gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 
5    GL10.GL_FASTEST);      //设置特定Hint项目的模式
6   gl.glClearColor(0, 0, 0, 0);    //设置屏幕背景色黑色RGBA
7   gl.glEnable(GL10.GL_DEPTH_TEST);   //打开深度检测
8   gl.glDisable(GL10.GL_CULL_FACE);   //设置为打开背面剪裁
9   gl.glShadeModel(GL10.GL_SMOOTH);   //设置着色模型为平滑着色  
10   Constant.menu_flag=true;    //置menu_flag为true
11   Constant.boxFlag=false;    //置boxFlag为false
12   Constant.xOffset=-9f;     //初始化箱子的位置
13   Constant.robotXstar=-15.5f;   //初始化机器人的位置
14   Constant.robotYstar=0;
15   Constant.robotZstar=-8.5f; 
16           armTexId=initTexture(gl, PicDataManager.picDataArray[4]);//机器人其他部分纹理id
17   headTexId=initTexture(gl, PicDataManager.picDataArray[3]); //机器人头纹理id
18   lovntArray[0]=new VertexTextureNormal3DObjectForDraw( //身体
19     VertexDataManager.vertexPositionArray[17],  //顶点位置数据
20     VertexDataManager.vertexTextrueArray[17],  //顶点纹理数据
21     VertexDataManager.vertexNormalArray[17],  //顶点法向量数据
22     VertexDataManager.vCount[17],     //顶点数
23     armTexId          //纹理id
24   );
25    ……//此处省略了类似的初始化lovntArray[1]到lovntArray[5]代码,
26    //需要的读者可自行查阅本书随书光盘中源代码
27   robot=new Robot(lovntArray,TXZMenuView.this);     //初始化机器人
28       dat=new MenuDoActionThread(robot,TXZMenuView.this);     //创建线程
29       MenuDoActionThread.currActionIndex=0;   //置currActionIndex为0
30       MenuDoActionThread.currStep=0;    //置currStep为0
31       dat.start();         //启动线程
32       boxId=initTexture(gl,PicDataManager.picDataArray[11]); //初始化箱子纹理id
33       ……//此处省略了类似的初始化纹理的代码,需要的读者可自行查阅本书随书光盘中源代码
34       helpl=initTexture(gl, PicDataManager.picDataArray[49]); //退出纹理id
35       button=new VertexTexture3DObjectForDraw(    //按钮
36      VertexDataManager.vertexPositionArray[15],  //顶点坐标数据
37      VertexDataManager.vertexTextrueArray[15],   //纹理坐标
38      VertexDataManager.vCount[15]      //顶点数
39    ); 
40    ……//此处省略了类似的箱子与大小星空对象初始化的代码,需要的读者
41   //可自行查阅本书随书光盘中源代码
42      new Thread(){
43           public void run(){
44              while(Constant.menu_flag) {
45               if(flagn) {      //若flagn为true则更改anglet值
46                anglet+=1f;    //若anglet小于25则加1
47                if(anglet>=25) { 
48                  flagn=false;   //置flagn为false
49                }
50                       }else{     //若anglet大于25则减1
51                anglet-=1f;
52                if(anglet<=-25) {   //置flagn为true
53                 flagn=true;
54                   } }
55    ……//此处省略了类似的anglex改变的代码,需要的读者可自行查阅本书随书光盘中源代码
56                   try {
57       Thread.sleep(90);  //线程休眠指定时间
58     } catch (InterruptedException e) {
59       e.printStackTrace();
60    }}}}.start();        //启动线程
61        new Thread(){
62             public void run(){
63              while(Constant.menu_flag) {
64               color=!color;       //更改color值
65               try{
66                Thread.sleep(500);    //线程休眠指定时间
67               }catch(Exception e) {
68                e.printStackTrace();
69          }}}}.start(); 
70          new Thread(){
71              public void run(){
72               while(Constant.menu_flag) {
73                yAngle+=0.5f;      //更改yAngle的值
74                if(yAngle>=360) {
75                 yAngle=0;
76                }
77                try{
78                 Thread.sleep(100);
79                }catch(InterruptedException e) {
80                 e.printStackTrace();
81           }}}}.start();
82   }}

第3-6行是关闭抗抖动、设置特定Hint项目的模式,以及设置屏幕背景色黑色RGBA。第7-9行是打开深度检测、打开背面剪裁,以及设置着色模式为平滑着色。
第10-15行是设置箱子运动标志位为false、设置机器人运动标志位为true,同时设置了箱子,以及机器人的初始位置。
第16-41行是初始化了开发过程中用到的纹理id及物体对象,同时创建并启动了菜单界面机器人运动的线程。
第42-60行是根据标志位及anglet值进行对anglet值的改变。第61-69行是根据标志位的不同更改颜色标志位。第70-81行是根据标志位更改yAngle的值。
(4)下面为读者介绍的是监听触控的方法onTouchEvent的开发,其是根据触控的位置判断是否按下按钮,其代码如下。

1  public boolean onTouchEvent(MotionEvent e) {
2   int x=(int)e.getX();
3   int y=(int)e.getY();
4   switch(e.getAction()){
5   case MotionEvent.ACTION_DOWN:
6    if(x>=(Constant.Menu_Start_l+Constant.screenScaleResult.lucX)*
7      Constant.screenScaleResult.ratio&&
8      x<=(Constant.Menu_Start_r+Constant.screenScaleResult.lucX)*
9      Constant.screenScaleResult.ratio&&
10      y>=(Constant.Menu_Start_u+Constant.screenScaleResult.lucY)*
11      Constant.screenScaleResult.ratio&&
12      y<=(Constant.Menu_Start_d+Constant.screenScaleResult.lucY)*
13      Constant.screenScaleResult.ratio) {//按下开始游戏按钮
14     activity.gotoGameView();
15     Constant.menu_flag=true;
16     Constant.boxFlag=false;
17     Constant.MENU_IS_WHILE=false;
18    }
19   ……//此处省略了类似的单击设置、选关、关于、帮助按钮的处理代码,
20     //需要的读者可自行查阅本书随书光盘中源代码
21    else if(x>=(Constant.Menu_Quit_l+Constant.screenScaleResult.lucX)*
22      Constant.screenScaleResult.ratio&&
23      x<=(Constant.Menu_Quit_r+Constant.screenScaleResult.lucX)*
24      Constant.screenScaleResult.ratio&&
25      y>=(Constant.Menu_Quit_u+Constant.screenScaleResult.lucY)*
26      Constant.screenScaleResult.ratio&&
27      y<=(Constant.Menu_Quit_d+Constant.screenScaleResult.lucY)*
28      Constant.screenScaleResult.ratio) {//单击退出游戏按钮
29     activity.stopBeiJingYinYue();
30     Constant.menu_flag=false;
31     Constant.boxFlag=false;
32     Constant.MENU_IS_WHILE=false;
33        System.exit(0);
34    }
35    break;
36   case MotionEvent.ACTION_MOVE:
37    break;
38   case MotionEvent.ACTION_UP:
39    break;
40   }
41   return true;
42  }

该方法的开发非常简单,其实现的功能是根据触控点的位置判断其按下的位置是否为指定的按钮位置,若是该按钮则实现相应按下按钮之后的功能。
(5)接下来为读者介绍的是该类用到的机器人的部件类MenuBodyPart,其代码如下。

1 package com.bn.txz;           //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class MenuBodyPart {
4  VertexTextureNormal3DObjectForDraw lovnt;     //绘制者
5  int index;             //部件索引
6  TXZMenuView menu; 
7     ArrayList<MenuBodyPart> childs=new ArrayList<MenuBodyPart>(); //子骨头列表 
8     MenuBodyPart father;           //指向父骨骼的引用 
9     //构造器的入口参数为子骨骼不动点在父坐标系中的坐标
10  public MenuBodyPart(float fx,float fy,float fz,VertexTextureNormal3DObject- ForDraw 
11  lovnt,int index,TXZMenuView menu) {     
12      this.index=index;
13      this.menu=menu;
14  //为子骨骼在初始坐标系中的不动点赋值
15      menu.gdMain.dataArray[index].bdd=new float[]{fx, fy, fz}; 
16      this.lovnt=lovnt;
17     } 
18     public void drawSelf(GL10 gl) {
19       gl.glPushMatrix();
20       gl.glTranslatef (
21        menu.gdTemp.dataArray[index].py[0],  //子骨骼在父骨骼坐标系中的平移
22        menu.gdTemp.dataArray[index].py[1], 
23        menu.gdTemp.dataArray[index].py[2]
24       );
25       gl.glTranslatef (     //子骨骼在父骨骼坐标系中的旋转的辅助平移
26        menu.gdTemp.dataArray[index].pyfz[0], 
27        menu.gdTemp.dataArray[index].pyfz[1], 
28        menu.gdTemp.dataArray[index].pyfz[2]
29       );
30       gl.glRotatef (
31        menu.gdTemp.dataArray[index].xz[0], //子骨骼在父骨骼坐标系中的旋转
32        menu.gdTemp.dataArray[index].xz[1], 
33        menu.gdTemp.dataArray[index].xz[2], 
34        menu.gdTemp.dataArray[index].xz[3]
35       );
36       if(this.lovnt!=null) {
37        this.lovnt.drawSelf(gl);
38       }
39       for(MenuBodyPart bc:childs) {     //然后更新自己的所有孩子
40        bc.drawSelf(gl);
41       }
42       gl.glPopMatrix();     
43     } 
44     //在父坐标系中平移自己
45     public void transtate(float x,float y,float z) { //设置沿_x_、_y_、_z_轴移动 
46       menu.gdMain.dataArray[index].py[0]=x;  //沿_x_轴移动距离
47       menu.gdMain.dataArray[index].py[1]=y;  //沿_y_轴移动距离
48       menu.gdMain.dataArray[index].py[2]=z;  //沿_z_轴移动距离
49     } 
50     //在父坐标系中旋转自己
51     public void rotate(float angle,float x,float y,float z) { //设置绕_x_、_y_、_z_轴转动 
52       menu.gdMain.dataArray[index].xz[0]=angle;   //旋转角度
53       menu.gdMain.dataArray[index].xz[1]=x;
54       menu.gdMain.dataArray[index].xz[2]=y;
55       menu.gdMain.dataArray[index].xz[3]=z;
56       float[] dot={          //不动点
57        menu.gdMain.dataArray[index].bdd[0],
58        menu.gdMain.dataArray[index].bdd[1],
59        menu.gdMain.dataArray[index].bdd[2],
60        1
61       };
62       float[] dotr=new float[4];
63       float[] mtemp=new float[16]; 
64       Matrix.setIdentityM(mtemp, 0);      //计算不动点位置后折返
65       Matrix.rotateM(mtemp, 0, angle, x, y, z);
66       Matrix.multiplyMV(dotr, 0, mtemp, 0, dot, 0);
67      menu.gdMain.dataArray[index].pyfz[0]=-dotr[0]+dot[0];
68      menu.gdMain.dataArray[index].pyfz[1]=-dotr[1]+dot[1];
69      menu.gdMain.dataArray[index].pyfz[2]=-dotr[2]+dot[2];      
70     } 
71     public void setFather(MenuBodyPart f) {    //设置本关节的父关节
72       this.father=f;
73     } 
74     public MenuBodyPart getFather(){     //获得本关节的父关节
75       return father;
76     } 
77     public void addChild(MenuBodyPart child) {   //添加本关节的子关节
78       childs.add(child);
79     } 
80     public MenuBodyPart getChild(int index) {   //获得本关节的子关节
81       return childs.get(index);
82     }
83 }

第4-8行是声明开发过程中用到的对象引用和基本数据类型引用。第9-17行是该类的构造方法,在其他类创建该类对象时被调用。
第18-43行是绘制机器人方法,其实现的功能是先绘制父关节再绘制子关节。第44-49行是在父坐标系中平移自己。第50-70行是在父坐标系中旋转自己。
第50-70行是在父坐标系中旋转自己。第71-73行是设置本关节的父关节。第74-76行是获得本关节的父关节。第77-79行是设置本关节的子关节。第80-82行是获得本关节的子关节。
(6)最后为读者介绍的是本类中执行动作的线程类MenuDoActionThread,其代码如下。

1 package com.bn.txz;          //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class MenuDoActionThread extends Thread{    //机器人动画
4     public static int currActionIndex=0;      //当前动作索引
5     public static int currStep=0;       //当前步骤
6     Action currAction;          //当前动作
7     Robot robot;           //机器人引用
8     TXZMenuView menu;           //菜单界面引用
9     public MenuDoActionThread(Robot robot,TXZMenuView menu) {
10      this.robot=robot;
11      this.menu=menu;
12     }    
13     public void run(){         //重写run方法
14       try {
15    Thread.sleep(50);       //线程休眠指定的时间
16   } catch (InterruptedException e) {
17    e.printStackTrace();
18   }
19       currAction=ActionGenerator.acrobortArray[currActionIndex];//获得当前的动作
20          while(Constant.MENU_IS_WHILE) {
21           if(currStep>=currAction.totalStep) {
22            currActionIndex=(currActionIndex+1)%  //更新currActionIndex
23     ActionGenerator.acrobortArray.length;
24            currAction=ActionGenerator.acrobortArray[currActionIndex];                  //更新currAction
25            currStep=0;         //更新currStep
26           }
27    if(currActionIndex==13||currActionIndex==25) { //更新箱子运动标志位
28         Constant.boxFlag=true;
29        }else{
30         Constant.boxFlag=false;
31        }
32           for(float[] ad:currAction.Robotdata) {   //修改主数据
33            int partIndex=(int) ad[0];     //部件索引
34            int aType=(int)ad[1];       //动作类型
35            if(aType==0) {       //平移 
36             float xStart=ad[2];     //起始点_x_坐标
37             float yStart=ad[3];     //起始点_y_坐标
38             float zStart=ad[4];     //起始点_z_坐标
39             float xEnd=ad[5];      //终点_x_坐标
40             float yEnd=ad[6];      //终点_y_坐标
41             float zEnd=ad[7];      //终点_z_坐标
42             float currX=xStart+(xEnd-xStart)*currStep/currAction.totalStep;                //当前_x_坐标
43             float currY=yStart+(yEnd-yStart)*currStep/currAction.totalStep;                //当前_y_坐标
44             float currZ=zStart+(zEnd-zStart)*currStep/currAction.totalStep;                //当前_z_坐标
45             //更新子骨骼在父骨骼坐标系中的平移
46             robot.bpArrayl[partIndex].transtate(currX, currY, currZ); 
47             if(currActionIndex==13||currActionIndex==25) {
48                   if(Constant.boxFlag) {
49                     if(xEnd-xStart>0) {
50                   Constant.xOffset=Constant.xOffset+10f/
51          currAction.totalStep;
52                  }else if(xEnd-xStart<0) {
53                      Constant.xOffset=Constant.xOffset-10f/
54          currAction.totalStep;
55                 }}}}else if(aType==1) {      //旋转
56             float startAngle=ad[2];    //起始角度
57             float endAngle=ad[3];     //终止角度
58             float currAngle=startAngle+(endAngle-startAngle)*//当前角度
59         currStep/currAction.totalStep;
60             float x=ad[4];         //旋转轴
61             float y=ad[5];
62             float z=ad[6];
63      //更新子骨骼在父骨骼坐标系中的旋转
64             robot.bpArrayl[partIndex].rotate(currAngle, x, y, z); }
65           }    
66           currStep++;        //当前步骤加1
67           synchronized(menu.gdDraw.dataLock) {  //将主数据复制进绘制数据
68            menu.gdMain.copyTo(menu.gdDraw);
69           }
70           try {
71         Thread.sleep(20);     //线程休眠指定的时间
72        } catch (InterruptedException e) {
73         e.printStackTrace();
74  }}} }

第4-8行是声明开发过程中用到的对象引用和基本数据类型引用。第9-12行是该类构造方法,其在其他类创建该类对象时被调用。
第19-31行是为机器人动画时用到的当前动作、当前步骤,以及标志位等赋值。第33-34行是获得部件索引和动作类型。
第35-54行是平移动作,其实现的功能是将机器人沿着指定的坐标轴进行平移。第55-65行是旋转动作,其实现的功能是将机器人绕着指定旋转轴旋转指定的角度。

6.5.3 设置界面类TXZSetView
设置界面是在菜单界面中按下“设置”按钮后进入的界面,在设置界面,玩家可以设置是否使用背景音乐与游戏音效。

(1)首先介绍的是设置界面主体框架的开发,开发人员需要根据界面的功能来设计设置界面类的方法,其代码如下。

1 package com.bn.txz;          //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class TXZSetView extends GLSurfaceView{
4  TXZActivity activity;         //Activity引用
5  private SceneRenderer mRenderer;      //场景渲染器
6  public GameData gdMain=new GameData();     //主数据
7     GameData gdDraw=new GameData();       //绘制数据
8     GameData gdTemp=new GameData();        //临时数据
9     boolean flagn=true;
10     float anglet=0;
11  float anglex=25;
12  boolean flagx=false;         //改变角度标志位
13  boolean color=false;         //颜色标志位
14  public TXZSetView(Context context) {
15   super(context);
16   this.activity=(TXZActivity)context;
17   mRenderer = new SceneRenderer();    //创建场景渲染器
18      setRenderer(mRenderer);      //设置渲染器 
19      setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 
20  }
21  ……//此处省略了触控方法onTouchEvent,将在下面进行介绍
22  private class SceneRenderer implements GLSurfaceView.Renderer {
23   VertexTexture3DObjectForDraw button;   //按钮
24   int headTexId;        //头部纹理ID
25   ……//此处省略了类似的声明纹理id的声明代码,读者可自行查阅随书光盘中的源代码
26   float yAngle=0;
27   VertexTextureNormal3DObjectForDraw[] lovntArray=new //3D物体的引用
28   VertexTextureNormal3DObjectForDraw[6]; 
29   VertexTexture3DObjectForDraw bigCelestial;   //大星空
30   VertexTexture3DObjectForDraw smallCelestial;  //小星空
31   Robot robot;          //机器人引用
32   SetDoActionThread dat;       //执行动作线程引用
33  ……//此处省略了绘制方法,将在下面进行介绍
34  ……//此处省略了与菜单界面相似的onSurfaceChanged方法,读者可自行查看随书光盘中源代码
48  ……//此处省略了onSurfaceCreated方法,将在下面进行介绍
49  ……//该处省略了本类中初始化纹理的方法,需要的读者请自行查阅随书光盘中的源代码
50 }

第4-13行是各种引用的声明。第14-20行是此类的构造器,其在其他类创建该类对象时调用,该构造器中创建并设置了渲染器。
(2)接下来为读者介绍的是方法onSurfaceCreated,其在画布创建时调用,其代码如下。

1 @Override
2 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
3  gl.glDisable(GL10.GL_DITHER);       //关闭抗抖动 
4  gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 
5    GL10.GL_FASTEST);       //设置为使用快速模式
6  gl.glClearColor(0, 0, 0, 0);      //设置屏幕背景色黑色RGBA
7  gl.glEnable(GL10.GL_DEPTH_TEST);     //打开深度检测
8  gl.glDisable(GL10.GL_CULL_FACE);     //设置为打开背面剪裁
9  gl.glShadeModel(GL10.GL_SMOOTH);     //设置着色模型为平滑着色
10  Constant.set_flag=true;
11  Constant.SET_IS_WHILE=true; 
12      armTexId=initTexture(gl, PicDataManager.picDataArray[4]);  //其他部分纹理id
13  headTexId=initTexture(gl, PicDataManager.picDataArray[3]); //机器人头纹理id
14  ……//此处省略了与菜单界面类似的初始化lovntArray[0]到lovntArray[5]代码,
15     //需要的读者可自行查阅本书随书光盘中源代码
16  robot=new Robot(lovntArray,TXZSetView.this);  
17      dat=new SetDoActionThread(robot,TXZSetView.this); 
18      dat.start(); 
19      back=initTexture(gl,PicDataManager.picDataArray[29]);
20  ……//此处省略了类似的初始化纹理的代码,需要的读者可自行查阅本书随书光盘中源代码
21      button=new VertexTexture3DObjectForDraw(   //输赢界面按钮
22    VertexDataManager.vertexPositionArray[15], //房子的顶点坐标数据
23    VertexDataManager.vertexTextrueArray[15],  //房间纹理坐标
24    VertexDataManager.vCount[15]     //顶点数
25  ); 
26   ……//此处省略了与菜单界面相似的线程代码,需要的读者可自行查阅本书随书光盘中源代码
27   ……//此处省略了与菜单界面相似的大小星空初始化代码,读者可自行查阅本书随书光盘中源代码
28   ……//此处省略了与菜单界面相似的线程代码,需要的读者可自行查阅本书随书光盘中源代码
29 }}

第4-7行是设置了使用快捷方式、设置了屏幕背景颜色,同时打开深度检测。第8-9行是设置打开背面剪裁并设置着色模型为平滑着色。
第10-11行是设置相应的标志位为true。第12-25行是初始化开发过程中用到的纹理id和物体对象。
(3)接下来介绍的是界面的绘制方法onDrawFrame的开发,其代码如下。

1 public void onDrawFrame(GL10 gl) {      //重写onDrawFrame方法
2  //清除深度缓存与颜色缓存
3  gl.glClear(GL10.GL_DEPTH_BUFFER_BIT|GL10.GL_COLOR_BUFFER_BIT);
4  gl.glMatrixMode(GL10.GL_PROJECTION);    //设置当前矩阵为投影矩阵
5  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵
6  gl.glFrustumf(-ratio, ratio, -1, 1, 1, 100);    //调用此方法计算产生透视投影矩阵
7  gl.glMatrixMode(GL10.GL_MODELVIEW);    //设置当前矩阵为模式矩阵
8  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵 
9  GLU.gluLookAt (         //可能变形的视角——大视角 
10          gl, 0f, 5f, 5f,        //人眼位置
11           0, 5f, 0,       //人眼球看的点 
12           0, 1, 0       //up向量 
20      );   
21  synchronized(gdDraw.dataLock) {      //将绘制数据复制进临时数据
22   gdDraw.copyTo(gdTemp);
23  } 
24  gl.glPushMatrix();
25  gl.glRotatef(yAngle, 0, 1, 0);
26  bigCelestial.drawSelf(gl);      //绘制星空
27  smallCelestial.drawSelf(gl);
28  gl.glPopMatrix();
29  gl.glPushMatrix();
30  gl.glTranslatef(-5f, 0, -2.5f); 
31      robot.drawSelfSet(gl);       //绘制物体
32      gl.glPopMatrix(); 
33      gl.glMatrixMode(GL10.GL_PROJECTION);   //设置投影矩阵
34  gl.glLoadIdentity();       //设置当前矩阵为单位矩阵
35  gl.glOrthof(-ratio, ratio, bottom, top, near, far);//调用此方法计算产生正交投影矩阵 
36   GLU.gluLookAt(         //设置摄像机 
37    gl, 0,0,10,
39    0,0,0,
40    0,1,0  
41   );  
42  gl.glMatrixMode(GL10.GL_MODELVIEW);   //设置模式矩阵
43      gl.glLoadIdentity();        //设置当前矩阵为单位矩阵  
44      if(color) {
45          gl.glPushMatrix();
46        gl.glTranslatef(0.6f, 0.6f, 0.1f); 
47        gl.glRotatef(-anglet, 0, 1, 0);
48        gl.glRotatef(-anglex, 1, 0, 0);
49        button.drawSelf(gl, bgmusic);
50        gl.glPopMatrix();
51        if(Constant.IS_BEIJINGYINYUE) {   //如果使用背景音乐
52         gl.glPushMatrix();
53         gl.glTranslatef(1.4f, 0.6f, 0.1f);
54         gl.glRotatef(-anglet, 0, 1, 0);
55                gl.glRotatef(-anglex, 1, 0, 0);
56         gl.glScalef(0.5f, 1f, 0.5f);
57         button.drawSelf(gl, off);    //绘制按钮
58         gl.glPopMatrix();
59        }else {
60       ……//此处省略了相似的绘制代码,读者可以自行查阅随书光盘中的源代码
61        } 
62        gl.glPushMatrix();
63        gl.glTranslatef(0.6f, 0.2f, 0.1f); 
64        gl.glRotatef(-anglet, 0, 1, 0);
65         gl.glRotatef(-anglex, 1, 0, 0);
66        button.drawSelf(gl, bgyouxi);
67        gl.glPopMatrix(); 
68        if(Constant.IS_YINXIAO) {     //如果使用音效
69         gl.glPushMatrix();
70         gl.glTranslatef(1.4f, 0.2f, 0.1f);
71         gl.glRotatef(-anglet, 0, 1, 0);
72          gl.glRotatef(-anglex, 1, 0, 0);
73         gl.glScalef(0.5f, 1f, 0.5f);
74         button.drawSelf(gl, off);
75         gl.glPopMatrix();
76        } else {
77       ……//此处省略了相似的绘制代码,读者可以自行查阅随书光盘中的源代码
78        }
79        gl.glPushMatrix();
80        gl.glTranslatef(1.2f, -0.5f, 0.1f);
81        gl.glRotatef(-anglet, 0, 1, 0);
82         gl.glRotatef(-anglex, 1, 0, 0);
83        button.drawSelf(gl, back);     //绘制返回按钮
84        gl.glPopMatrix();
85          }else{
86   ……//此处省略了相似的绘制代码,读者可以自行查阅随书光盘中的源代码
87   }}

第2-5行是设置清除深度缓存与颜色缓存,并设置当前矩阵为投影矩阵和模式矩阵。第6-20行是设置为透视投影、设置当前矩阵为投影矩阵和模式矩阵,并设置了摄像机。
第21-23行是将绘制数据复制进临时数据。第24-32行是在透视投影下绘制3D物体。第44-86行是在正交投影下绘制按钮。
(4)接下来介绍的是此类中监听触控的方法onTouchEvent的开发,此方法中根据触控位置的不同,判断是否按下了按钮,其代码如下。

1 public boolean onTouchEvent(MotionEvent e) {
2  int x=(int)e.getX();         //记录触控点的x坐标
3  int y=(int)e.getY();         //记录触控点的y坐标
4  switch(e.getAction()){
5    case MotionEvent.ACTION_DOWN:      //按下动作
6    if(x>=(Constant.Set_kai_1_l+Constant.screenScaleResult.lucX)*
7       Constant.screenScaleResult.ratio&& //按下背景音乐的开关按钮
8      x<=(Constant.Set_kai_1_r+Constant.screenScaleResult.lucX)*
9       Constant.screenScaleResult.ratio&&
10      y>=(Constant.Set_kai_1_u+Constant.screenScaleResult.lucY)*
11       Constant.screenScaleResult.ratio&&
12      y<=(Constant.Set_kai_1_d+Constant.screenScaleResult.lucY)*
13       Constant.screenScaleResult.ratio) {
14     Constant.IS_BEIJINGYINYUE=!Constant.IS_BEIJINGYINYUE;
15     if(Constant.IS_BEIJINGYINYUE) {
16      activity.playBeiJingYinYue();  //播放音乐
17     }else{
18      activity.stopBeiJingYinYue();  //关闭音乐
19    }}else if(x>=(Constant.Set_kai_2_l+Constant.screenScaleResult.lucX)*
20      Constant.screenScaleResult.ratio&& //按下游戏音效的开关按钮
21      x<=(Constant.Set_kai_2_r+Constant.screenScaleResult.lucX)*
22      Constant.screenScaleResult.ratio&&
23      y>=(Constant.Set_kai_2_u+Constant.screenScaleResult.lucY)*
24      Constant.screenScaleResult.ratio&&
25      y<=(Constant.Set_kai_2_d+Constant.screenScaleResult.lucY)*
26      Constant.screenScaleResult.ratio) {
27     Constant.IS_YINXIAO=!Constant.IS_YINXIAO;
28    }else if(x>=(Constant.Set_back_l+Constant.screenScaleResult.lucX)*
29      Constant.screenScaleResult.ratio&& //按下返回按钮
30      x<=(Constant.Set_back_r+Constant.screenScaleResult.lucX)*
31      Constant.screenScaleResult.ratio&&
32      y>=(Constant.Set_back_u+Constant.screenScaleResult.lucY)*
33      Constant.screenScaleResult.ratio&&
34      y<=(Constant.Set_back_d+Constant.screenScaleResult.lucY)*
35      Constant.screenScaleResult.ratio) {
36     activity.gotoMenuView();
37     Constant.SET_IS_WHILE=false;  //置SET_IS_WHILE为false
38     Constant.set_flag=false;   //置set_flag为false
39    }
40    break;
41  case MotionEvent.ACTION_MOVE:
42    break;
43  case MotionEvent.ACTION_UP:
44    break;
45   }
46   return true;
47  }

该方法的开发非常简单,其实现的功能是判断是否按下指定的按钮,若是则执行相应的功能代码。由于 SetBodyPart 和 SetDoActionThread 与菜单界面的Menu BodyPart 和Menu DoActionThread 非常相似,因此笔者不再一一赘述,需要的读者请自行查阅本书随书光盘中的源代码。

6.5.4 选关界面类TXZSelectView
选关界面是在菜单界面中按下“选关”按钮后进入的界面,选关界面绘制了本游戏中所有的关卡,若关卡已解锁,按下相应的关卡则进入相应关的游戏。

(1)首先为读者介绍的是选关界面主体框架的开发,开发人员需要根据界面的功能来设计菜单界面类的方法,其代码如下。

1 package com.bn.txz;           //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class TXZSelectView extends GLSurfaceView{
4  public SceneRenderer mRenderer; 
5  float x,y;            //触控点的x与y坐标
6  boolean flagn=true;
7      float anglet=0;
8  float anglex=25;
9  boolean flagx=false;
10  boolean color=false;
11  public GameData gdMain=new GameData();     //主数据
12      GameData gdDraw=new GameData();      //绘制数据
13      GameData gdTemp=new GameData();       //临时数据
14  int count=0;
15  TXZActivity activity;
16  public TXZSelectView(TXZActivity activity) {
17   super(activity);
18   this.activity=activity;
19   Constant.SET_IS_WHILE=true;
20   this.count=activity.sharedUtil.getPassNum();
21   mRenderer = new SceneRenderer();
22   setRenderer(mRenderer);        //设置渲染器
23   setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置为主动渲染
24  }
25  ……//此处省略了触控方法onTouchEvent,将在下面进行介绍
26  private class SceneRenderer implements GLSurfaceView.Renderer{
27   VertexTexture3DObjectForDraw bgButton;    //按钮
28   VertexTexture3DObjectForDraw bigCelestial;   //星空
29   VertexTexture3DObjectForDraw smallCelestial;  //星空
30   Robot robot;
31   //从obj文件中加载的3D物体的引用
32   VertexTextureNormal3DObjectForDraw[] lovntArray=new 
33     VertexTextureNormal3DObjectForDraw[6]; 
34   SelectDoActionThread dat;
35   float yAngle=0;
36   int bgButtonId[]=new int[9];      //关卡按钮纹理id
37   int bgButtonBack;         //返回按钮纹理id
38   int bgButtonBackl;
39   int suoId;
40   int headTexId;         //头部纹理id
41   int armTexId;          //其他部位纹理id
42   ……//此处省略了绘制方法,将在下面进行介绍
43   @Override
44   public void onSurfaceChanged(GL10 gl, int width, int height) {
45    gl.glViewport(
46      Constant.screenScaleResult.lucX, Constant.screenScaleResult.lucY, 
47      (int)(Constant.screenWidthStandard*Constant.screenScaleResult.ratio),
48      (int)(Constant.screenHeightStandard*Constant.screenScaleResult.ratio)
49    );
50    Constant.ratio=Constant.screenWidthStandard/Constant.screenHeightStandard;  
51       gl.glEnable(GL10.GL_CULL_FACE);     //设置为打开背面剪裁
52   }
53   ……//此处省略了onSurfaceCreated方法,将在下面进行介绍
54   public void initTexId(GL10 gl) {    //初始化纹理id
55    bgButtonId[0]=initTexture(gl, PicDataManager.picDataArray[21]);                //关卡1按钮纹理id
56    ……//该处省略了类似的初始化纹理id的代码,需要的读者请自行查
57     //阅随书光盘中的源代码
58   }
59   ……//该处省略了本类中初始化纹理的方法,需要的读者请自行查阅随书光盘中的源代码
60   }}

第4-15行是声明或初始化开发过程中用到的各种引用。第16-24行是该类的构造方法,其在其他类创建该类对象时被调用。
第27-41行是声明3D界面用到的物体对象引用和开发过程中用到的纹理id的引用。第43-52行是重写方法onSurfaceChanged,在该方法中设置了视口大小并计算了宽高比。第54-58行是初始化纹理id的方法。
(2)接下来为读者介绍的是绘制方法onDrawFrame的开发,其代码如下。

1 @Override
2 public void onDrawFrame(GL10 gl)  {     //重写onDrawFrame方法
3  //清除深度缓存与颜色缓存
4  gl.glClear(GL10.GL_DEPTH_BUFFER_BIT|GL10.GL_COLOR_BUFFER_BIT);
5  gl.glMatrixMode(GL10.GL_PROJECTION);    //设置当前矩阵为投影矩阵
6  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵
7  gl.glFrustumf(-ratio, ratio, bottom, top, near, far); //调用此方法计算产生透视投影矩阵
8  gl.glMatrixMode(GL10.GL_MODELVIEW);    //设置当前矩阵为模式矩阵
9  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵
10  GLU.gluLookAt(        //设置摄像机 
11           gl, 
12            0f, 5f, 5f,        //人眼位置
13            0, 5f, 0,         //人眼球看的点
14            0, 1, 0       //up向量
15      );     
16  synchronized(gdDraw.dataLock) {    //将绘制数据复制进临时数据
17    gdDraw.copyTo(gdTemp);
18  } 
19  gl.glPushMatrix();
20      gl.glRotatef(yAngle, 0, 1, 0);
21      bigCelestial.drawSelf(gl);       //绘制星空
22  smallCelestial.drawSelf(gl);
23      gl.glPopMatrix();
24      gl.glPushMatrix();
25      gl.glTranslatef(-4f, 0, -5.5f); 
26      robot.drawSelfSelect(gl);        //绘制物体
27      gl.glPopMatrix();
28      gl.glMatrixMode(GL10.GL_PROJECTION);    //设置投影矩阵
29  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵
30  gl.glOrthof(-ratio, ratio, bottom, top, near, far);//调用此方法计算产生正交投影矩阵 
31   GLU.gluLookAt (          //设置摄像机 
32    gl, 0,0,10,
33    0,0,0,
34    0,1,0  
35   ); 
36  gl.glMatrixMode(GL10.GL_MODELVIEW);    //设置模式矩阵
37      gl.glLoadIdentity();         //设置当前矩阵为单位矩阵 
38      gl.glEnable(GL10.GL_BLEND);      //开启混合
39  //设置源混合因子与目标混合因子
40  gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); 
41      for(int i=0;i<9;i++){
42          if(i<count) {
43           if(i==0){
44            gl.glPushMatrix();
45         gl.glTranslatef(0.12f, 0.6f, 0.1f);
46          bgButton.drawSelf(gl,bgButtonId[i] );
47         gl.glPopMatrix();
48           }
49     ……//此处省略了相似的处理i==1到i==8时的代码,读者可自行查阅随书光盘中的源代码
50         gl.glDisable(GL10.GL_BLEND);      //开启混合 
51         if(color) {
52       gl.glPushMatrix();
53   gl.glTranslatef(1.2f, -0.65f, 0.1f);
54   gl.glRotatef(-anglet, 0, 1, 0);
55       gl.glRotatef(-anglex, 1, 0, 0);
56   bgButton.drawSelf(gl, bgButtonBack);
57   gl.glPopMatrix();
58         }else{
59       ……//此处省略了相似的绘制代码,读者可以自行查阅随书光盘中的源代码
60  }}

第3-6行是清除深度缓存颜色缓存并设置当前矩阵为模式矩阵和单位矩阵。第7-9行是设置透视投影矩阵,并设置当前矩阵为模式矩阵和单位矩阵。第10-15行是设置摄像机。
第16-18行是将绘制数据复制进临时数据。第19-27行是绘制3D场景中的物体。第28-30行设置当前矩阵为模式矩阵和单位矩阵并且设置为正交投影。
第31-35行是设置摄像机。第38-40行是设置为允许混合,并设置源混合因子和目标混合因子。第41-59行是绘制选关界面中的关卡及返回按钮。
(3)下面为读者介绍的是方法onSurfaceCreated的开发,该方法在画布创建时被调用,其代码如下。

1 @Override
2 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
3  gl.glDisable(GL10.GL_DITHER);      //关闭抗抖动 
4  gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 
5    GL10.GL_FASTEST);      //设置为使用快速模式
6  gl.glClearColor(0, 0, 0, 0);     //设置屏幕背景色黑色RGBA
7  gl.glEnable(GL10.GL_DEPTH_TEST);    //打开深度检测
8  gl.glDisable(GL10.GL_CULL_FACE);    //设置为打开背面剪裁
9  gl.glShadeModel(GL10.GL_SMOOTH);    //设置着色模型为平滑着色 
10  initTexId(gl);       //初始化纹理
11  Constant.select_flag=true;
12  Constant.SELECT_IS_WHILE=true;
13  bgButton=new VertexTexture3DObjectForDraw(   //输赢界面按钮
14    VertexDataManager.vertexPositionArray[15], //房子的顶点坐标数据
15    VertexDataManager.vertexTextrueArray[15],  //房间纹理坐标
16    VertexDataManager.vCount[15]     //顶点数
17  );
18  ……//此处省略了相似的星空与机器人部件对象初始化代码,读者可自行查阅随书光盘中的源代码
20  }

该方法的开发非常简单,其实现的主要功能是,设置了画布的基本情况,并初始化了开发过程中用到的物体对象和纹理id。
(4)最后为读者介绍的是监听触控的方法onTouchEvent的开发,其是根据触控的位置判断是否按下按钮,然后执行相应的操作,其代码如下。

1 @Override
2 public boolean onTouchEvent(MotionEvent event) {
3  x=event.getX();          //记录触控点的x坐标
4  y=event.getY();          //记录触控点的y坐标
5  switch(event.getAction()){
6   case MotionEvent.ACTION_DOWN:
7    if(count>=1&&x>=(Constant.Select_1_l+Constant.screenScaleResult.lucX)*
8      Constant.screenScaleResult.ratio&&
9      x<=(Constant.Select_1_r+Constant.screenScaleResult.lucX)*
10      Constant.screenScaleResult.ratio&&
11      y>=(Constant.Select_1_u+Constant.screenScaleResult.lucY)*
12      Constant.screenScaleResult.ratio
13      &&y<=(Constant.Select_1_d+Constant.screenScaleResult.lucY)*
14      Constant.screenScaleResult.ratio) {//按下第一关
15     com.bn.txz.game.GameData.level=1; //置关卡数为1
16     Constant.SELECT_IS_WHILE=false; //置SELECT_IS_WHILE为false
17     Constant.select_flag=false;  //置select_flag为false
18     activity.gotoGameView();   //返回游戏界面
19    }
20     ……//该处省略了相似的按下第2-9关的处理代码,读者可自行查阅随书光盘中的源代码
21    else if(x>=(Constant.Select_back_l+Constant.screenScaleResult.lucX)*
22      Constant.screenScaleResult.ratio&&
23      x<=(Constant.Select_back_r+Constant.screenScaleResult.lucX)*
24      Constant.screenScaleResult.ratio&&
25      y>=(Constant.Select_back_u+Constant.screenScaleResult.lucY)*
26      Constant.screenScaleResult.ratio&&
27      y<=(Constant.Select_back_d+Constant.screenScaleResult.lucY)*
28      Constant.screenScaleResult.ratio) { //单击返回按钮
29     Constant.SELECT_IS_WHILE=false;  //置SELECT_IS_WHILE为false
30     Constant.select_flag=false;  //置select_flag为false
31     activity.gotoMenuView();   //返回到菜单界面
32    }
33   break;
34   case MotionEvent.ACTION_MOVE:
35   break;
36   case MotionEvent.ACTION_UP:
37   break;
38  }
39  return true;
40   }

该方法的开发相当简单,其实现的功能是,根据按下区域的不同判断是否单击某个按钮,并且根据单击按钮的不同实现相应选关与返回的功能。

6.5.5 帮助界面类TXZHelpView
帮助界面是在菜单界面中单击“帮助”按钮后进入的界面,帮助界面绘制了本游戏的一些简单操作,可以帮助玩家更好地了解本游戏的操作。

(1)下面先介绍帮助界面主体框架的开发,开发人员需要根据界面的功能来设计菜单界面类的方法,其代码如下。

1 package com.bn.txz;          //声明包
2 ……//此处省略了本类中导入类的代码,读者可以自行查阅随书光盘中的源代码
3 public class TXZHelpView extends GLSurfaceView{
4  TXZActivity activity;         //Activity引用
5  private SceneRenderer mRenderer;      //场景渲染器
6  boolean isLoadedOk=false;        //是否加载完成标志位
7  boolean inLoadView=true;        //是否在加载界面标志位
8  private int load_step=0;        //进度条步数
9  VertexTexture3DObjectForDraw laodBack;     //加载界面背景图
10  VertexTexture3DObjectForDraw processBar;   //加载界面中的进度条矩形
11  VertexTexture3DObjectForDraw loading;    //加载界面中文字矩形
12  public TXZHelpView(TXZActivity activity) {
13   super(activity);
14   this.activity=activity;
15   mRenderer = new SceneRenderer();    //创建场景渲染器
16   setRenderer(mRenderer);
17   setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//渲染模式为主动渲染
18  }
19  ……//此处省略了触控方法onTouchEvent,将在下面进行介绍
20  private class SceneRenderer implements GLSurfaceView.Renderer {
21   VertexTexture3DObjectForDraw helpback;
22   VertexTexture3DObjectForDraw button;
23   int helpId[]=new int[10];
24   int prepageId;
25   int nextpageId;
26   int loadBackId;       //加载界面背景矩形纹理
27   int processBeijing;      //加载界面进度条矩形背景
28   int tex_processId;      //进度条
29   int loadId;
30   private boolean isFirstFrame=true;
31   @Override
32   public void onDrawFrame(GL10 gl) {
33    //清除深度缓存与颜色缓存
34    gl.glClear(GL10.GL_DEPTH_BUFFER_BIT|GL10.GL_COLOR_BUFFER_BIT);
35    if(!isLoadedOk) {      //如果加载未完成
36     inLoadView=true;
37     drawLoadingView(gl);
38    }else{        //如果加载已完成
39     inLoadView=false;
40     drawHelpView(gl);
41   }}
42   @Override
43   public void onSurfaceChanged(GL10 gl, int width, int height) {
44    gl.glViewport(
45      Constant.screenScaleResult.lucX, Constant.screenScaleResult.   
    lucY, 
46      (int)(Constant.screenWidthStandard*Constant.screenScale-   
    Result.ratio), 
47      (int)(Constant.screenHeightStandard*Constant.screenScale-   
    Result.ratio)
48    );
49    Constant.ratio=Constant.screenWidthStandard/Constant.screenHeight-   
    Standard;  
50        gl.glEnable(GL10.GL_CULL_FACE);    //设置为打开背面剪裁
51   }
52   ……//此处省略了onSurfaceCreated方法,将在下面进行介绍
53   ……//该处省略了本类中初始化纹理的方法,需要的读者请自行查阅随书光盘中的源代码
54   ……//此处省略了drawLoadingView方法,需要的读者请自行查阅随书光盘中的源代码
55   ……//此处省略了loadResource方法,需要的读者请自行查阅随书光盘中的源代码
56   ……//此处省略了drawHelpView方法,将在下面进行介绍
57  }
58 }

第4-11行是声明开发过程中用到的各个对象的引用。第12-18行为此类的构造器,其中创建了渲染器,并设置了渲染模式。第21-29行是声明开发过程中用到的3D物体对象的引用和纹理id。
第32-41行是重写的onDrawFrame方法,在该方法中根据资源是否加载完成的标志位来绘制不同的界面。第42-51行是重写的onSurfaceChanged方法,在该方法中设置了视口的大小,并计算了宽高比。
(2)接下来介绍监听触控的方法onTouchEvent,其代码如下。

1 @Override
2 public boolean onTouchEvent(MotionEvent event) {    //重写onTouchEvent方法
3  float x=event.getX();         //记录触控点的x坐标
4  float y=event.getY();         //记录触控点的y坐标
5  switch(event.getAction()){
6        case MotionEvent.ACTION_DOWN:
7   if(x<(Constant.Help_pre_r+Constant.screenScaleResult.lucX)*
8     Constant.screenScaleResult.ratio
9     &&x>(Constant.Help_pre_l+Constant.screenScaleResult.lucX)*
10     Constant.screenScaleResult.ratio
11     &&y<(Constant.Help_pre_b+Constant.screenScaleResult.lucY)*
12     Constant.screenScaleResult.ratio
13     &&y>(Constant.Help_pre_t+Constant.screenScaleResult.lucY)*
14     Constant.screenScaleResult.ratio) { //单击左边按钮
15    if(idKey==0) {
16     activity.gotoMenuView();    //返回菜单界面
17     idKey=0;        //如果是第一页
18    }else{
19     idKey=idKey-1;      // idKey为idKey减1
20   }}
21   if(x<(Constant.Help_next_r+Constant.screenScaleResult.lucX)*
22     Constant.screenScaleResult.ratio
23     &&x>(Constant.Help_next_l+Constant.screenScaleResult.lucX)*
24     Constant.screenScaleResult.ratio
25     &&y<(Constant.Help_next_b+Constant.screenScaleResult.lucY)*
26     Constant.screenScaleResult.ratio
27     &&y>(Constant.Help_next_t+Constant.screenScaleResult.lucY)*
28     Constant.screenScaleResult.ratio) { //单击右边按钮
29    if(idKey==9) {       //如果是最后一页
30     activity.gotoMenuView();    //返回菜单界面
31     idKey=0;        //置idKey为0
32    }else{
33     idKey=idKey+1;      // idKey为idKey加1
34   }}
35   break;
36  }
37  return true;
38  }

该方法的开发非常简单,其实现的功能是:根据获取的触控位置判断单击的是否是按钮的位置,单击的若是按钮的位置则根据单击按钮的不同来更改相应的idKey值,若是idKey等于0时按下上一张;或idKey等于9时按下下一张,则界面跳转到菜单界面。
(3)接下来介绍监听触控的方法绘制界面的方法onSurfaceCreated,其代码如下。

1 @Override
2 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
3   gl.glDisable(GL10.GL_DITHER);      //关闭抗抖动 
4   gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 
5     GL10.GL_FASTEST);      //设置为使用快速模式
6   gl.glClearColor(0, 0, 0, 0);     //设置屏幕背景色黑色RGBA
7   gl.glEnable(GL10.GL_DEPTH_TEST);    //打开深度检测
8   gl.glDisable(GL10.GL_CULL_FACE);    //设置为打开背面剪裁
9   gl.glShadeModel(GL10.GL_SMOOTH);    //设置着色模型为平滑着色 
10   loadBackId=initTexture(gl, PicDataManager.picDataArray[12]);//背景纹理id
11   processBeijing=initTexture(gl, PicDataManager.picDataArray[13]);                //进度条背景纹理id
12   tex_processId=initTexture(gl, PicDataManager.picDataArray[14]);//进度条纹理id
13   loadId=initTexture(gl, PicDataManager.picDataArray[15]); //背景纹理id
14   laodBack=new VertexTexture3DObjectForDraw(    //背景矩形
15     VertexDataManager.vertexPositionArray[22],  //顶点坐标数据
16     VertexDataManager.vertexTextrueArray[22],   //纹理坐标
17     VertexDataManager.vCount[22]      //顶点数
18   );
19   processBar=new VertexTexture3DObjectForDraw(  //加载界面背景矩形
20     VertexDataManager.vertexPositionArray[11], //顶点坐标数据
21     VertexDataManager.vertexTextrueArray[11],  //纹理坐标
22     VertexDataManager.vCount[11]     //顶点数
23   );
24   loading=new VertexTexture3DObjectForDraw(   //加载界面文字矩形
25     VertexDataManager.vertexPositionArray[12], //顶点坐标数据
26     VertexDataManager.vertexTextrueArray[12],  //纹理坐标
27     VertexDataManager.vCount[12]     //顶点数
28  ); }

该方法的开发非常简单,其实现的主要功能是设置了画布的基本情况,并初始化了开发过程中用到的物体对象和纹理id。
(4)最后为读者介绍的是绘制帮助界面的方法drawHelpView,其代码如下。

1 public void drawHelpView(GL10 gl) {
2  gl.glMatrixMode(GL10.GL_PROJECTION);    //设置投影矩阵
3  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵
4  gl.glOrthof(-ratio, ratio, bottom, top, near, far); //调用此方法计算产生正交投影矩阵
5  GLU.gluLookAt (         //设置摄像机
6     gl, 
7     0,0,10,
8     0,0,0,
9     0,1,0  
10  ); 
11  gl.glMatrixMode(GL10.GL_MODELVIEW);   //设置模式矩阵
12  gl.glLoadIdentity();        //设置当前矩阵为单位矩阵 
13  helpback.drawSelf(gl, helpId[idKey]);    //绘制背景图
14  gl.glEnable(GL10.GL_BLEND);     //开启混合
15  //设置源混合因子与目标混合因子
16  gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); 
17  gl.glPushMatrix();
18  gl.glTranslatef(-1.3f, -0.7f, 0.1f);
19  button.drawSelf(gl, prepageId);     //绘制向左的箭头
20  gl.glPopMatrix();
21  gl.glPushMatrix();
22  gl.glTranslatef(1.3f, -0.7f, 0.1f);
23  button.drawSelf(gl, nextpageId);     //绘制向右的箭头
24  gl.glPopMatrix();
25  gl.glDisable(GL10.GL_BLEND);     //关闭混合
26  }

该方法的开发相当简单,其实现的功能是在正交投影下绘制帮助界面,帮助界面主要由一个大的背景图和两个图片按钮组成。
至此欢迎界面、菜单界面、选关界面和帮助界面介绍完毕,但是由于关于界面的开发与前面其他界面相比非常简单,因此,笔者不再一一赘述,需要的读者请自行查阅随书光盘中的源代码。

相关文章
|
19天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
1月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
94 0
|
2月前
|
Android开发
安卓SO层开发 -- 编译指定平台的SO文件
安卓SO层开发 -- 编译指定平台的SO文件
30 0
|
1月前
|
设计模式 人工智能 开发工具
安卓应用开发:构建未来移动体验
【2月更文挑战第17天】 随着智能手机的普及和移动互联网技术的不断进步,安卓应用开发已成为一个热门领域。本文将深入探讨安卓平台的应用开发流程、关键技术以及未来发展趋势。通过分析安卓系统的架构、开发工具和框架,本文旨在为开发者提供全面的技术指导,帮助他们构建高效、创新的移动应用,以满足不断变化的市场需求。
18 1
|
1月前
|
机器学习/深度学习 调度 Android开发
安卓应用开发:打造高效通知管理系统
【2月更文挑战第14天】 在移动操作系统中,通知管理是影响用户体验的关键因素之一。本文将探讨如何在安卓平台上构建一个高效的通知管理系统,包括服务、频道和通知的优化策略。我们将讨论最新的安卓开发工具和技术,以及如何通过这些工具提高通知的可见性和用户互动性,同时确保不会对用户造成干扰。
33 1
|
11天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
19天前
|
Android开发
Android开发小技巧:怎样在 textview 前面加上一个小图标。
Android开发小技巧:怎样在 textview 前面加上一个小图标。
10 0
|
20天前
|
Android开发
Android 开发 pickerview 自定义选择器
Android 开发 pickerview 自定义选择器
12 0
|
26天前
|
Java Android开发
Android开发系列全套课程
本系列课程面向有java基础,想进入企业从事android开发的计算机专业者。学习搭配实战案例,高效掌握岗位知识。
17 1
|
28天前
|
数据可视化 测试技术 Android开发
安卓应用开发:打造高效用户界面的五大技巧
【2月更文挑战第30天】在竞争激烈的应用市场中,一个流畅且直观的用户界面(UI)对于安卓应用的成功至关重要。本文将探讨五个关键的UI设计技巧,这些技巧旨在提升用户体验并优化性能。我们将深入分析布局优化、资源管理、动画效果、响应式设计和测试流程等方面,并提供实用的代码示例和最佳实践,帮助开发者构建既美观又高效的安卓应用。