我想为Android制作基于磁贴的游戏。目前我正在将每个图块绘制为单独的位图。我有一个大的for循环,它从字符串中读取并根据它找到的绘制关卡的字符绘制不同的tile。
我允许用户使用滚动手势滚动屏幕。然而游戏太慢了。用户滚动后更新屏幕需要很长时间。我认为这是因为它必须单独绘制每个tile的位图。
绘制关卡的更快方法是什么?我以为我可以把所有的瓷砖合并成一个位图。但我不知道该怎么做。有什么想法吗?
无论如何这里是我的代码,所以你可以看到问题:
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();
}
}
}
答案 0 :(得分:28)
看起来您正在为每个渲染的图块创建每个位图图像的新实例。也许不是这样做,你可以为每种瓷砖类型创建一个实例?例如:
private Bitmap wallTile = BitmapFactory.decodeResource(getResources(), R.drawable.walla);
private Bitmap floorTile = BitmapFactory.decodeResource(getResources(), R.drawable.floore);
每次绘制图块时重复使用相同的图块实例。如果这不起作用,您应该进行某种性能测量,以查看代码的哪一部分花费的时间最长,并最小化代码运行的次数,或者尝试减少代码量。
免责声明:我不是Android程序员
答案 1 :(得分:15)
问题非常明显。你只是泄漏了很多记忆。看看LogCat,你会明白我的意思。 1.永远不要在每帧分配字符串。它们是不可变的,因此每个带字符串的操作都等于内存泄漏。请改为使用您修改的简单char []对象。 2.一遍又一遍地停止创建位图对象。我想DecodeBitmap方法在内部为每个调用分配一个位图对象。每帧都这样做很糟糕。
根据经验 - &gt;泄漏的内存和它的伙伴GC是绘图时要避免的非常昂贵的操作。
答案 2 :(得分:3)
我以前从来没有为Android编程,所以我不是百分之百地确定那里的封面是什么,但假设:
BitmapFactory.decodeResource(getResources(), R.drawable.walla);
这段代码会将位图加载到内存中,看起来你在绘制它时会重新加载每个位图。
我在过去开发小游戏时所做的是,当你去加载一个关卡时,计算出你需要的所有资源,然后将它们加载到内存中,然后重复使用它们。
答案 3 :(得分: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);
}
}
}
答案 4 :(得分:0)
另一个想法,来自非程序员,可能会创建一个滚动等大的平铺背景,以及第二个顶层,其中可移动项目(玩家,npc,项目)绘制在背景之上。因此,滚动应该更快,更少整体(重新)渲染。 我想这可能比引用已经渲染的瓷砖更耗费资源(如上面的另一个建议所述)..但可能不是,你必须给它一个镜头看看。 : - )
答案 5 :(得分:0)
当用户滚动时移动整个视图怎么样?当您在窗口中滚动时,实际上您将以与您感知的相反方向移动窗口。向下滚动,窗户向上移动..
我很确定你可以收集视图中的所有元素(网格)并移动整个视图.. 在屏幕上渲染图像(更快)并根据需要移动它们。
有可用的示例..我知道您可以获得主屏幕的所有代码,并且它从左向右滚动。可能需要研究一下。我很确定他们做的那个应用是对的。