如何在Android中加载一个包含5行5列的顶视角精灵图?

9
我有一个尺寸为612x864的精灵表,其中包含5行5列。我的问题是如何加载它并进行动画处理?我只想在y轴上移动猫的精灵。我已经尝试过了,但我的代码不正常工作。以下是我的代码。
在GameView.java中:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class GameView extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private Sprite sprite;

public GameView(Context context) {
    super(context);
    gameLoopThread = new GameLoopThread(this);
    holder = getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {

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

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

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format,
                                   int width, int height) {
        }
    });
    bmp = BitmapFactory.decodeResource(getResources(), R.drawable.catsprite);
    sprite = new Sprite(this,bmp);
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawColor(Color.BLACK);
    sprite.onDraw(canvas);
}
}

GameLoopThread.java

import android.graphics.Canvas;

public class GameLoopThread extends Thread {
static final long FPS = 10;
private GameView view;
private boolean running = false;

public GameLoopThread(GameView view) {
    this.view = view;
}

public void setRunning(boolean run) {
    running = run;
}

@Override
public void run() {
    long ticksPS = 1000 / FPS;
    long startTime;
    long sleepTime;
    while (running) {
        Canvas c = null;
        startTime = System.currentTimeMillis();
        try {
            c = view.getHolder().lockCanvas();
            synchronized (view.getHolder()) {
                view.onDraw(c);
            }
        } finally {
            if (c != null) {
                view.getHolder().unlockCanvasAndPost(c);
            }
        }
        sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
        try {
            if (sleepTime > 0)
                sleep(sleepTime);
            else
                sleep(10);
        } catch (Exception e) {}
    }
}
}

Sprite.java

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;

public class Sprite {
private static final int BMP_ROWS = 5;
private static final int BMP_COLUMNS = 5;
private int x = 0;
private int y = 0;
private int ySpeed = 3;
private GameView gameView;
private Bitmap bmp;
private int currentFrame = 1;
private int width;
private int height;

public Sprite(GameView gameView, Bitmap bmp) {
    this.gameView = gameView;
    this.bmp = bmp;
    this.width = bmp.getWidth() / BMP_COLUMNS;
    this.height = bmp.getHeight() / BMP_ROWS;
}

private void update() {
    if (y > gameView.getWidth() - width - y) {
        ySpeed = -5;
    }
    if (y + y < 0) {
        ySpeed = 5;
    }
    y = y + ySpeed;
    currentFrame = ++currentFrame % BMP_COLUMNS;
}

public void onDraw(Canvas canvas) {
    update();
    int srcX = currentFrame * width;
    int srcY = 1 * 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);
}
}

2
你应该考虑使用一个游戏引擎,比如LibGDX。它拥有类和方法,可以让这种事情变得非常简单轻松。 - Code-Apprentice
据我所知,您将“GameView”传递给“Sprite”构造函数的唯一原因是为了检测与屏幕侧面的碰撞。我建议向“Sprite”构造函数发送一个“int maxY”参数,而不是一个“GameView”。这将解耦您的“Sprite”和“GameView”类。 - Code-Apprentice
感谢您的评论,@Code-Apprentice先生。我会尝试使用游戏引擎实现它。 - user7135072
2个回答

3
我建议您使用这个库来进行Sprite动画。虽然它有一些限制,但它的表现良好。以下是我完成它的代码。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.runningcat);
int width = bitmap.getWidth();
int height = bitmap.getHeight();

int frameWidth = width / 5;    //you have 5 columns
int frameHeight = height / 5;  //and 5 rows
int frameNum = 25;             //there would be 25 images


SpriteSheetDrawer spriteSheetDrawer = new SpriteSheetDrawer(
        bitmap,
        frameWidth,
        frameHeight,
        frameNum)
        .spriteLoop(true)
        .frequency(2);   //change it as per your need


DisplayObject displayObject = new DisplayObject();
displayObject
        .with(spriteSheetDrawer)
        .tween()
        .tweenLoop(true)
        .transform(0, 0) //I have changed it according to my need, you can also do this by changing these values
        .toX(4000, 0)    //this one too.
        .end();
//In actual example, it's set as animation starts from one end of the screen and goes till the other one.


FPSTextureView textureView = (FPSTextureView) findViewById(R.id.fpsAnimation);
textureView.addChild(displayObject).tickStart();

3

问题出在你使用的方法中的src值上。canvas.drawBitmap(bmp, src, dst, null); srcY 应该是零。我已在此测试过。

private Bitmap character;
private counter,charFrame;
private RectF annonymousRectF;
private Rect annonymousRect;

public Sprite() {
     character=BitmapFactory.decodeResource(context.getResources(), R.drawable.flipchar2);
     annonymousRect=new Rect();
     annonymousRectF=new RectF();
} 

public void update() {
    counter++;
    if(counter%5==0)
        if(charFrame<NO_CHAR_FRAME-1)
            charFrame++;
        else
            charFrame=0;
}

public void draw(){
    annonymousRect.set(charFrame*character.getWidth()/NO_CHAR_FRAME,0,(charFrame+1)*character.getWidth()/NO_CHAR_FRAME,character.getHeight());
    annonymousRectF.set(-width*.015f,height*.35f,width*.5f,height*.58f);  //set value according to where you want to draw
    canvas.drawBitmap(character, annonymousRect,annonymousRectF, null);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接