在山寨腾讯“爱消除”游戏项目中,我们使用了两种简单的屏幕拾取技术,这在第五天的教程及山寨腾讯“爱消除”游戏之菜单特效里已经有介绍。
很显然,对于一些有复杂不规则图案,或有层次感的场面,上面的技术并不能满足要求。今天我们将介绍另外一种屏幕拾取的方法。
如下图所示,这个是《教你玩魔方》游戏里的一个场景。
显然,由于以下的原因,用前面介绍的两种技术将很难完成准确的屏幕拾取。
1、中间的魔方体是可以随意缩放的;
2、魔方的每一个面都是可以转动的;
3、每个面在转动过程中,位置都是随时变化的;
4、转动结束后,面上每一个方块都会发生变化;
5、每个方块都有6个面,有些面是可见的,有些面是不可见的,触摸到不同的面,产生的动作效果应该是不一样的。
好,下面详细地看一下DisplaySingleCube函数,该函数分别将方块的6个面渲染出来。
1、DisplaySingleCube用于显示一个小方块,一个完整的魔方需要调用27次这个函数,最后一个参数cubeIndex指定了方块的索引(0~26)
2、DisplaySingleCube函数中的渲染操作分为DRAW和SELECT_PLATE两种模式,在屏幕拾取时,要将模式设置为SELECT_PLATE
3、DRAW模式下,使用纹理渲染;SELECT_PLATE模式下,使用色彩渲染,每个面分别渲染出不同的颜色,详细看下面代码的注释;根据选定的颜色,就可以知道触摸到的是哪一个面了。
//显示单个方块 private void DisplaySingleCube(ST_DreamCube dreamcube, float fX, float fY,float fZ,float fRX,float fRY,float fRZ, int cubeIndex) { E_WORKMODE eMode = GetGLMode(); E_COLOR cU, cD, cF, cB, cL, cR; cU = dreamcube.cube_status[cubeIndex].Color.U; cD = dreamcube.cube_status[cubeIndex].Color.D; cF = dreamcube.cube_status[cubeIndex].Color.F; cB = dreamcube.cube_status[cubeIndex].Color.B; cL = dreamcube.cube_status[cubeIndex].Color.L; cR = dreamcube.cube_status[cubeIndex].Color.R; BindVertexAndTexture(cubeBuff, cubetexBuff); mGl.glLoadIdentity(); //可不要 GLU.gluLookAt(mGl, -5.0f, 5.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); mGl.glScalef(mCfg.cubeConfig.fZoomFactor, mCfg.cubeConfig.fZoomFactor, mCfg.cubeConfig.fZoomFactor); mGl.glTranslatef((float)0.3, (float)0.3-1, (float)-0.3); mGl.glFrontFace(GL10.GL_CCW); if (mCube.fYDegree != 0) { mGl.glRotatef(mCube.fYDegree, 0.0f, 1.0f, 0.0f); } if (fRX != 0 || fRY != 0 || fRZ != 0) { mGl.glRotatef(fRotate, fRX, fRY, fRZ); } mGl.glTranslatef(fX, fY, fZ); // FRONT AND BACK if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cF.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(0, 0, 1, 1); //RGB(0,0,1) 蓝 } mGl.glNormal3f(0.0f, 0.0f, 1.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cB.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(0, 1, 0, 1); //RGB(0,1,0) 绿 } mGl.glNormal3f(0.0f, 0.0f, -1.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4); // LEFT AND RIGHT if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cL.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(0, 1, 1, 1); //RGB(0,0,1) 蓝+绿 } mGl.glNormal3f(-1.0f, 0.0f, 0.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4); if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cR.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(1, 0, 0, 1); //RGB(0,0,1) 红 } mGl.glNormal3f(1.0f, 0.0f, 0.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4); // TOP AND BOTTOM if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cU.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(1, 0, 1, 1); //RGB(0,0,1) 红+蓝 } mGl.glNormal3f(0.0f, 1.0f, 0.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4); if (eMode == E_WORKMODE.DRAW) { mGl.glBindTexture(GL10.GL_TEXTURE_2D, texture[cD.ordinal()]); } else if (eMode == E_WORKMODE.SELECT_PLATE) { mGl.glColor4f(1, 1, 0, 1); //RGB(0,0,1) 红+绿 } mGl.glNormal3f(0.0f, -1.0f, 0.0f); mGl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4); }
接下来介绍选择物体的函数SelectObj,参数是手指触碰到屏幕的坐标值。
1、在进入SelectObj函数之前,需要将模式设置为PICK,这样DisplaySingleCube函数内部的DRAW及SELECT_PLATE将不起作用;
2、在绘制每一个小方块之前,调用glColor4f给这个方块设置颜色,每个方块都会设置成不同的颜色,详细看代码注释;
3、我们用(0,0,1)~(1,1,1)来表示不同的颜色,最多只能7种颜色,那么如何用7种颜色来识别27个方块呢?我们采用分组的方法来做到这一点的,详细见代码;
//对象选择操作 E_SELECTION SelectObj(int x, int y) { E_SELECTION eObj = E_SELECTION.SELE_INVALID; int iObj = 0; InitViewPortMain(); mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); { mCube.XChgDreamCube4Display(mCube.DREAM_CUBE_CACULATE); mGl.glColor4f(0, 0, 1, 1); // RGB(0,0,1) 蓝色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 24); mGl.glColor4f(0, 1, 0, 1); // RGB(0,1,0) 绿色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 25); mGl.glColor4f(0, 1, 1, 1); // RGB(0,1,1) 绿色+蓝色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 26); mGl.glColor4f(1, 0, 0, 1); // RGB(1,0,0) 红色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 15); mGl.glColor4f(1, 0, 1, 1); // RGB(1,0,1) 红色+蓝色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 16); mGl.glColor4f(1, 1, 0, 1); // RGB(1,1,0) 红色+绿色 DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 17); iObj = GetObj(x, y); //分组1 if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_BLK24; break; case 2: eObj = E_SELECTION.SELE_BLK25; break; case 3: eObj = E_SELECTION.SELE_BLK26; break; case 4: eObj = E_SELECTION.SELE_BLK15; break; case 5: eObj = E_SELECTION.SELE_BLK16; break; case 6: eObj = E_SELECTION.SELE_BLK17; break; // default: // eObj = -1; // break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); mGl.glColor4f(0, 0, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 6); mGl.glColor4f(0, 1, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 7); mGl.glColor4f(0, 1, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 8); mGl.glColor4f(1, 0, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 23); mGl.glColor4f(1, 0, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 14); mGl.glColor4f(1, 1, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5); iObj = GetObj(x, y); //分组2 if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_BLK6; break; case 2: eObj = E_SELECTION.SELE_BLK7; break; case 3: eObj = E_SELECTION.SELE_BLK8; break; case 4: eObj = E_SELECTION.SELE_BLK23; break; case 5: eObj = E_SELECTION.SELE_BLK14; break; case 6: eObj = E_SELECTION.SELE_BLK5; break; // default: // eObj = -1; // break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); mGl.glColor4f(0, 0, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 2); mGl.glColor4f(0, 1, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 11); mGl.glColor4f(0, 1, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 18); mGl.glColor4f(1, 0, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 19); mGl.glColor4f(1, 0, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 20); mGl.glColor4f(1, 1, 0, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 21); mGl.glColor4f(1, 1, 1, 1); DisplaySingleCube(mCube.DREAM_CUBE_DISPLAY, 0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 22); iObj = GetObj(x, y); //分组3 if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_BLK2; break; case 2: eObj = E_SELECTION.SELE_BLK11; break; case 3: eObj = E_SELECTION.SELE_BLK18; break; case 4: eObj = E_SELECTION.SELE_BLK19; break; case 5: eObj = E_SELECTION.SELE_BLK20; break; case 6: eObj = E_SELECTION.SELE_BLK21; break; case 7: eObj = E_SELECTION.SELE_BLK22; break; // default: // eObj = -1; // break; } return eObj; } } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowUnseenPlate(mCube.DREAM_CUBE_CACULATE); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_LEFT; break; case 2: eObj = E_SELECTION.SELE_RIGHT; break; case 3: eObj = E_SELECTION.SELE_DOWN; break; // default: // eObj = E_Selection.SELE_INVALID; // break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowLButton(); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_MENU; break; case 2: eObj = E_SELECTION.SELE_HELP; break; case 3: eObj = E_SELECTION.SELE_ZOOMOUT; break; case 4: eObj = E_SELECTION.SELE_DISRUPT; break; case 5: eObj = E_SELECTION.SELE_AUTOPLAY; break; case 6: eObj = E_SELECTION.SELE_RESTART; break; case 7: eObj = E_SELECTION.SELE_STUDY; break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowRButton(); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_ZOOMIN; break; case 2: eObj = E_SELECTION.SELE_SETUP; break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowProgressStars(); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_STAR; break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowCubeTip(); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.TIP_0; break; case 2: eObj = E_SELECTION.TIP_1; break; case 3: eObj = E_SELECTION.TIP_2; break; case 4: eObj = E_SELECTION.TIP_3; break; case 5: eObj = E_SELECTION.TIP_4; break; case 6: eObj = E_SELECTION.TIP_5; break; case 7: eObj = E_SELECTION.TIP_6; break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowTime(miCubeClockSec); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_TIMER; break; } return eObj; } mGl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); ShowStep(mCube.GetStepBar()); iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_STEP; break; } return eObj; } return eObj; }
下面介绍获取对象函数GetObj:
1、根据传入的坐标值,获取指定坐标出的色彩信息;
2、将色彩信息转换为RGB格式,然后返回该信息。
//获取对象 private int GetObj(int x, int y) { mGl.glFinish(); mGl.glFinish(); Delay(miSelectDelay); ByteBuffer pixel = ByteBuffer.allocate(4); byte data[] = new byte[4]; mGl.glReadPixels(x , iScreenHeight - y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixel); data = pixel.array(); data[0] = (byte) Math.abs(data[0]); data[1] = (byte) Math.abs(data[1]); data[2] = (byte) Math.abs(data[2]); data[0] = (byte) (data[0] << 2); data[1] = (byte) (data[1] << 1); int iObj = data[0] + data[1] + data[2]; return iObj; }
调用GetObj得到RGB格式的颜色信息,根据该信息,我们可以确定手指触摸到的是哪个方块了:
iObj = GetObj(x, y); if(iObj != 0) { switch(iObj) { case 1: eObj = E_SELECTION.SELE_BLK24; break; case 2: eObj = E_SELECTION.SELE_BLK25; break; case 3: eObj = E_SELECTION.SELE_BLK26; break; case 4: eObj = E_SELECTION.SELE_BLK15; break; case 5: eObj = E_SELECTION.SELE_BLK16; break; case 6: eObj = E_SELECTION.SELE_BLK17; break; // default: // eObj = -1; // break; } return eObj; }
当触摸事件发生时,我们做了以下事情:
1、将模式设置为PICK
2、用不同颜色来渲染魔方体的各个方块,并拾取到手指触摸处的方块对象
3、将模式设置为DRAW模式
4、按正常的纹理渲染模式,将魔方体完整地渲染出来
case TOUCH: mCubeGL.SetGLMode(E_WORKMODE.PICK); GetTouchObj(); mCubeGL.SetGLMode(E_WORKMODE.DRAW); mCubeGL.DisplayCube(); break;
采用这种方法,只有模式为DRAW的图案会最终显示出来,PICK或SELECT_PLATE模式下的图案是不会显示的,因此,你不需要担心PICK模式下会破坏整体的渲染效果。
好了,完整的代码请参考我的另一个开源项目《教你玩魔方》
项目地址在GITHUB,欢迎你FORK.
https://github.com/3125788/CrazyCube3D