【问题标题】:How To Remove The Last Image In An Animation如何删除动画中的最后一个图像
【发布时间】:2014-04-11 12:10:13
【问题描述】:

我想要做的是创建一个创建“跑步”动作的动画。每当我在屏幕上绘制它时,动画中的最后一帧都会被留下(所以当精灵移动时会留下一连串动画帧)。我尝试了 if 语句并在框架更改时更改图像的绘制位置:

if(a2.sceneNum() == 0)
spectre_Draw1 = (screenWidth() / 2 - 120 / 2 + 120 - 6);
else
spectre_Draw1 = 0;

g.drawImage(pic[2], spectre_Draw1, (screenHeight() / 2 - 180 / 2), null);

if(a2.sceneNum() == 1)
spectre_Draw2 = (screenWidth() / 2 - 120 / 2 + 120 - 6);
else
spectre_Draw2 = 0;

g.drawImage(pic[3], spectre_Draw2, (screenHeight() / 2 - 180 / 2), null);

if(a2.sceneNum() == 2)
spectre_Draw3 = (screenWidth() / 2 - 120 / 2 + 120 - 6);
else
spectre_Draw3 = 0;

g.drawImage(pic[4], spectre_Draw3, (screenHeight() / 2 - 180 / 2), null);

有没有办法在删除尾随图像时做到这一点?

【问题讨论】:

  • 您记得在您的paint/paintComponent 方法中调用super.paint/paintComponent 吗?除此之外,我说发布一个我们可以测试的可运行示例。
  • @peeskillet 我刚刚添加了它,它继续做同样的事情。在发布可运行的示例之前,我将再测试一件事。
  • 请注意,您没有在else 条件周围使用{} 大括号,这意味着每次都会绘制每个图像...

标签: java image animation graphics awt


【解决方案1】:

我知道上面写着“避免回复其他答案”,但作为新注册者(但长期用户),我只能写“答案”或建议编辑。我不能添加 cmets 回答,直到我被投票! :-)

但是,peeskillet 的答案中的源代码错误太多,无法简单地称为“建议的编辑”:-)

  1. ROWS 和 COLUMNS 的意外交换/混淆:

        SPRITE_ROWS = 5;
        SPRITE_COLUMNS = 2;
    
        DIM_W = img.getWidth() / SPRITE_ROWS;
        DIM_H = img.getHeight() / SPRITE_COLUMNS;
    

再往下看,该程序显然试图正确处理网格中动画图片的行和列,但这只是因为两者都是向后的,程序才能工作。 (即 nerdgirl 示例的图像网格中有 5 个和 2 个)。

  1. DIM_H 和 DIM_W 的意外互换(1 次):

                if (x1Src >= img.getWidth() - DIM_H - 5) {  // 5 to take care of precisi
    

    “DIM_H”应该是“DIM_W”

如所列,如果单个图像的高度明显高于宽度,或否则原始网格的每一行都有很多图像。对于 nerdgirl 样本 (833 x 639),如果整个网格仅高 10 像素,则不会显示每行中的最后一张图像。 (DIM_H = 319,img.getWidth()-DIM_H-5 = 503,最后一帧显示 x1Src = 498 ..刚刚成功!)

  1. 当前代码所示仅处理 2 行动画帧(即使认为它通过将例如 SPRITE_COLUMNS (sic) 更改为 4 来正确计算每个帧的 SIZE 以用于正在运行的 Man 示例)。

这是因为下面的 if-test :

                    if (y1Src >= DIM_H - 5) { // 5 to take care of lack of prec
                        y1Src = 0;

...应该是:

                    if (y1Src >= imag.getHeight() - DIM_H - 5) { // 5 to take car
                        y1Src = 0;

..因为当前代码只显示 2 行精灵图像,即使有更多。 (这解释了为什么拍打的鸟样本“正常”但没有显示最终的全白帧……仔细看,它只显示前 2 行图像)。

  1. 使用硬编码 5 表示“精度损失”(意味着当源图像没有均匀地组合到组合网格中并且它们不是帧大小的精确倍数时)实际上应该是指帧数现在:

                if (x1Src >= img.getWidth() - DIM_W - SPRITE_COLUMNS) {  // - SPRITE_COLUMNS to take care of non-aligned source images (see samples)
    
                if (y1Src >= img.getHeight() - DIM_H - SPRITE_ROWS) {  // - SPRITE_ROWS to take care of non-aligned source images (see samples)
    

这是因为可能的“精度损失”在每行和每列中每帧不超过 1 - 由计算动画图像宽度和高度时发生的整数舍入导致。例如,833 x 639 的 nerdgirl 样本计算出的动画帧大小为 166 x 319,因此在 5 张图像之后,我们显示了高达 830,638 的原始图像。

但是,我建议以下内容将使这两个 if 语句更加清晰和简单:

                if (x2Src > img.getWidth()) {     // Beyond end of Row

                if (y2Src > img.getHeight()) {    // Beyond Last Row

...还有一些其他的小调整,如下所示。

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class NerdGirl extends JPanel {
    private static int SPRITE_COLUMNS = 5;         // Number of columns of sprite images in source file
    private static int SPRITE_ROWS    = 2;         // Number of rows    of sprite images in source file
    private static final int DELAY    = 100;       // inter-frame pause in milliseconds

    private int dim_W;                             // Width of an individual animation frame
    private int dim_H;                             // Height of an individual animation frame

    private int x1Src;                             // top-left coordinates of current frame
    private int y1Src;
    private int x2Src;                             //bottom-right coordinates of current frame
    private int y2Src;

    private BufferedImage img;

    public NerdGirl() {
        try {
            img = ImageIO.read(getClass().getResource("images/nerdgirl.jpg"));
            SPRITE_ROWS = 2;
            // Other sample files
            //img = ImageIO.read(getClass().getResource("images/explosion.png"));
            //SPRITE_ROWS = 3;
            //img = ImageIO.read(getClass().getResource("images/birdflight.png"));
            //SPRITE_ROWS = 3;
            //img = ImageIO.read(getClass().getResource("images/running_man.png"));
            //SPRITE_ROWS    = 4;
            //SPRITE_COLUMNS = 6;

        } catch (IOException ex) {
            Logger.getLogger(NerdGirl.class.getName()).log(Level.SEVERE, null, ex);
        }
        dim_W = img.getWidth()  / SPRITE_ROWS;
        dim_H = img.getHeight() / SPRITE_COLUMNS;
        x1Src = 0;
        y1Src = 0;
        x2Src = x1Src + dim_W;
        y2Src = y1Src + dim_H;

        Timer timer = new Timer(DELAY, new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                // Move right to next frame (next column)
                x1Src += dim_W;
                x2Src += dim_W;

                if (x2Src > img.getWidth()) {  // Past end of current row.
                    x1Src = 0;                  // Back to start of row
                    x2Src = dim_W;

                    y1Src += dim_H;             // Move down to next Row
                    y2Src += dim_H;
                    if (y2Src > img.getHeight()) { // Below bottom of source grid of images
                        y1Src = 0;              // Back to Top.
                        y2Src = dim_H;
                    }
                }

                repaint();
            }
        });
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, getWidth(), getHeight(), x1Src, y1Src, x2Src, y2Src, this);
    }

    @Override
    public Dimension getPreferredSize() {
        return (img == null) ? new Dimension(300, 300) : new Dimension(dim_W, dim_H);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new NerdGirl());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
  1. 最后,请注意,与这些问题无关,示例动画源网格的组合很差(精灵帧对齐并不总是一致),因此在本页的示例中,您有时会看到相邻帧的碎片显示。仔细观看上面的鸟框动画,并查看说明图herehere。)

如果网格全部编译为单个帧大小的整数倍(如运行 man png 图像@900x600 的情况 - 即使提供的示例具有图片“重叠” - 请参阅上面的图片链接) ,那么坐标检查的程序代码会更容易:

        Timer timer = new Timer(DELAY, new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                // Move right to next frame (next column)
                x1Src += dim_W;

                if (x1Src == img.getWidth()) {  // At end of current row.
                    x1Src = 0;                  // Go Back to start of row
                    y1Src += dim_H;             // Move down to next Row

                    if (y1Src == img.getHeight()) { // Past all images
                        y1Src = 0;              // Back to Top.
                    }
                }

                x2Src += dim_W;
                y2Src += dim_H;

                repaint();
            }
        });
        timer.start();
    }

但是,由于输入样本网格的整体大小和组成不可靠,因此建议使用较早的代码,因为它可以处理这些不一致。

我希望我的回答从长远来看可以为其他人节省宝贵的时间!

干杯,

【讨论】:

  • 鉴于您知道的限制,我建议您编辑您的答案以删除对他人的具体引用,并通过关于不该做什么的一般建议将这些内容纳入您的答案。
  • 是的,我知道存在一些逻辑错误,但一直没有解决它们。感谢您花时间这样做!欢迎来到 Stackoverflow!
  • @peeskillet。感谢并很高兴能够改进您的有用产品。干杯,瓦兹。 :-)
【解决方案2】:

注意:示例程序中的以下代码存在一些逻辑错误。请参阅answer from Warren K 以获得解释和修复

我注意到您正在尝试为动画图像使用不同的图像。你知道你可以使用单个动画精灵(假设它被格式化为类似网格的模式)并且只需在 drawImage 方法中更改某些 x y 位置的位置

public abstract boolean drawImage(Image img,
            int dx1,
            int dy1,
            int dx2,
            int dy2,
            int sx1,
            int sy1,
            int sx2,
            int sy2,
            ImageObserver observer)
img - the specified image to be drawn. This method does nothing if img is null.
dx1 - the x coordinate of the first corner of the destination rectangle.
dy1 - the y coordinate of the first corner of the destination rectangle.
dx2 - the x coordinate of the second corner of the destination rectangle.
dy2 - the y coordinate of the second corner of the destination rectangle.
sx1 - the x coordinate of the first corner of the source rectangle.
sy1 - the y coordinate of the first corner of the source rectangle.
sx2 - the x coordinate of the second corner of the source rectangle.
sy2 - the y coordinate of the second corner of the source rectangle.
observer - object to be notified as more of the image is scaled and converted.

查看完整描述API

话虽如此,您可以使用javax.swing.Timer 为源图像设置动画并更改其位置。

对于您在下面看到的所有示例,这里有一些使用相同代码的示例。我只是更改了图像并相应地更改了SPRITE_ROWSSPRITE_COLUMNSDELAY。更多信息请访问How to Use Swing Times

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class NerdGirl extends JPanel {
    private static final int SPRITE_ROWS = 5;
    private static final int SPRITE_COLUMNS = 2;
    private static final int DELAY = 150;

    private int DIM_W;
    private int DIM_H;

    private int x1Src;
    private int y1Src;
    private int x2Src;
    private int y2Src;

    private BufferedImage img;

    public NerdGirl() {
        try {
            img = ImageIO.read(getClass().getResource("/resources/nerd-girl.jpg"));
        } catch (IOException ex) {
            Logger.getLogger(NerdGirl.class.getName()).log(Level.SEVERE, null, ex);
        }
        DIM_W = img.getWidth() / SPRITE_ROWS;
        DIM_H = img.getHeight() / SPRITE_COLUMNS;
        x1Src = 0;
        y1Src = 0;
        x2Src = x1Src + DIM_W;
        y2Src = y1Src + DIM_H;
        Timer timer = new Timer(DELAY, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (x1Src >= img.getWidth() - DIM_H - 5) {  // 5 to take care of precision loss
                    x1Src = 0;
                    x2Src = x1Src + DIM_W;
                    if (y1Src >= DIM_H - 5) { // 5 to take care of precision loss
                        y1Src = 0;
                        y2Src = y1Src + DIM_H;
                    } else {
                        y1Src += DIM_H;
                        y2Src = y1Src + DIM_H;
                    }

                } else {
                    x1Src += DIM_W;
                    x2Src = x1Src + DIM_W;
                }

                repaint();
            }
        });
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, getWidth(), getHeight(), x1Src, y1Src, x2Src, y2Src, this);
    }

    @Override
    public Dimension getPreferredSize() {
        return (img == null) ? new Dimension(300, 300) : new Dimension(DIM_W, DIM_H);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new NerdGirl());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

【讨论】:

    【解决方案3】:

    如果有人仍然有这个问题,我找到了答案,您可以使用paintComponent(),或者如果您坚持使用该功能,您可以在循环或计时器中为您的内容窗格使用repaint()。如下代码:

    public void run(){
        getContentPane().repaint();
    }
    

    【讨论】:

      猜你喜欢
      • 2015-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-15
      • 2018-10-31
      • 2020-10-01
      • 1970-01-01
      相关资源
      最近更新 更多