Android 3D游戏开发教程
2010-08-16 08:21 $等待$ 阅读(5784) 评论(7) 编辑 收藏 举报
第一部分首先介绍OpenGL相关的术语,并引导你开始3D开发的第一步。
这个关于3D游戏的系列的叫做 Vortex .
这个教程主要focus在3D编程上,其他的东西比如菜单和程序生命周期虽然是代码的一部分,但是在这里不会被提到。
首先开始介绍OpenGL的术语。
顶点Vertex
顶点是3D空间中的一个点,也是许多对象的基础元素。在OpenGL中你可以生命少至二维坐标(X,Y),多至四维(X,Y,Z,W). w轴是可选的,默认的值是1.0. Z轴也是可选的,默认为0. 在这个系列中,我们将要用到3个主要的坐标X,Y,Z,因为W一般都是被用来作为占位符。vertex的复数是vertices(这对非英语母语的人来说比较重要,因为这容易产生歧义)。所有的对象都是用vertices作为它们的点,因为点就是vertex。
三角形Triangle
三角形需要三个点才能创建。因此在OpenGL中,我们使用3个顶点来创建一个三角形。
多边形Polygon
多边形是至少有3个连接着的点组成的一个对象。三角形也是一个多边形。
图元Primitives
一个Primitive是一个三维的对象,使用三角形或者多边形创建。形象的说,一个有50000个顶点的非常精细的模型是一个Primitive,同样一个只有500个顶点的低模也叫做一个Primitive。
现在我们可以开始变成了。
创建一个工程交Vortex,activity也是这个名字。我们的工程应该大概是这个样子的:
1 package com.droidnova.android.games.vortex;
2
3 import android.content.Context;
4 import android.opengl.GLSurfaceView;
5
6 public class VortexView extends GLSurfaceView {
7 private static final String LOG_TAG = VortexView.class.getSimpleName();
8 private VortexRenderer _renderer;
9
10 public VortexView(Context context) {
11 super(context);
12 _renderer = new VortexRenderer();
13 setRenderer(_renderer);
14 }
15 }
16
如上所示,我们继承了GLSurfaceView是因为它会帮助我们画3D图像。接下来看VortexRenderer类。一个Renderer包含画一帧所必需的所有东西。 引用自这儿references 。Renderer负责OpenGL call来render一个帧。
来看一下这个类:
1 package com.droidnova.android.games.vortex;
2
3 import javax.microedition.khronos.egl.EGLConfig;
4 import javax.microedition.khronos.opengles.GL10;
5
6 import android.opengl.GLSurfaceView;
7
8 public class VortexRenderer implements GLSurfaceView.Renderer {
9 private static final String LOG_TAG = VortexRenderer.class.getSimpleName();
10
11 private float _red = 0.9f;
12 private float _green = 0.2f;
13 private float _blue = 0.2f;
14
15 @Override
16 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
17 // Do nothing special.
18 }
19
20 @Override
21 public void onSurfaceChanged(GL10 gl, int w, int h) {
22 gl.glViewport(0, 0, w, h);
23 }
24
25 @Override
26 public void onDrawFrame(GL10 gl) {
27 // define the color we want to be displayed as the "clipping wall"
28 gl.glClearColor(_red, _green, _blue, 1.0f);
29 // clear the color buffer to show the ClearColor we called above...
30 gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
31 }
32 }
33
好,我们做了什么?
首先我们实现了GLSurfaceView.Renderer这个接口,主要是实现3个方法:onSurfaceCreated(), onSurfaceChanged() 和 onDrawFrame()。这些方法很容易理解,第一个在surface创建以后调用,第二个是在surface发生改变以后调用,例如从竖屏切换到横屏的时候,最后一个方法是当任何时候调用一个画图方法的时候。
从11行到13行,我们用浮点数来定义RGB颜色系统中的每一个颜色。
在28行,我们通过glClearColor()方法为底色定义了颜色。底色是在我们能看到的所有东西的后面,所以所有在底色后面的东西都是不可见的。可以想象这种东西为浓雾,挡住了所有的东西。然后我们将要为之设置距离来show一下它怎么用的。那时候你就一定会明白它是怎么存在的了。
为了让颜色变化可见,我们必须调用glClear()以及颜色缓冲的Mask来清空buffer,然后为我们的底色使用新的底色。
为了能看到它在起作用,我们这里为MotionEvent创建一个response,使用它来改变颜色。首先在VortexRenderer中来创建一个设置颜色的函数。
1 public void setColor(float r, float g, float b) {
2 _red = r;
3 _green = g;
4 _blue = b;
5 }
6
下面是VortexView类中创建的方法来处理MotionEvent。
1 public boolean onTouchEvent(final MotionEvent event) {
2 queueEvent(new Runnable() {
3 public void run() {
4 _renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f);
5 }
6 });
7 return true;
8 }
9
我们创建了一个匿名的Runnable对象,这里的run()方法调用renderer中的setColor方法。这有会根据MotionEvent坐标做一些小的计算。
现在我们已经有了一个小小的程序来使用OpenGl来改变我们的背景色了。
在德语中我们叫这种小case为“Mit Kanonen auf Spatzen schießen”,翻译过来应该是“你在车轮上打死了一只苍蝇”。这说的恰到好处,这只是一个最最最小的例子,要学习OpenGL,你现在要准备更多更多的东西。
这部分最后提一下OpenGL的文档documentation for OpenGL 。这个东西虽然可用想不高,但是它最少是一个文档。
Eclipse工程源代码在这里下载(原地址): Vortex Part I
这个系列的第二部分是关于如何添加一个三角形并可以旋转它。
第一件事情是初始化需要显示的三角形。我们来在VortexRenderer类中添加一个方法
1 initTriangle()。
2 // new object variables we need
3 // a raw buffer to hold indices
4 private ShortBuffer _indexBuffer;
5
6 // a raw buffer to hold the vertices
7 private FloatBuffer _vertexBuffer;
8
9 private short[] _indicesArray = {0, 1, 2};
10 private int _nrOfVertices = 3;
11
12 // code snipped
13
14 private void initTriangle() {
15 // float has 4 bytes
16 ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4);
17 vbb.order(ByteOrder.nativeOrder());
18 _vertexBuffer = vbb.asFloatBuffer();
19
20 // short has 2 bytes
21 ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2);
22 ibb.order(ByteOrder.nativeOrder());
23 _indexBuffer = ibb.asShortBuffer();
24
25 float[] coords = {
26 -0.5f, -0.5f, 0f, // (x1, y1, z1)
27 0.5f, -0.5f, 0f, // (x2, y2, z2)
28 0f, 0.5f, 0f // (x3, y3, z3)
29 };
30
31 _vertexBuffer.put(coords);
32 _indexBuffer.put(_indicesArray);
33
34 _vertexBuffer.position(0);
35 _indexBuffer.position(0);
36 }
37
让我们从新的对象变量开始. _vertexBuffer为我们的三角形保存坐标._indexBuffer保存索引. _nrOfVertices变量定义需要多少个顶点.对于一个三角形来说,一共需要三个顶点. 这个方法首先为这里两个buffer分配必须的内存(14-22行). 接下来我们定义一些坐标(24-28行) 后面的注释对用途给予了说明.
在30行,我们将coords数组填充给_vertexBuffer . 同样在31行将indices数组填充给_indexBuffer 。最后将两个buffer都设置position为0.
为了防止每次都对三角形进行初始化,我们仅仅在onDrawFrame()之前的行数调用它一次。一个比较好的选择就是在onSurfaceCreated()函数中.
1 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
2 // preparation
3 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
4 initTriangle();
5 }
6
glEnableClientState() 设置OpenGL使用vertex数组来画。这是很重要的,因为如果不这么设置OpenGL不知道如何处理我们的数据。接下来我们就要初始化我们的三角形。为什么我们不需使用不同的buffer? 在新的onDrawFrame()方法中我们必须添加一些新的OpenGL调用。
1 public void onDrawFrame(GL10 gl) {
2 // define the color we want to be displayed as the "clipping wall"
3 gl.glClearColor(_red, _green, _blue, 1.0f);
4
5 // clear the color buffer to show the ClearColor we called above...
6 gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
7
8 // set the color of our element
9 gl.glColor4f(0.5f, 0f, 0f, 0.5f);
10
11 // define the vertices we want to draw
12 gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer);
13
14 // finally draw the vertices
15 gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer);
16 }
17
好,一步一步地看。
glClearColor() 和 glClear() 在教程I部分已经提到过。在第10行使用glColor4f(red, green, blue, alpha)设置三角形为暗红色 .
在第13行,我们使用glVertexPointer()初始化Vertex Pointer. 第一个参数是大小,也是顶点的维数。我们使用的是x,y,z三维坐标。第二个参数,GL_FLOAT定义buffer中使用的数据类型。第三个变量是0,是因为我们的坐标是在数组中紧凑的排列的,没有使用offset。最后哦胡第四个参数顶点缓冲。
最后,glDrawElements()将所有这些元素画出来。第一个参数定义了什么样的图元将被画出来。第二个参数定义有多少个元素,第三个是indices使用的数据类型。最后一个是绘制顶点使用的索引缓冲。
当最后测试这个应用的使用,你会看到一个在屏幕中间静止的三角形。当你点击屏幕的时候,屏幕的背景颜色还是会改变。
现在往里面添加对三角形的旋转。下面的代码是写在VortexRenderer类中的.
1 private float _angle;
2
3 public void setAngle(float angle) {
4 _angle = angle;
5 }
6
glRotatef()方法在glColor4f()之前被onDrawFrame()调用.
1 public void onDrawFrame(GL10 gl) {
2 // set rotation
3 gl.glRotatef(_angle, 0f, 1f, 0f);
4
5 gl.glColor4f(0.5f, 0f, 0f, 0.5f);
6 // code snipped
7 }
8
这时候我们可以绕y轴旋转。如果需要改变只需要改变glRotate()方法中的0f。这个参数中的值表示一个向量,标志三角形绕着旋转的坐标轴。
要让它可用,我们必须在VortexView中的onTouchEvent()中添加一个调用。
1 public boolean onTouchEvent(final MotionEvent event) {
2 queueEvent(new Runnable() {
3 public void run() {
4 _renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f);
5 _renderer.setAngle(event.getX() / 10);
6 }
7 });
8 return true;
9 }
10
上面代码中除以10是为了减小角度变换的速度。
现在编译运行这个程序。如果你在屏幕的最左边点击,你会看到三角形轻微旋转。如果你将手指移到右边,旋转的速度就会变得很快。
Eclipse工程的源代码在这里下载(原链接): Vortex Part II