Как нарисовать много растровых изображений на экране в игре для Android без снижения производительности

StackOverflow https://stackoverflow.com/questions/1430741

  •  07-07-2019
  •  | 
  •  

Вопрос

Я хочу создать игру на основе плиток для Android.На данный момент я рисую каждую плитку как отдельное растровое изображение.У меня есть большой цикл for, который считывает данные из строки и рисует разные фрагменты в зависимости от того, какой символ он находит для рисования уровня.

Я разрешил пользователю прокручивать экран с помощью жестов прокрутки.Однако игра идет слишком медленно.Обновление экрана после прокрутки пользователем занимает много времени.Я предполагаю, что это связано с тем, что он должен рисовать растровое изображение каждой плитки по отдельности.

Какой был бы более быстрый способ нарисовать уровень?Я думал, что мог бы объединить все фрагменты в одно растровое изображение.Но я не знаю, как это сделать.Есть какие-нибудь идеи?

В любом случае, вот мой код, чтобы вы могли увидеть проблему:

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;
}
////////////////////
///////////////////
//////////////////
}

И класс, который выполняет всю работу:

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();
      }
 }
}
Это было полезно?

Решение

Похоже, вы создаете новый экземпляр каждого растрового изображения для каждой визуализированной плитки. Может быть, вместо этого, вы могли бы создать один экземпляр для каждого типа плитки? например:

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

Затем повторно используйте один и тот же экземпляр тайла каждый раз, когда он рисуется. Если это не сработает, вы должны провести какое-то измерение производительности, чтобы увидеть, какая часть кода занимает больше всего времени, и минимизировать количество выполнений кода, или попытаться уменьшить его.

Отказ от ответственности: я не программист на Android

Другие советы

Проблема довольно очевидна. Ты просто чертовски много утратишь памяти. Загляните в LogCat, и вы поймете, что я имею в виду. 1. НИКОГДА не выделяйте строки в каждом кадре. Они неизменны, поэтому каждая операция со строками равна утечке памяти. Вместо этого используйте простой объект char [], который вы изменяете. 2. Прекратите создавать растровые объекты снова и снова. Я предполагаю, что метод DecodeBitmap внутренне выделяет растровый объект для каждого вызова. Это плохо делать каждый кадр.

Как правило - > утечка памяти и приятель GC - очень дорогие операции, которых следует избегать при рисовании.

Я никогда раньше не программировал для Android, поэтому я не уверен на 100%, что происходит под покровом, но предполагаю, что:

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

этот фрагмент кода отправляется и загружает растровое изображение в память; может показаться, что вы перезагружаете каждое растровое изображение по мере его отрисовки.

В прошлом я занимался разработкой небольших игр: когда вы загружаете уровень, отрабатываете все ресурсы, которые вам понадобятся, затем загружаете их в память, а затем снова используете.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MapLoader extends SurfaceView implements SurfaceHolder.Callback,
    Runnable {

SurfaceHolder holder;
Thread thread;

Bitmap grass = BitmapFactory.decodeResource(getResources(),
        R.drawable.tilemapdemo);
boolean running = false;

int[][] grassCoords = new int[][] { { 0, 16, 32, 48, 64 },
        { 0, 16, 32, 48, 64 }, { 0, 16, 32, 48, 64 },
        { 0, 16, 32, 48, 64 }, { 0, 16, 32, 48, 64 } };

public MapLoader(Context context, int width, int height) {
    super(context);

    holder = getHolder();
    holder.addCallback(this);
}

public MapLoader(Context context, AttributeSet attrs) {
    super(context, attrs);

    holder = getHolder();
    holder.addCallback(this);
}

public MapLoader(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    holder = getHolder();
    holder.addCallback(this);
}

public void pause() {
    running = false;

    while (running) {
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        break;
    }
    thread = null;
}

public void resume() {
    running = true;
    thread = new Thread(this);
    thread.start();

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {

    running = true;
    thread = new Thread(this);
    thread.start();

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Canvas c = holder.lockCanvas();
    draw(c);
    holder.unlockCanvasAndPost(c);

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

@Override
public void run() {

    while (running == true) {

        // performs drawing to the canvas
        if (!holder.getSurface().isValid()) {

            continue;
        }

        Canvas c = holder.lockCanvas();

        int x = 0;
        int y = 0;

        for (x = 0; x < grassCoords.length; x += grass.getWidth()) {

            for (y = 0; y < grassCoords.length; y += grass.getHeight()) {

                c.drawBitmap(grass, x, y, null);
            }

        }

        holder.unlockCanvasAndPost(c);

    }

}

}

Еще одна мысль, сделанная не программистом, это, возможно, создание большого мозаичного фона, который прокручивается и т. д., и второй верхний слой, где подвижные элементы (игроки, NPC, предметы) рисуются поверх фона. поэтому прокрутка должна быть более быстрой и менее общей (пере) рендеринга. Я полагаю, что это может быть более ресурсоемким, чем ссылка на уже обработанные плитки (как упомянуто в другом предложении выше) ... но это может быть не так, вам придется попробовать и посмотреть. : -)

Как насчет перемещения всего вида, когда пользователь прокручивает. Когда вы прокручиваете окна, вы фактически перемещаете окно в противоположном направлении, которое вы воспринимаете. Вы прокручиваете вниз, окна перемещаются вверх.

Я совершенно уверен, что вы можете собрать все элементы в виде (сетке) и переместить весь вид. Рендеринг изображений за пределы экрана (быстрее) и перемещение их по мере необходимости.

Есть примеры. Я знаю, что вы можете получить весь код для домашнего экрана, и он прокручивается слева направо. Может быть, есть что посмотреть. Я совершенно уверен, что они сделали это приложение правильно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top