12

Android用のタイルベースのゲームを作りたいです。現在、各タイルを個別のビットマップとして描画しています。文字列から読み取り、レベルを描画するために見つけた文字に応じて異なるタイルを描画する大きなforループがあります。

ユーザーがスクロールジェスチャを使用して画面をスクロールできるようにしました。ただし、ゲームは遅すぎます。ユーザーがスクロールした後、画面を更新するのに長い時間がかかります。これは、各タイルのビットマップを個別に描画する必要があるためだと思います。

レベルを描画するためのより速い方法は何でしょうか?すべてのタイルを1つのビットマップにマージできると思っていました。しかし、私はこれを行う方法がわかりません。何か案は?

とにかくここに私のコードがあるので、問題を見ることができます:

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();
      }
 }
}
4

6 に答える 6

28

レンダリングされたタイルごとに、各ビットマップ イメージの新しいインスタンスを作成しているようです。その代わりに、タイル タイプごとに 1 つのインスタンスを作成できますか? 元:

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

次に、タイルが描画されるたびに同じタイル インスタンスを再利用します。これが機能しない場合は、何らかのパフォーマンス測定を行って、コードのどの部分に最も時間がかかっているかを確認し、コードの実行時間を最小限に抑えるか、スリム化する必要があります。

免責事項: 私は Android プログラマーではありません

于 2009-09-16T03:18:48.803 に答える
15

問題はかなり明白です。あなたはただ地獄のようなたくさんのメモリを漏らしています。LogCatを見ると、私が何を意味しているのかがわかります。1.フレームごとに文字列を割り当てないでください。それらは不変であるため、文字列を使用するすべての操作はメモリリークに等しくなります。代わりに、変更する単純なchar[]オブジェクトを使用してください。2.ビットマップオブジェクトの作成を何度も停止します。DecodeBitmapメソッドは、呼び出しごとにビットマップオブジェクトを内部的に割り当てると思います。フレームごとにそれを行うのは悪いことです。

経験則として->メモリリークとそれは相棒ですGCは描画時に回避される非常に高価な操作です。

于 2009-09-17T15:43:37.817 に答える
3

私はこれまで Android 向けにプログラミングしたことがないので、その裏で何が起こっているのか 100% 確信が持てませんが、次のように仮定します。

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

このコードのビットは、ビットマップをメモリにロードしてロードするため、描画するたびに各ビットマップを再ロードしているように見えます。

過去に小さなゲームを開発するときに私が行ったことは、レベルをロードし、必要なすべてのリソースを計算し、それらを一度メモリにロードしてから再利用することでした。

于 2009-09-16T03:18:06.970 に答える
2
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);

    }

}

}

于 2014-10-15T07:13:19.273 に答える
0

プログラマー以外からの別の考えは、スクロールする大きなタイルの背景などを作成することと、移動可能なアイテム (プレイヤー、npc、アイテム) が背景の上に描画される 2 番目の最上層を作成することです。したがって、スクロールはより高速になり、全体的な (再) レンダリングが少なくなります。これは、既にレンダリングされたタイルを参照するよりもリソースを集中的に使用する可能性があると思います (上記の別の提案で述べたように)。しかし、そうではないかもしれません。試してみてください。:-)

于 2011-01-03T11:09:04.160 に答える
0

ユーザーがスクロールするときにビュー全体を移動するのはどうですか。ウィンドウをスクロールすると、実際には、認識した方向とは反対の方向にウィンドウが移動します。下にスクロールすると、ウィンドウが上に移動します..

ビュー(グリッド)内のすべての要素を収集し、ビュー全体を移動できると確信しています..画像を画面外にレンダリングし(より速く)、必要に応じてそれらを上に移動します..

利用可能な例があります..ホーム画面のすべてのコードを取得できることを知っています。左から右にスクロールします。調べるものかもしれません。彼らがそのアプリを正しくやったと確信しています。

于 2011-11-25T11:00:16.133 に答える