我正在尝试使用某种绘制方法将一个精灵图像绘制到我的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 TimersGraphics#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()
的东西,这将为整个动画提供一整秒的时间(所有帧在一秒钟之内)。对于较长或较短的动画,可能需要根据需要使用不同的值