Java Android:移动bmp留下轨迹(曲面视图和画布)

Java Android:移动bmp留下轨迹(曲面视图和画布),java,android,sprite,android-canvas,surfaceview,Java,Android,Sprite,Android Canvas,Surfaceview,我试图为android应用程序制作一个精灵的动画,但遇到了一个小问题。我在一个surfaceView上画图,我在已经存在的布局上添加了它。在这个surfaceView上,我想给一些精灵设置动画,让它们沿着一条路径行走 这就是我现在面临的结果: 行走的精灵正在留下一条小径。因此,我决定用谷歌搜索这个问题,显然我必须先清理画布,然后再在画布上绘图。 这是我以前的onDraw方法: public void onDraw(Canvas canvas) { update(); int sr

我试图为android应用程序制作一个精灵的动画,但遇到了一个小问题。我在一个surfaceView上画图,我在已经存在的布局上添加了它。在这个surfaceView上,我想给一些精灵设置动画,让它们沿着一条路径行走

这就是我现在面临的结果:

行走的精灵正在留下一条小径。因此,我决定用谷歌搜索这个问题,显然我必须先清理画布,然后再在画布上绘图。
这是我以前的onDraw方法:

public void onDraw(Canvas canvas) {
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}
这是我的onDraw方法

public void onDraw(Canvas canvas) {
    canvas.drawRGB(0, 0, 0);
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}
所以我现在有了这个结果

这是伟大的,因为它没有留下更多的线索,但我看不到下面的布局。不管怎样,我能做到两全其美吗?我原以为清理画布会有效果,但显然没有达到预期效果

我将在下面发布我的代码

表面视图:

public class MonsterView extends SurfaceView {

private Bitmap monsterImg;
private SurfaceHolder holder;
private MonsterThread mainThread;
private Sprite monsterSprite;
private int x;
private int xSpeed = 1;

public MonsterView(Context context) {
    super(context);
    this.mainThread = new MonsterThread(this);
    this.x = 0;


    holder = getHolder();
    holder.setFormat(PixelFormat.TRANSPARENT);

    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            mainThread.setRunning(true);
            mainThread.start();
        }

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

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            boolean retry = true;
            mainThread.setRunning(false);
            while (retry) {
                try {
                    mainThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                }
            }

        }
    });
    setZOrderOnTop(true);
    monsterImg = BitmapFactory.decodeResource(getResources(), R.drawable.fire);
    monsterSprite = new Sprite(this,monsterImg);
}

@Override
public void onDraw(Canvas canvas) {
    if (x == getWidth() - monsterImg.getWidth()) {
        xSpeed = -1;
    }
    if (x == 0) {
        xSpeed = 1;
    }
    x = x + xSpeed;
    monsterSprite.onDraw(canvas);
}
线程:

public class MonsterThread extends Thread {
private MonsterView monsterSurface;
private boolean running;
static final long FPS = 35;

public MonsterThread(MonsterView monsterSurface){
    this.monsterSurface = monsterSurface;
    this.running = false;
}

@Override
public void run() {
    long ticksPS = 1000 / FPS;
    long startTime;
    long sleepTime;

    while(running){
        Canvas c = null;
        startTime = System.currentTimeMillis();
        try{
            c = monsterSurface.getHolder().lockCanvas();
            synchronized (monsterSurface.getHolder()){

                monsterSurface.onDraw(c);
            }
        } finally {
            if( c!= null){
                monsterSurface.getHolder().unlockCanvasAndPost(c);

            }
        }
        sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
        try {
            if(sleepTime > 0)
                sleep(sleepTime);
            else
                sleep(10);
        } catch (Exception e){}
    }
}

public MonsterView getMonsterSurface() {
    return monsterSurface;
}

public void setMonsterSurface(MonsterView monsterSurface) {
    this.monsterSurface = monsterSurface;
}

public boolean isRunning() {
    return running;
}

public void setRunning(boolean running) {
    this.running = running;
}
雪碧:

public class Sprite {
private static final int BMP_ROWS = 4;
private static final int BMP_COLUMNS = 3;
private int x = 0;
private int y = 0;
private int xSpeed = 5;
private MonsterView monsterView;
private Bitmap bmp;
private int currentFrame = 0;
private int width;
private int height;

private int directionX;

public Sprite(MonsterView monsterView, Bitmap bmp) {
    this.monsterView = monsterView;
    this.bmp=bmp;
    this.width = bmp.getWidth() / BMP_COLUMNS;
    this.height = bmp.getHeight() / BMP_ROWS;
    this.directionX = 2;
}

private void update() {
    if (x > monsterView.getWidth() - width - xSpeed) {
        xSpeed = -5;
        directionX = 1;
    }
    if (x + xSpeed < 0) {
        xSpeed = 5;
        directionX = 2;
    }
    x = x + xSpeed;
    currentFrame = ++currentFrame % BMP_COLUMNS;
    Log.d("test", ""+currentFrame);
}

public void onDraw(Canvas canvas) {
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}
公共类精灵{
私有静态final int BMP_ROWS=4;
私有静态final int BMP_COLUMNS=3;
私有整数x=0;
私有整数y=0;
私有int xSpeed=5;
私人视图;
私有位图bmp;
私有int currentFrame=0;
私有整数宽度;
私人内部高度;
私有int-directionX;
公共精灵(怪物视图,位图bmp){
this.monsterView=monsterView;
这个.bmp=bmp;
this.width=bmp.getWidth()/bmp\u列;
this.height=bmp.getHeight()/bmp\u行;
这个方向x=2;
}
私有void更新(){
if(x>monsterView.getWidth()-width-xSpeed){
xSpeed=-5;
方向x=1;
}
如果(x+x速度<0){
xSpeed=5;
方向x=2;
}
x=x+x速度;
currentFrame=++currentFrame%BMP_列;
Log.d(“测试”,“当前帧”);
}
公共空白onDraw(画布){
更新();
int srcX=当前帧*宽度;
int srcY=方向x*高度;
Rect src=新的Rect(srcX,srcY,srcX+宽度,srcY+高度);
Rect dst=新的Rect(x,y,x+宽度,y+高度);
drawBitmap(bmp、src、dst、null);
}

因此,在绘制精灵之前,您必须先将背景位图绘制到曲面上。现在,您正在绘制覆盖背景布局的黑色


或者,您也可以使用Paint.setColor to Color.Transparent绘制canvas.drawRect。

有多种方法可以处理此问题,但通常的方法是维护背景层(这只是另一个图像)和精灵层。而不是清除,而是在每个帧上进行blit(复制)将背景放在曲面上(这会擦除所有内容),然后将精灵闪烁。

您使用的是
setZOrderOnTop()
,它将曲面视图的曲面部分放在高于所有内容的图层上。(默认情况下,它是其他内容下方的一个单独图层。)当您使用
canvas.drawRGB(0,0)清除它时
将整个图层设置为不透明黑色,这将使其下方的视图图层变得模糊

相反,如果您使用
canvas.drawColor(Color.transparent,PorterDuff.Mode.clear)
将其清除为透明黑色,您应该会得到想要的结果


FWIW,如果您仅在曲面上绘制,则不应重写
onDraw()
。视图层次结构调用
onDraw()
方法来绘制SurfaceView的视图部分。如果有什么东西使SurfaceView的视图无效,则您的
onDraw()
将被调用,您将在视图层上看到一个角色精灵(根据您的布局,这可能不可见)只需给该方法一个不同的名称。

背景不是位图,它是由多个imageView组成的相对布局,每个都像一个单元格。surfaceView添加在末尾,因此我首先将板附加到我的根布局,然后将surfaceView附加到板上。我尝试了这个
Paint p=new Paint(Color.TRANSPARENT);canvas.drawRect(x,y,spriteWidth,spriteHeight,p);
但不起作用(我仍然得到了第一个屏幕截图那样的线索):/嗯,您可能正在使用位图作为ImageView的源,因此我仍然会建议我所说的内容,并将它们绘制到其他人也建议的画布上。或者,您可以采用该相对布局并绘制到画布上。RelativeLayout有一个绘制(画布)覆盖这些像素,然后在背景层重新绘制精灵的函数实际上不是一个简单的图像,它是由许多小图像视图组成的布局,每个图像视图的行为都像一个单元格(它们将与我的游戏类通信,以获得精灵在屏幕上的确切位置等),正好有198个imageView,所以我想如果每次我的精灵迈出一步时我都要重新绘制背景的imageView,那么我的性能就会下降。我想说的是,大多数视频游戏都不是这样做的-出于性能和效率的原因。为了保持速度,请将背景预渲染为位图。请查看任意n关于如何制作基于瓷砖的小型视频游戏的大量教程,你会明白我的意思。在尝试了太多关于这个问题的解决方案后,我决定重新思考我绘制背景图像和精灵的方式,并决定采用这种方式。结果证明它非常有效:)