views:

8350

answers:

3

Hi,

I want to make a tile based game for android. At the moment I am drawing each tile as a separate bitmap. I have a big for loop that reads from a string and draws different tiles depending on what character it finds to draw the level.

I have allowed the user to scroll the screen using scrolling gestures. However the game is too slow. It takes a long time to update the screen after the user scrolls. I presume this is because it has to draw each tile's bitmap individually.

What would be a faster way to draw the level? I was thinking I could merge all the tiles into one bitmap. But I don't know how to do this. Any ideas?

Anyway here is my code so you can see the problem:

package org.example.tutorial2d;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.GestureDetector.OnGestureListener;

import org.example.tutorial2d.Panel;

public class Tutorial2D extends Activity implements OnGestureListener {

GestureDetector gestureScanner;
Panel main;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    gestureScanner = new GestureDetector(this);

    //requestWindowFeature(Window.FEATURE_NO_TITLE);       
    main = new Panel(this);
    setContentView(main);       

}

@Override
public boolean onTouchEvent(MotionEvent me)
{
 return gestureScanner.onTouchEvent(me);
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
 main.handleScroll(distanceX,distanceY);
 return true;
}

////////////////////
///////////////////
//////////////////
@Override
public boolean onDown(MotionEvent e)
{
 return true;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
 return true;
}

@Override
public void onLongPress(MotionEvent e){    }

@Override
public void onShowPress(MotionEvent e) {   }    

@Override
public boolean onSingleTapUp(MotionEvent e)    
{
 return true;
}
////////////////////
///////////////////
//////////////////
}

And the class that does all the work:

package org.example.tutorial2d;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import org.example.tutorial2d.Point;

public class Panel extends View {

private int scrollX = 0;
private int scrollY = 0;

public Panel(Context context)
{
 super(context);
}

@Override
public void onDraw(Canvas canvas)
{
 /*Bitmap scratch;
 //Drawable scratch;
 //scratch = getContext().getResources().getDrawable(
    //        R.drawable.icon);
 canvas.drawColor(Color.BLACK);
 //scratch.draw(canvas);
 int origin = 0;
 scratch = BitmapFactory.decodeResource(getResources(), R.drawable.horizontal5);
 canvas.drawBitmap(scratch, origin, origin, null);
 int width = scratch.getWidth();
 int height = scratch.getHeight();
 scratch = BitmapFactory.decodeResource(getResources(), R.drawable.room4entrynesw3x3);
 canvas.drawBitmap(scratch, origin + width, origin - 32, null);
 */

 String sucide_mission = 
"                  wwwww\n" +
"                  wfffw\n" +
"                  wfffw\n" +
"                  wfffw\n" + 
"                  wwfww\n" +
"                   wfw\n" + 
"                   wfw\n" +
"                   wfw\n" +
"             wwwwwwwfwwwwwfw\n" +
"             wfffffffffffffw\n" +
"             wfwwwwwfwwwwwfw\n" +
"     wwwww   wfw   wfw   wfw\n" +
"wwwwwwfffwwwwwfwwwwwfwwwwwfw\n" +
"fffffffffffffffffffffffffffw\n" +
"wwwwwwfffwwwwwwwwwwwfwwwwwfw\n" +
"     wwfww         wfw\n" +
"      wfw          wfw\n" +
"      wfw          wfw\n" +
"      wfw          wfw\n" +
"      wfw          wfw\n" +
"     wwfww         wfw\n" +
"     wfffwwfw      fff\n" +
"     wffffffw      www\n" +
"     wfffwwfw\n" +
"     wwwww";

 canvas.drawColor(Color.BLACK);
 int x = 0, y = 0;

 for (int i = 0; i < sucide_mission.length(); i++)
 {
  Bitmap tileImage;
  char tile = sucide_mission.charAt(i);

  Log.d("Draw tiles", Character.toString(tile) + " " + x + "," + y);

  switch (tile)
  {
   case 'w':
    if (x < tileImage = BitmapFactory.decodeResource(getResources(), R.drawable.walla);
    canvas.drawBitmap(tileImage, x - scrollX, y - scrollY, null);
    x += 32;
    break;
   case 'f':
    tileImage = BitmapFactory.decodeResource(getResources(), R.drawable.floore);
    canvas.drawBitmap(tileImage, x - scrollX, y - scrollY, null);
    x += 32;
    break;
   case ' ':
    x += 32;
    break;
   case '\n':
    y += 32;
    x = 0;
    break;
  }

 }

 //canvas.drawBitmap(adapt, 0, 0, paint);
 //canvas.drawBitmap(corner, origin -scrollX , origin -scrollY, paint);

}

 public void handleScroll(float distX, float distY)
 {
      // X-Axis ////////////////////////////////

      if(distX > 6.0)
      {
           if(scrollX < 460)
           {
                scrollX += 30;
           }
      }
      else if(distX < -6.0)
      {
           if(scrollX >= 30)
           {
                scrollX -= 30;
           }
      }
      ////////////////////////////////////////////

      // Y-AXIS //////////////////////////////////
      if(distY > 6.0)
      {
           if(scrollY < 100)
           {
                scrollY += 30;
           }
      }
      else if(distY < -6.0)
      {
           if(scrollY >= 30)
           {
                scrollY -= 30;
           }
      }              
      ////////////////////////////////////////////

      if((scrollX <= 480) && (scrollY <= 120))
      {
           //adapt = Bitmap.createBitmap(bmp, scrollX, scrollY, 320, 480);
           invalidate();
      }
 }
}
+1  A: 

I've never programmed for Android before, so I'm not 100% sure about what's going on underneath the covers there, but assuming that:

BitmapFactory.decodeResource(getResources(), R.drawable.walla);

this bit of code goes and loads up a bitmap into memory, it would appear that you're reloading each bitmap as you draw it.

What I've done when developing small games in the past, is when you go to load a level, work out all of the resources that you'll need then load them up into memory once, then reuse them.

Benny Hallett
+9  A: 

It looks like you are creating a new instance of each bitmap image for every tile rendered. Maybe instead of doing that, you could create one instance for each tile type? ex:

private Bitmap wallTile = BitmapFactory.decodeResource(getResources(), R.drawable.walla); private Bitmap floorTile = BitmapFactory.decodeResource(getResources(), R.drawable.floore);

Then reuse the same tile instance each time the tile is drawn. If this doesn't work, you should put in some kind of performance measurement to see what part of the code is taking the longest time, and minimize the amount of times that code is run, or try to slim it down.

Disclaimer: I am not an Android programmer

RMorrisey
Thanks that fixed my program.
+5  A: 

The problem is pretty obvious. You are just leaking a hell lot of memory. Take a look in the LogCat and you'll see what I mean. 1. NEVER allocate strings each frame. They are immutable so every operation with a strings equals memory leaking. Use instead a simple char[] object that you modify. 2. Stop creating bitmap objects over and over again. I suppose that the DecodeBitmap method internally allocates a bitmap object for each call. It's bad to do that every frame.

As a rule of thumb -> leaking memory and it's buddy the GC are very expensive operations that be avoided when drawing.