代码清单13-5:onDrawFrame函数
- public void onDrawFrame(GL10 gl) {
- if (editMode) {
- // 编辑模式则渲染为红色背景
- gl.glClearColor(0.5f, 0, 0f, 1.0f);
- } else {
- // 游戏模式则渲染为蓝色
- gl.glClearColor(0f, 0, 0.5f, 1.0f);
- }
- //清理颜色缓冲区和深度缓冲区
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
- //设置材质的混合模式
- gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
- GL10.GL_REPLACE);
- //绘制游戏模式下的当前选择的模型
- if (!editMode) {
- drawActiveBody(gl);
- }
- //绘制编辑模式时的矩形条
- if (editMode) {
- float midX = (startX + endX) / 2f;
- float midY = (startY + endY) / 2f;
- float sizeX = (endX - midX);
- float sizeY = (endY - midY);
- float rotate = (float) Math.atan((double) (sizeY / sizeX));
- float size = (float) Math
- .sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));
- mBox.draw(gl, midX, midY, 0f, rotate * 57.2957795f, size, .2f);
- }
- Vec2 vec;
- //得到世界场景中的实体列表
- Body mBody = mWorld.getBodyList();
- do {
- //取得该实体的形状列表
- Shape mShape = mBody.getShapeList();
- if (mShape != null) {
- vec = mBody.getPosition();
- float rot = mBody.getAngle() * 57f; //弧度转化为角度
- if (ShapeType.POLYGON_SHAPE == mShape.getType()) {
- Vec2[] vertexes = ((PolygonShape) mShape).getVertices();
- mBox.draw(gl, vec.x, vec.y, 0f, rot, vertexes[2].x,
- vertexes[2].y);
- } else if (ShapeType.CIRCLE_SHAPE == mShape.getType()) {
- float radius = ((CircleShape) mShape).m_radius;
- mCircle.draw(gl, vec.x, vec.y, 0f, rot, radius);
- }
- }
- //取得下一个实体
- mBody = mBody.getNext();
- } while (mBody != null);
- //更新场景
- mWorld.update();
- }
首先根据当前是否处于编辑状态选择清理屏幕的颜色,编辑状态为红色,非编辑状态为蓝色,然后通过glClear清理颜色缓存和深度缓存,通过glTexEnvx设置材质的混合方式,如果在非编辑模式下就调用drawActiveBody函数来绘制右上角的标志,如果在编辑模式下,则需要通过触摸的位置(startX, endX, startY, endY),来计算正在编辑的物体的位置,大小等,然后通过绘制一个box展示出来。
下面的部分则和我们的物理引擎相关了,首先通过getBodyList得到当前世界中的物体列表,然后通过一个循环来渲染这每个物体,但是在渲染这些物体时,我们需要渲染物体中的每个多边形来组成这个物体,这就可以使用getShapeList来取得该物体的形状列表,然后根据形状的类型,来确定需要绘制什么对象的物体。形状的类型主要分为多边形(POLYGON_SHAPE)和圆形(CIRCLE_SHAPE),绘制的时候需要注意,由于形状中的角度是弧度表示的,所以需要转化成角度。每个物体处理完成之后可以通过getNext来得到列表中的下一个物体。最后通过调用世界的update函数来更新整个物理世界。
现在已经可以运行了,能看到效果了,但是编辑模式无效,下面我们将来实现编辑模式。首先在代码中加入代码清单13-6所示的代码。
代码清单13-6:GLRenderer.java片段
- //在编辑模式时添加一条线
- public void addLine() {
- float midX = (startX + endX) / 2f;
- float midY = (startY + endY) / 2f;
- float sizeX = (endX - midX);
- float sizeY = (endY - midY);
- float rotate = (float) Math.atan((double) (sizeY / sizeX));
- float size = (float) Math
- .sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));
- //添加一个长的box
- mWorld.addBox(midX, midY, size, .2f, rotate, false);
- startX = 0;
- startY = 0;
- endX = 0;
- endY = 0;
- }
- public void touchEvent(float x, float y, int eventCode) {
- //计算x,y对应的场景坐标
- float worldX = ((x - (this.width / 2)) * 12f) / (this.width / 2);
- float worldY = ((y - (this.height / 2)) * -20f) / (this.height / 2);
- if (!editMode) {
- //当鼠标松开时,添加一个指定的实体
- if (eventCode == MotionEvent.ACTION_UP) {
- switch (activeModel) {
- case 0:
- mWorld.addBall(worldX, worldY, 0.98f, true);
- break;
- case 1:
- mWorld.addBox(worldX, worldY, .98f, .98f, 0f, true);
- break;
- case 2:
- mWorld.addBox(worldX, worldY, .2f, 2f, 0f, true);
- break;
- }
- }
- } else {
- //确定要添加的线的坐标
- if (eventCode == MotionEvent.ACTION_DOWN) {
- startX = worldX;
- startY = worldY;
- endX = worldX;
- endY = worldY;
- } else if (eventCode == MotionEvent.ACTION_MOVE) {
- endX = worldX;
- endY = worldY;
- } else if (eventCode == MotionEvent.ACTION_UP) {
- endX = worldX;
- endY = worldY;
- addLine();
- }
- }
- }
对于编辑模式的实现,主要集中在事件处理过程中,首先进入touchEvent函数之后,将当前触摸的位置坐标转换成场景中的坐标(worldX,worldY),然后判断当前是否处于编辑模式,如果没有在编辑模式,那么点击屏幕后就是放置一个物体到场景做,因此我们直接根据当前的选择的模型activeModel,来添加一个对应的物体即可。如果在编辑状态则,则当鼠标按下时确定开始结束坐标,移动过程中确定结束坐标,弹起事件时再次确定结束坐标,这是还将调用addLine函数来添加一条线,我们可以看到addLine函数的实现其实际也就是添加一个长条形的box而已。添加完成之后将开始和结束的坐标位置都设置为0即可。好了运行一下可以看到上一篇文章眼的效果了,这里在截图一张,如图13-1所示。
图13-1 物理引擎小游戏Ophone版
总结
本文本文主要对上一篇文中所演示的一个物理引擎小游戏进行的完善,功能都按照前一篇文章的描述实现了,代码页几乎都展示出来了,所以使用Jbox2d来实现这样物理系统将是非常简单的事情,大家可以考虑一下如何使用NDK来调用C++版本的物理引擎Box2d或者chipmunk,有机会我们将继续一起来学习。由于分享经验的心情急切,难免会出现一些疏忽或错误,还望不吝赐教!