本文共 43455 字,大约阅读时间需要 144 分钟。
本节书摘来自异步社区《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,则直接return25 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); //绘制当前的Logo12 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-10;i=i-10) { //动态更改图片的透明度值并不断重绘10 currentAlpha=i;11 if(currentAlpha<0) { //当前透明值小于零时12 currentAlpha=0; //设置当前透明值为013 }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 @Override39 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 @Override2 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); //设置屏幕背景色黑色RGBA7 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为true11 Constant.boxFlag=false; //置boxFlag为false12 Constant.xOffset=-9f; //初始化箱子的位置13 Constant.robotXstar=-15.5f; //初始化机器人的位置14 Constant.robotYstar=0;15 Constant.robotZstar=-8.5f; 16 armTexId=initTexture(gl, PicDataManager.picDataArray[4]);//机器人其他部分纹理id17 headTexId=initTexture(gl, PicDataManager.picDataArray[3]); //机器人头纹理id18 lovntArray[0]=new VertexTextureNormal3DObjectForDraw( //身体19 VertexDataManager.vertexPositionArray[17], //顶点位置数据20 VertexDataManager.vertexTextrueArray[17], //顶点纹理数据21 VertexDataManager.vertexNormalArray[17], //顶点法向量数据22 VertexDataManager.vCount[17], //顶点数23 armTexId //纹理id24 );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为030 MenuDoActionThread.currStep=0; //置currStep为031 dat.start(); //启动线程32 boxId=initTexture(gl,PicDataManager.picDataArray[11]); //初始化箱子纹理id33 ……//此处省略了类似的初始化纹理的代码,需要的读者可自行查阅本书随书光盘中源代码34 helpl=initTexture(gl, PicDataManager.picDataArray[49]); //退出纹理id35 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则加147 if(anglet>=25) { 48 flagn=false; //置flagn为false49 }50 }else{ //若anglet大于25则减151 anglet-=1f;52 if(anglet<=-25) { //置flagn为true53 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 ArrayListchilds=new ArrayList (); //子骨头列表 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 161 };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)% //更新currActionIndex23 ActionGenerator.acrobortArray.length;24 currAction=ActionGenerator.acrobortArray[currActionIndex]; //更新currAction25 currStep=0; //更新currStep26 }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++; //当前步骤加167 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; //头部纹理ID25 ……//此处省略了类似的声明纹理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 @Override2 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); //设置屏幕背景色黑色RGBA7 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]); //其他部分纹理id13 headTexId=initTexture(gl, PicDataManager.picDataArray[3]); //机器人头纹理id14 ……//此处省略了与菜单界面类似的初始化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为false38 Constant.set_flag=false; //置set_flag为false39 }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]; //关卡按钮纹理id37 int bgButtonBack; //返回按钮纹理id38 int bgButtonBackl;39 int suoId;40 int headTexId; //头部纹理id41 int armTexId; //其他部位纹理id42 ……//此处省略了绘制方法,将在下面进行介绍43 @Override44 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) { //初始化纹理id55 bgButtonId[0]=initTexture(gl, PicDataManager.picDataArray[21]); //关卡1按钮纹理id56 ……//该处省略了类似的初始化纹理id的代码,需要的读者请自行查57 //阅随书光盘中的源代码58 }59 ……//该处省略了本类中初始化纹理的方法,需要的读者请自行查阅随书光盘中的源代码60 }}
第4-15行是声明或初始化开发过程中用到的各种引用。第16-24行是该类的构造方法,其在其他类创建该类对象时被调用。
第27-41行是声明3D界面用到的物体对象引用和开发过程中用到的纹理id的引用。第43-52行是重写方法onSurfaceChanged,在该方法中设置了视口大小并计算了宽高比。第54-58行是初始化纹理id的方法。(2)接下来为读者介绍的是绘制方法onDrawFrame的开发,其代码如下。1 @Override2 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
第3-6行是清除深度缓存颜色缓存并设置当前矩阵为模式矩阵和单位矩阵。第7-9行是设置透视投影矩阵,并设置当前矩阵为模式矩阵和单位矩阵。第10-15行是设置摄像机。
第16-18行是将绘制数据复制进临时数据。第19-27行是绘制3D场景中的物体。第28-30行设置当前矩阵为模式矩阵和单位矩阵并且设置为正交投影。第31-35行是设置摄像机。第38-40行是设置为允许混合,并设置源混合因子和目标混合因子。第41-59行是绘制选关界面中的关卡及返回按钮。(3)下面为读者介绍的是方法onSurfaceCreated的开发,该方法在画布创建时被调用,其代码如下。1 @Override2 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); //设置屏幕背景色黑色RGBA7 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 @Override2 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.ratio13 &&y<=(Constant.Select_1_d+Constant.screenScaleResult.lucY)*14 Constant.screenScaleResult.ratio) {//按下第一关15 com.bn.txz.game.GameData.level=1; //置关卡数为116 Constant.SELECT_IS_WHILE=false; //置SELECT_IS_WHILE为false17 Constant.select_flag=false; //置select_flag为false18 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为false30 Constant.select_flag=false; //置select_flag为false31 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 @Override32 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 @Override43 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 @Override2 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.ratio9 &&x>(Constant.Help_pre_l+Constant.screenScaleResult.lucX)*10 Constant.screenScaleResult.ratio11 &&y<(Constant.Help_pre_b+Constant.screenScaleResult.lucY)*12 Constant.screenScaleResult.ratio13 &&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减120 }}21 if(x<(Constant.Help_next_r+Constant.screenScaleResult.lucX)*22 Constant.screenScaleResult.ratio23 &&x>(Constant.Help_next_l+Constant.screenScaleResult.lucX)*24 Constant.screenScaleResult.ratio25 &&y<(Constant.Help_next_b+Constant.screenScaleResult.lucY)*26 Constant.screenScaleResult.ratio27 &&y>(Constant.Help_next_t+Constant.screenScaleResult.lucY)*28 Constant.screenScaleResult.ratio) { //单击右边按钮29 if(idKey==9) { //如果是最后一页30 activity.gotoMenuView(); //返回菜单界面31 idKey=0; //置idKey为032 }else{33 idKey=idKey+1; // idKey为idKey加134 }}35 break;36 }37 return true;38 }
该方法的开发非常简单,其实现的功能是:根据获取的触控位置判断单击的是否是按钮的位置,单击的若是按钮的位置则根据单击按钮的不同来更改相应的idKey值,若是idKey等于0时按下上一张;或idKey等于9时按下下一张,则界面跳转到菜单界面。
(3)接下来介绍监听触控的方法绘制界面的方法onSurfaceCreated,其代码如下。1 @Override2 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); //设置屏幕背景色黑色RGBA7 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]);//背景纹理id11 processBeijing=initTexture(gl, PicDataManager.picDataArray[13]); //进度条背景纹理id12 tex_processId=initTexture(gl, PicDataManager.picDataArray[14]);//进度条纹理id13 loadId=initTexture(gl, PicDataManager.picDataArray[15]); //背景纹理id14 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 }
该方法的开发相当简单,其实现的功能是在正交投影下绘制帮助界面,帮助界面主要由一个大的背景图和两个图片按钮组成。
至此欢迎界面、菜单界面、选关界面和帮助界面介绍完毕,但是由于关于界面的开发与前面其他界面相比非常简单,因此,笔者不再一一赘述,需要的读者请自行查阅随书光盘中的源代码。转载地址:http://ylncx.baihongyu.com/