views:

530

answers:

2

Hi !

I'll start by saying that i'm REALLY new to OpenGL ES (I started yesterday =), but I do have some Java and other languages experience.

I've looked a lot of tutorials, of course Nehe's ones and my work is mainly based on that.

As a test, I started creating a "tile generator" in order to create a small Zelda-like game (just moving a dude in a textured square would be awsome :p).

So far, I have achieved a working tile generator, I define a char map[][] array to store wich tile is on :

private char[][] map = {
            {0, 0, 20, 11, 11, 11, 11, 4, 0, 0},
            {0, 20, 16, 12, 12, 12, 12, 7, 4, 0},
            {20, 16, 17, 13, 13, 13, 13, 9, 7, 4},
            {21, 24, 18, 14, 14, 14, 14, 8, 5, 1},
            {21, 22, 25, 15, 15, 15, 15, 6, 2, 1},
            {21, 22, 23, 0, 0, 0, 0, 3, 2, 1},
            {21, 22, 23, 0, 0, 0, 0, 3, 2, 1},
            {26, 0, 0, 0, 0, 0, 0, 3, 2, 1},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
    };

It's working but I'm no happy with it, I'm sure there is a beter way to do those things :

1) Loading Textures :

I create an ugly looking array containing the tiles I want to use on that map :

private int[] textures = {
            R.drawable.herbe, //0
            R.drawable.murdroite_haut, //1
            R.drawable.murdroite_milieu, //2
            R.drawable.murdroite_bas, //3
            R.drawable.angledroitehaut_haut, //4
            R.drawable.angledroitehaut_milieu,  //5
    };

(I cutted this on purpose, I currently load 27 tiles)

All of theses are stored in the drawable folder, each one is a 16*16 tile.

I then use this array to generate the textures and store them in a HashMap for a later use :

int[] tmp_tex = new int[textures.length]; 
            gl.glGenTextures(textures.length, tmp_tex, 0); 
            texturesgen = tmp_tex; //Store the generated names in texturesgen 
            for(int i=0; i < textures.length; i++)
            { 
                    //Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), textures[i]);
                    InputStream is = context.getResources().openRawResource(textures[i]);
                    Bitmap bitmap = null;
                    try {
                        //BitmapFactory is an Android graphics utility for images
                        bitmap = BitmapFactory.decodeStream(is);

                    } finally {
                        //Always clear and close
                        try {
                            is.close();
                            is = null;
                        } catch (IOException e) {
                        }
                    } 
                    // Get a new texture name 
                    // Load it up 
                    this.textureMap.put(new Integer(textures[i]),new Integer(i)); 
                    int tex = tmp_tex[i]; 
                    gl.glBindTexture(GL10.GL_TEXTURE_2D, tex); 
                    //Create Nearest Filtered Texture
                    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
                    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

                    //Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
                    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
                    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

                    //Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
                    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

                    bitmap.recycle();
            } 

I'm quite sure there is a better way to handle that... I just was unable to figure it. If someone has an idea, i'm all ears.

2) Drawing the tiles

What I did was create a single square and a single texture map :

/** The initial vertex definition */
    private float vertices[] = { 
                                -1.0f, -1.0f, 0.0f,     //Bottom Left
                                1.0f, -1.0f, 0.0f,      //Bottom Right
                                -1.0f, 1.0f, 0.0f,      //Top Left
                                1.0f, 1.0f, 0.0f        //Top Right
                                                };

    private float texture[] = {         
            //Mapping coordinates for the vertices
              0.0f, 1.0f,
              1.0f, 1.0f,
              0.0f, 0.0f,
              1.0f, 0.0f

                                };

Then, in my draw function, I loop through the map to define the texture to use (after pointing to and enabling the buffers) :

for(int y = 0; y < Y; y++){
            for(int x = 0; x < X; x++){
                tile = map[y][x];
                try 
                    { 
                                   //Get the texture from the HashMap
                       int textureid = ((Integer) this.textureMap.get(new Integer(textures[tile]))).intValue(); 
                       gl.glBindTexture(GL10.GL_TEXTURE_2D, this.texturesgen[textureid]); 
                    }    
                    catch(Exception e) 
                    { 
                      return; 
                    }

                //Draw the vertices as triangle strip
                gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);            
                gl.glTranslatef(2.0f, 0.0f, 0.0f); //A square takes 2x so I move +2x before drawing the next tile
            }
            gl.glTranslatef(-(float)(2*X), -2.0f, 0.0f); //Go back to the begining of the map X-wise and move 2y down before drawing the next line
        }

This works great by I really think that on a 1000*1000 or more map, it will be lagging as hell (as a reminder, this is a typical Zelda world map : http://vgmaps.com/Atlas/SuperNES/LegendOfZelda-ALinkToThePast-LightWorld.png ).

I've read things about Vertex Buffer Object and DisplayList but I couldn't find a good tutorial and nodoby seems to be OK on wich one is the best / has the better support (T1 and Nexus One are ages away).

I think that's it, I've putted a lot of code but I think it helps.

Thanks in advance !

A: 

A couple of things:

  1. There's no need to use a hashmap, just use a vector/list.
  2. It may be faster/easier to have one large texture that contains all your tiles. Use appropriate texture coordinates to select the appropriate tile. You might have to be a little bit careful about texture filtering here. It sounds like you are doing a 2D game in which case you probably want to use nearest-neighbour filtering for the tiles and clamp the camera to integer pixel locations.
  3. Wouldn't it be easier to use GL_QUADS rather than GL_TRIANGLE_STRIP. Not sure about your code there - you don't seem to use the 'texture' array.
  4. The map size shouldn't make any difference, as long as you don't draw tiles that aren't on the screen. Your code should be something like:

.

int minX = screenLeft / tileSize;
int minY = screenBottom / tileSize;
int maxX = screenRight / tileSize;
int maxY = screenTop / tilesSize;
for (int x = minX; x <= maxX; ++x)
{
   for (int y = minY; y < maxY; ++y)
   {
      ...
Timmmm
A: 

Thanks for your input.

  1. I'll see about that tonigh.

  2. Yeah, i'm doing a 2D game (SNES Zelda-like). So far I just have copied things that worked and modified them, i'll look into "nearest-neighbour". What exactly is "clamp the camera" ? Right now my camera isn't moving, because I want to have a clean&fast tile generator first.

  3. GL_QUADS is not available on Android :(. The texture array is transformed into a textureBuffer :

    byteBuf = ByteBuffer.allocateDirect(texture.length * 4); byteBuf.order(ByteOrder.nativeOrder()); textureBuffer = byteBuf.asFloatBuffer(); textureBuffer.put(texture); textureBuffer.position(0);

And then I use it in my draw function :

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);       

        //Enable vertex buffer
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  1. Yeah that is probably the best, as long as I can retrieve the screen size, because every Android phone is different =.

Sorry for answering my own question, but the code is unreadable in comments.

Thanks !

Inoe