我正在尝试使用某种绘制方法将一个精灵图像绘制到我的JPanel子类AnimationPanel。我创建了一个Spritesheet类,该类可以生成一个BufferedImage [],其中包含工作表中的所有Sprite。在实现Runnable的AnimationPanel类中,我从在AnimationPanel构造函数中实例化的Spritesheet中获得了BufferedImage []。我希望能够遍历此数组并将每个子画面显示到屏幕上。我该怎么做?这是我的AnimationPanel和Spritesheet类。

AnimationPanel

package com.kahl.animation;

import javax.swing.JPanel;

public class AnimationPanel extends JPanel implements Runnable {

//Instance Variables
private Spritesheet sheet;
private int currentFrame;
private Thread animationThread;
private BufferedImage image;

public AnimationPanel(Spritesheet aSheet) {
    super();
    sheet = aSheet;
    setPreferredSize(new Dimension(128,128));
    setFocusable(true);
    requestFocus();

}

public void run() {
    BufferedImage[] frames = sheet.getAllSprites();
    currentFrame = 0;
    while (true) {
        frames[currentFrame].draw(); //some implementation still necessary here
        currentFrame++;
        if (currentFrame >= frames.length) {
            currentFrame = 0;
        }
    }
}

public void addNotify() {
    super.addNotify();
    if (animationThread == null) {
        animationThread = new Thread(this);
        animationThread.start();
    }
}

}


Spritesheet

package com.kahl.animation;

import java.awt.image.BufferedImage;
import java.imageio.ImageIO;
import java.io.IOException;
import java.io.File;

public class Spritesheet {

//Instance Variables
private String path;
private int frameWidth;
private int frameHeight;
private int framesPerRow;
private int frames;
private BufferedImage sheet = null;

//Constructors
public Spritesheet(String aPath,int width,int height,int fpr, int numOfFrames) {

    path = aPath;
    frameWidth = width;
    frameHeight = height;
    framesPerRow = fpr;
    frames = numOfFrames;

    try {
        sheet = ImageIO.read(getClass().getResourceAsStream());
    } catch (IOException e) {
        e.printStackTrace();
    }

}

//Methods

public int getHeight() {
    return frameWidth;
}

public int getWidth() {
    return frameWidth;
}

public int getFramesPerRow() {
    return framesPerRow;
}

private BufferedImage getSprite(int x, int y, int h, int w) {
    BufferedImage sprite = sheet.getSubimage(x,y,h,w);
}

public BufferedImage[] getAllSprites() {
    BufferedImage[] sprites = new BufferedImage[frames];
    int y = 0;
    for (int i = 0; i < frames; i++) {
        x = i * frameWidth;
        currentSprite = sheet.getSprite(x,y,frameHeight,frameWidth);
        sprites.add(currentSprite);
    }
    return sprites;

}

}

最佳答案

我鼓励使用javax.swing.Timer控制帧速率,而不是不受控制的循环
一旦计时器“滴答”,您需要增加当前帧,获取要渲染的当前图像,然后在repaint上调用JPanel
使用Graphics#drawImage渲染图像。


看到...


Painting in AWT and Swing
Performing Custom Painting
How to use Swing Timers
Graphics#drawImage(Image, int, int, ImageObserver)


更多细节

您的Spritesheet类存在一系列级联的问题,除了它实际上不会编译之外,还存在一些问题,您需要从某些方法返回错误的值并依赖能够更好地计算出的值...

我不得不对您的代码进行太多修改,我记不起来大多数

public int getHeight() {
    return frameWidth;
}




public BufferedImage[] getAllSprites() {
    BufferedImage[] sprites = new BufferedImage[frames];
    int y = 0;
    for (int i = 0; i < frames; i++) {
        x = i * frameWidth;
        currentSprite = sheet.getSprite(x,y,frameHeight,frameWidth);
        sprites.add(currentSprite);
    }
    return sprites;

}


作为两个主要例子脱颖而出...



import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestSpriteSheet {

    public static void main(String[] args) {
        new TestSpriteSheet();
    }

    public TestSpriteSheet() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Spritesheet spritesheet;
        private BufferedImage currentFrame;
        private int frame;

        public TestPane() {
            spritesheet = new Spritesheet("/Sheet02.gif", 240, 220);
            Timer timer = new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    currentFrame = spritesheet.getSprite(frame % spritesheet.getFrameCount());
                    repaint();
                    frame++;
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(240, 220);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (currentFrame != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - currentFrame.getWidth()) / 2;
                int y = (getHeight() - currentFrame.getHeight()) / 2;
                g2d.drawImage(currentFrame, x, y, this);
                g2d.dispose();
            }
        }

    }

    public class Spritesheet {

//Instance Variables
        private String path;
        private int frameWidth;
        private int frameHeight;
        private BufferedImage sheet = null;
        private BufferedImage[] frameImages;

//Constructors
        public Spritesheet(String aPath, int width, int height) {

            path = aPath;
            frameWidth = width;
            frameHeight = height;

            try {
                sheet = ImageIO.read(getClass().getResourceAsStream(path));
                frameImages = getAllSprites();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        public BufferedImage getSprite(int frame) {
            return frameImages[frame];
        }

//Methods
        public int getHeight() {
            return frameHeight;
        }

        public int getWidth() {
            return frameWidth;
        }

        public int getColumnCount() {
            return sheet.getWidth() / getWidth();
        }

        public int getRowCount() {
            return sheet.getHeight() / getHeight();
        }

        public int getFrameCount() {
            int cols = getColumnCount();
            int rows = getRowCount();
            return cols * rows;
        }

        private BufferedImage getSprite(int x, int y, int h, int w) {
            BufferedImage sprite = sheet.getSubimage(x, y, h, w);
            return sprite;
        }

        public BufferedImage[] getAllSprites() {
            int cols = getColumnCount();
            int rows = getRowCount();
            int frameCount =  getFrameCount();
            BufferedImage[] sprites = new BufferedImage[frameCount];
            int index = 0;
            System.out.println("cols = " + cols);
            System.out.println("rows = " + rows);
            System.out.println("frameCount = " + frameCount);
            for (int row = 0; row < getRowCount(); row++) {
                for (int col = 0; col < getColumnCount(); col++) {
                    int x = col * getWidth();
                    int y = row * getHeight();
                    System.out.println(index + " " + x + "x" + y);
                    BufferedImage currentSprite = getSprite(x, y, getWidth(), getHeight());
                    sprites[index] = currentSprite;
                    index++;
                }
            }
            return sprites;

        }

    }
}


请记住,动画是随着时间变化的幻觉。您需要在动画的每一帧之间提供一个延迟,该延迟足够长以便用户识别它,但又要足够短以使动画看起来平滑。

在上面的示例中,我仅将100毫秒用作任意值。可以使用更像1000 / spritesheet.getFrameCount()的东西,这将为整个动画提供一整秒的时间(所有帧在一秒钟之内)。

对于较长或较短的动画,可能需要根据需要使用不同的值

10-07 20:52