腾讯“爱消除”游戏之菜单特效"/>
山寨腾讯“爱消除”游戏之菜单特效
前面的教程,我们一直关注游戏主要功能的设计,今天我们将介绍其他的辅助功能设计,比如游戏的主菜单设计。
如下图示,游戏主界面中的“极限模式”,其实是由一个动画图片及一个静态文字图片构成的。不管触摸到的是动画图片,或是文字图片,最后都会进入游戏的“极限模式”。
我们设计了DrawMenu来实现这个功能。除了要用到前面介绍的纹理渲染技术外,DrawMenu还绑定了TouchArea,这样当触摸事件发生时,会自动检测是否触摸到了DrawMenu所绘制的图片,如果是,这发出携带有对应触摸区域信息的触摸事件。在事件处理函数中,处理该事件让其执行特定的操作即可。
1、DrawMenu类,给类与前面介绍的渲染类很类似,唯一不同的地方就是在创建对象时,会touchRegister函数向控制中心ControlCenter类注册TouchArea。当触摸事件发生时,会检查对应的TouchArea。
public class DrawMenu {float mCol = 0;float mRow = 0;int mWidth = 0;int mHeight = 0;float mPicNum = 4.0f;E_TOUCHAREA mTouchArea = E_TOUCHAREA.NONE;private IntBuffer mVertexBuffer; //顶点坐标数据缓冲private FloatBuffer mTextureBuffer; //顶点纹理数据缓冲int vCount=0; //顶点数量 int textureId; //纹理索引float textureRatio; //为了准确获取纹理图片中的素材对象,需要设置纹理的变换率public IControl control;public DrawMenu(int textureId, float col, float row, int width, int height, int picNum, E_TOUCHAREA area){this.textureId = textureId;mCol = col - 3;mRow = row - 3;mTouchArea = area;//要渲染的图片宽和高,是实际宽高像素的一半mWidth = width/2;mHeight = height/2;mPicNum = picNum;control = new CtlMenu();CtlMenu ctl = (CtlMenu)control;ctl.init(picNum);touchRegister();}void touchRegister(){int w = mWidth;int h = mHeight;int deltaX = (int)(mCol*w);int deltaY = (int)(mRow*h);int x = -w+deltaX + CrazyZombyConstent.VIEW_WIDTH/2;int y = (CrazyZombyConstent.REAL_HEIGHT - CrazyZombyConstent.VIEW_HEIGHT) / 2 + CrazyZombyConstent.VIEW_HEIGHT/2 - (h+deltaY);TouchArea touchArea = new TouchArea(x, y, w*2, h*2, mTouchArea);ControlCenter.mTouchMsg.touchRegister(touchArea, control); }//顶点坐标数据的初始化private void initVertexBuffer(){vCount=6;//顶点的数量,一个正方形用两个三角形表示,共需要6个顶点 int w = mWidth*CrazyZombyConstent.ADP_SIZE;int h = mHeight*CrazyZombyConstent.ADP_SIZE;int deltaX = (int)(mCol*w);int deltaY = (int)(mRow*h);int vertices[]=new int[]//顶点坐标数据数组{-w+deltaX,h+deltaY,0,-w+deltaX,-h+deltaY,0,w+deltaX,-h+deltaY,0,w+deltaX,-h+deltaY,0,w+deltaX,h+deltaY,0,-w+deltaX,h+deltaY,0};//创建顶点坐标数据缓冲//int类型占用4个字节,因此转换为byte的数据时需要*4ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);vbb.order(ByteOrder.nativeOrder()); //设置本地的字节顺序//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题 mVertexBuffer = vbb.asIntBuffer(); //转换为int型缓冲mVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据mVertexBuffer.position(0); //设置缓冲区起始位置return;}//顶点纹理数据的初始化 private void initTextureBuffer(int witch){textureRatio = (float)(1/mPicNum); //图片是4个独立的素材对象组成,每次需要根据witch准确地获取对应的素材float textureCoors[]=new float[] //顶点纹理S、T坐标值数组{(witch - 1) * textureRatio,0,(witch - 1) * textureRatio,1,witch * textureRatio,1,witch * textureRatio,1,witch * textureRatio,0, (witch - 1) * textureRatio,0}; //创建顶点纹理数据缓冲//int类型占用4个字节,因此转换为byte的数据时需要*4ByteBuffer cbb = ByteBuffer.allocateDirect(textureCoors.length*4);cbb.order(ByteOrder.nativeOrder());//设置本地字节顺序//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题mTextureBuffer = cbb.asFloatBuffer();//转换为int型缓冲mTextureBuffer.put(textureCoors);//向缓冲区中放入顶点着色数据mTextureBuffer.position(0);//设置缓冲区起始位置return;}public void draw(GL10 gl){if (!control.isRun()) return;CtlMenu ctl = (CtlMenu)control;initVertexBuffer(); initTextureBuffer(ctl.getPicId()); //初始化纹理顶点数据 //顶点坐标,允许使用顶点数组gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//为画笔指定顶点坐标数据gl.glVertexPointer(3, //每个顶点的坐标数量为3 xyz GL10.GL_FIXED, //顶点坐标值的类型为 GL_FIXED0, //连续顶点坐标数据之间的间隔mVertexBuffer //顶点坐标数据);//纹理坐标,开启纹理gl.glEnable(GL10.GL_TEXTURE_2D); //允许使用纹理数组gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//为画笔指定纹理uv坐标数据gl.glTexCoordPointer(2, //每个顶点两个纹理坐标数据 S、TGL10.GL_FLOAT, //数据类型0, //连续纹理坐标数据之间的间隔mTextureBuffer //纹理坐标数据); gl.glBindTexture(GL10.GL_TEXTURE_2D,textureId);//为画笔绑定指定ID纹理 //绘制图形gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vCount);gl.glDisable(GL10.GL_TEXTURE_2D);//关闭纹理}
}
2、TouchArea的检测。当触摸事件发生,会调用到raiseTouchEvent函数,根据输入的坐标值,按注册的顺序检测TouchArea,只要检测到其中一个满足条件,就发送一个带有区域信息的消息;如果没有符合条件的TouchArea,则发送E_TOUCHARE.NONE消息。
//输入坐标是(x,y),检测是否落在某个TouchAreavoid raiseTouchEvent(int x, int y){TouchArea touchArea;IControl control;for(int i = 0; i < mTouchList.mTouchAreaList.size(); i++){touchArea = mTouchList.mTouchAreaList.get(i);control = mTouchList.mControl.get(i);if(x >= touchArea.mStartX && x <= touchArea.mStartX + touchArea.mW){if(y >= touchArea.mStartY && y <= touchArea.mStartY + touchArea.mH){if(control.isRun()){//检测到一个有效的就立即退出sendMsg(touchArea.mTouchArea);return;}}}}sendMsg(E_TOUCHAREA.NONE);return;}
3、TOUCHAREA消息的处理。在handleMessage中,会处理上面发出的消息。
//处理触摸事件static void dealTouchMsg(int event){event = event - TOUCH_EVENT_BASE;if(event == E_TOUCHAREA.MENU_TIME_MODE.ordinal()){mMode = E_GAMEMODE.TIME;mScore.init();mLife.set(CrazyZombyConstent.LIFE_NUM);mLife.reset();mTimer.reset();init();Message msg = new Message();msg.what = ControlCenter.LOADING_START;ControlCenter.mHandler.sendMessage(msg);stopMenuScene();}else if(event == E_TOUCHAREA.MENU_LIFE_MODE.ordinal()){mMode = E_GAMEMODE.LIFE;mScore.init();mLife.set(CrazyZombyConstent.LIFE_NUM);mLife.reset();mTimer.reset();init();Message msg = new Message();msg.what = ControlCenter.LOADING_START;ControlCenter.mHandler.sendMessage(msg);stopMenuScene();}else if(event == E_TOUCHAREA.MENU_ABOUT.ordinal()){}else if(event == E_TOUCHAREA.MENU_EXIT.ordinal()){}else if(event == E_TOUCHAREA.RESULT_CONTINUE.ordinal()){ControlCenter.mScene = E_SCENARIO.MENU;stopResultScene();startMenuScene();}else if(event == E_TOUCHAREA.RESULT2.ordinal()){}}
这样,我们就完成了一个可以处理触摸事件的渲染类的设计。
顺便提一下,DrawMenu渲染类不光可以渲染静态的效果,也可以渲染动态的效果。
public DrawMenu(int textureId, float col, float row, int width, int height, int picNum, E_TOUCHAREA area)
col,row:图片显示的位置
width:要渲染的图片像素宽度
height:要渲染的图片像素高度
picNum:要渲染的素材由几张图片组成
area:自动根据图片尺寸及显示位置计算出TouchArea
通过设计合理的素材,并设定picNum,就可以实现静态或动态的渲染效果。
如上面这一幅素材,通过下面的代码就可以渲染出动画效果:
drawMenuTimeModePic = new DrawMenu(timeModePicTextureId, 1, 3, 128, 128, 4, E_TOUCHAREA.MENU_TIME_MODE);
【后记】上述代码在我的另一个开源项目“消除僵尸”中可以免费下载:
更多推荐
山寨腾讯“爱消除”游戏之菜单特效
发布评论