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


    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/

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

    学习愉快。

  • 相关阅读:
    拼linq 时网上整理的一个类
    ASP.NET MVC controller 之间传JS值
    javascript 事件的一点感悟
    C#扩展特性
    javascript对json对象的序列化与反序列化
    javascript序列化json 第二篇
    单列模式
    Foreach 原理
    浅浅一谈 设计模式
    CRC循环冗余校验码总结(转)
  • 原文地址:https://www.cnblogs.com/shengdoushi/p/1923940.html
Copyright © 2020-2023  润新知