android 游戏导引(2. 游戏的基础设置)

上一节已经学习了一个基本的 OpenGL 框架了,今天这一节就进一步设置一下 2D 游戏相关的东西了。对 2D 游戏的喜爱甚于 3D。 相信大多数人也是吧。

1 游戏全屏显示

2行代码搞定,一个让应用去掉自己的标题栏,一个设置全屏,放在应用的创建函数 OnCreate 中:

 

public class GlGame extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,
                      WindowManager.LayoutParams. FLAG_FULLSCREEN);
        
        // ... 其他略
    }
}

 

2 设置 OpenGL 的 2D 环境

我们先要设计好要建立的世界定位方式,就是坐标系,一般的 opengl 程序是标准的笛卡尔坐标系,即左下角为原点,x横向右延伸,y纵向上延伸。像著名的 cocos2d 引擎就是此种方式。我比较偏好另一套就是左上角为原点的描述方式,所以采用了此套坐标系:

GLSurfaceView 的 Renderer,给出了三个接口:创建时,大小改变时,绘制。我们在三个函数中采取的操作如下:

  • 创建时, onSurfaceCreated, 进行 OpenGL 的基本设置,如阴影平滑,深度测试啊神马的。
  • 大小改变时,onSurfaceChanged, 进行投影,视口等设置。
  • 绘制,onDrawFrame, 我们后续的游戏绘制主要在这个函数了。

2.1 onSurfaceCreated

不多说,都是基本设置。

 

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 告诉系统对透视进行修正
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
    // 背景黑色
    gl.glClearColor(0, 0, 0, 1);
    // 启用阴影平滑
    gl.glShadeModel(GL10.GL_SMOOTH);
    // 设置深度缓存
    gl.glClearDepthf(1.0f);
    // 启用深度测试
    gl.glEnable(GL10.GL_DEPTH_TEST);
    // 所做深度测试的类型
    gl.glDepthFunc(GL10.GL_LEQUAL);
}

 

2.2 onSurfaceChanged

 

public void onSurfaceChanged(GL10 gl, int width, int height) {
    // 设置 OpenGL 场景的大小
    gl.glViewport(0, 0, width, height);
    
    // 设置投影矩阵
    gl.glMatrixMode(GL10.GL_PROJECTION);
    // 重置投影矩阵
    gl.glLoadIdentity();
    // 设置视口的大小
    // gl.glOrthof(0, width, height, 0, -1000, 1000);
    GLU.gluOrtho2D(gl, 0, width, height, 0);
 
    // 重置模型矩阵
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();
}

 

代码中先用 glViewPort 设定了场景的大小,我们所有的opengl世界都被投射到这个场景中,在这里,我们将场景的大小设置为窗口 Surface 的大小。

继而设置了投影矩阵,投影矩阵定义了 opengl 世界怎样反应在你的场景中,在 OpenGL 中又两种投影方式: 透视和正交。透视比较接近我们现实的方式了,你的眼睛发出的光形成一个夹角,离你的眼睛越近,东西越大,范围越小;反之离眼睛越远,东西越小,视野越开阔。因此多用于 3D 中。 而正交却是世界中的物体按照平行的光线投射到一张纸上(你的画布),仿佛被压缩在上面,无论这个物体在世界中多远,投射结果还是原来的大小, 2d 游戏多用此种投影。

设置透视投影有 glFrustum 和 glu 库的 gluPerspective, 都可以设置上面透视投影中的角度,远近等参数。还可以 gluLookAt 来设置眼睛(相机)的位置。

设置正交投影有 glOrtho 和 glu 库的 gluOrth2D :

glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
gluOrth2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);

glu版本的y轴参数不用我们设置了,因为不会用到。由于我们的坐标系以左上角为原点,所以 left 和 top 参数为 0。

glMatrixMode() 函数指定了其下面的代码操作的是何种矩阵。 我们这里用到两类矩阵变换:投影变换和模型视图变换。投影变换中我们更改了世界空间的裁切范围,在模型视图变换中,我们可以移动和旋转世界中的物体,所以我们在绘制的时候就是在模型视图矩阵变换中。 glLoadIdentity() 用于重置矩阵,清除上次的残留信息。

代码最后设定为模型视图变换,便于我们在绘制函数中移动和变换物体模型了。

2.3 onDrawFrame

先说明一下 GLSurfaceView 的 Renderer 绘制是在独立的线程中完成的,这个函数被不断的循环调用。

前面已经设置为模型视图矩阵了,每次 onDrawFrame 的时候,我们都要 glLoadIdentity 重置一次,清除上次绘制造成的残留信息。

 

public void onDrawFrame(GL10 gl) {
    // 清除屏幕和深度缓存
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    // 重置模型矩阵
    gl.glLoadIdentity();
 
    //... 要添加的具体绘制代码
}

 

3 三角形-测试

好了,上面已经设置好了,我们先来个简单的测试吧,在你的窗口中绘制一个红色三角形。先定义好三角形的三个角度,左上角为 (60,200), 右上角为 (180, 200), 下角为 (120,300)。

 

private FloatBuffer triggerBuffer = FloatBuffer.wrap(new float[]{
    60,200,     // 左上角
    180, 200,   // 右上角
    120,300,    // 下顶角
});
 
public void onDrawFrame(GL10 gl) {
    // 清除屏幕和深度缓存
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    // 重置模型矩阵
    gl.glLoadIdentity();
    
 
    gl.glPushMatrix();
        gl.glColor4f(1, 0, 0, 0);
        // 允许设置顶点
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, triggerBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
        // 取消设置顶点
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glPopMatrix();
}

 

主要代码包围在了 glPushMatrix() 和 glPopMatrix() 中,OpenGL内部维护了各种矩阵栈,栈的操作就是 push 和 pop 了。这里我们的操作是在模型视图矩阵变换中进行的,所以操作的是模型视图栈。之所以压栈,我们不想绘制三角形的矩阵信息污染到外层的矩阵中,比如颜色设置等。所以大家在绘制一个单位体的时候尽量使用 push 和 pop 操作。

先用 glColor4f 设置绘制颜色为红色。opengles 中少了 glBegin, glEnd 类标准函数,采用了数组顶点来设坐标方式,主要还是性能考虑吧。默认是关掉这个选项的,所以先开启,不用了再关闭: glEnableClientState(GL10.GLVERTEXARRAY), glDisableClientState(GL10.GLVERTEXARRAY).

指定数组顶点用 glVertexPointer, 指定完了就可以用 glDrawArray() 来将指定的数组中的顶点绘制出来了。这里有必要罗嗦一下这两个函数了,因为这两个函数用到的太多了。先看第一个:

void glVertexPointer (int size, int type, int stride, Buffer pointer)

参数:

size
每个顶点有几个数值描述。必须取值2,3,4之一。初始值是 4.
type
数组中每个顶点坐标的类型。取值:GLBYTE, GLSHORT, GLFIXED, GLFLOAT。初始值为 GLFLOAT。
stride
数组中每个顶点间的间隔,步长(字节位移)。取值若为 0 表示数组是连续的。初始值为 0。
pointer
就是你的数组了,存储着每个顶点的坐标值。初始值为 0。

注意了, type 中告诉 opengl 你的数组类型。 GLBYTE, GLSHORT, GLFLOAT 对应 byte[], short[], float[]. GLFIXED 对应 int[]. 有一个特别的地方, GLFIXED 描述的时候,大家的点坐标单位是 0x10000, 比如一个点是 (60, 120), 用 GLFIXED 的时候,需要设置为 (60 * 0x10000, 120 * 0x10000), 所以经常看到 int one = 0x10000 的编程语句, 这个数值就是这么来的。

再来看 glDrawArray:

void glDrawArrays (int mode, int first, int count);

参数:

mode
指定你要绘制何种图元, opengl 中的图元就这几个: GLPOINTS, GLLINESTRIP, GLLINELOOP, GLLINES, GLTRIANGLESTRIP, GLTRIANGLEFAN, GLTRIANGLES.
first
在已制定的数组中的开始位置(索引位置)
count
点的绘制次数, 比如我们绘制一个三角形,就是绘制三个顶点,即此参数为 3。

这两个函数就介绍这么多了,足以应付这个程序了。有问题可以查官方函数文档: http://www.khronos.org/opengles/sdk/1.1/docs/man/

好了,代码就介绍完了。一个三角形大家应该会绘制了吧,大家可以试试其他的图元,点,线,四边形,多边形等等。

学习愉快。

posted on 2011-01-02 00:24  圣斗士  阅读(6352)  评论(14编辑  收藏  举报