使用可变帧大小动态地将精灵表切割成单独的位图

时间:2013-11-09 18:43:49

标签: java android animation sprite

我想创建一个cookie-cutter类型的类,它接受一个源矩形并动态地遍历精灵表中的每个帧,直到每个图像都被剪切出来用于动画。当精灵表中的每个帧大小相同时,我可以做到这一点没有问题,但是几乎不可能找到具有相同帧大小的动画中具有任何复杂性的精灵表。例如:

Simple sprite sheet with constant frame size

像我想要的那样被剪切和动画,但是:

More complex bitmap with variable frame size

随着它的可变帧大小,变得有趣(因为我的程序目前假设所有帧都是相同的大小)。在某种程度上,是否有办法检测精灵表中每个帧的帧大小,或者这是一个失败的原因?

创建“cookie cutter”源框架的当前代码:

// Indirect Variable Sets (Sprite Animation and Sprite Sheet Cuts)
    framesPerRow = frameCount/spriteSheetRows;
    spriteWidth = bmp.getWidth() / framesPerRow;    // cut the sheet into pieces based on 
                                                    // sprite width: frames/row ratio
    spriteHeight = bmp.getHeight()/spriteSheetRows; // cut the sheet horizontally into pieces based on
                                                    // total height : total rows ratio

    setSourceRect(new Rect(0, 0, spriteWidth, spriteHeight));
    setFramePeriod(1000 / fps);                     // set the framePeriod based on desired fps

然后在我的更新方法中使用:

public void update(long gameTimeInMillis){
    // If the game time has been longer than the frame period...
    // the reason that we need frameTicker is so that we can use our variable (frameTicker)
    // to keep track of the last time that the frame was updated (relative to our game)
    if (gameTimeInMillis > frameTicker + framePeriod){
        frameTicker = gameTimeInMillis;             // set last update time (current time)
        // increment the animation frame
        currentFrame++;

        // Get current column in sprite sheet:
        // this works like this, imagine we are at frame 20, and we have 5 frames per row
        // 20%5 = 0 so we are at the end of the row, if we are at frame 22, 22%5 = 2, etc.
        frameColumn = currentFrame%framesPerRow;

        // if we are at our max frame count (note, we start at 0) then reset our animation
        if(currentFrame >= frameCount){ 
            currentFrame = 0;                   
        }

        // increment the sprite sheet row if we are at the end of the row
        if(frameColumn == 0){
            currentRow++;
        }

        // if we are at our max rows (note, we start at 0) then reset our animation rows
        if(currentRow >= spriteSheetRows){
            currentRow = 0;
        }

        // define the "cookie cutter" sourceRectangle for our sprite sheet
        this.sourceRect.left = frameColumn * spriteWidth;
        this.sourceRect.right = this.sourceRect.left + spriteWidth;
        this.sourceRect.top = currentRow * spriteHeight;
        this.sourceRect.bottom = this.sourceRect.top + spriteHeight;
        Log.d(TAG, "Top coordinates = " + currentRow);
    }
}

我的目标,因为我不是艺术家,是使用预渲染的精灵表,以便我可以在2D动画环境中处理我的技能。问题是,我发现的大多数精灵表似乎都有可变帧,这对我来说相当无用,除非我能找到一种更精确地切割它们的方法(或者其他切割方法,是否有我缺少的API工具? )

2 个答案:

答案 0 :(得分:0)

您可以添加一些元数据以与精灵表一起定义像素位置加上工作表中每个帧的宽度/高度。这比完美的网格要多得多,但它可以让你按原样使用更多种类的精灵表。例如(不按比例)如果你去获取第1帧,你会发现从像素位置128开始它是16x32,你可以根据整个工作表的宽度/高度来计算纹理坐标。这听起来很混乱,但一旦你开始工作,它应该会给你更大的灵活性,并且可以在各种精灵表中重复使用。

否则,您可以使用Gimp将单个帧剪切并粘贴到网格上。或者,将帧剪切成单独的图像,所有图像都具有相同的宽度/高度,并使用像ImageMagick之类的东西将各个图像组合成一个精灵表。我不记得实际的命令,但我使用命令行中的ImageMagick将一些Reiner的Tilesets组合成精灵表。 ImageMagick将连接目录中的所有图像,您甚至可以告诉它为您调整大小,以便您可以从更高的res图像开始,并在需要时强制它们为2的幂。

答案 1 :(得分:0)

您可以将精灵存储为Rect的数组。 (说真的,你不想把它们存储在2D数组中。)

Rect[] frames = new Rect[TOTAL_NUMBER_OF_FRAMES];

现在,计算机无法知道如何剪切工作表。最简单的方法是使文本文件看起来像:

x,y,width,height
x,y,width,height
x,y,width,height

这将允许您使用具有不同宽度和高度的奇怪的spritesheets。这也意味着您可以拥有无​​精灵的图像位(例如,当每个帧周围有边框或您不想使用的帧时)。使用文件IO将它们放入数组中。 (注意,您可能希望将BitMapFactory选项设置为不缩放位图)。

您可以使用以下方式绘制第n帧:

canvas.drawBitmap(bmp, frames[n],
    new RectF(x, y, x + frames[n].width(), y + frames[n].height()),
    paint);