让动画更流畅
Make animation more smoother
我正在尝试使用 JPanel 和 painComponent 创建一些动画(只要按下键就会运行的男孩)。
所以,首先我声明了一些图像、图像数组和一些绘图方法。我创建了计时器并将其添加到构造函数中。
Image img1;
Image img2;
Image img3;
Image img4;
int index = 0;
Image currentImage l;
Image[] images = new Image[4]
public class Animation extends JPanel implements ActionListener
{
public Animation()
{
loadImages();
getTimer();
imgAdder();
addFirstImage();
}
public void serCurrentImage(Image currentImage)
{
this.currentImage = currentImage;
}
public void getTimer()
{
Timer timer = new Timer(20,this)
timer.start();
}
public void imgAdder()
{
images[1] = img1;
...
}
public void addFirstImage()
{
currentImage = img1;
}
private void loadImages()
{
try
{
BufferedImage img = ImageIO.read(getClass.getResource(“/resources/images/img1.png”);
img1 = img;
// and so on for every image
}catch(IOexception ioe )
ioe.printStackTrace();
}
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.drewImage(currentImage,0,0,this);
requestsFocus();
}
public class FieldKeyListener extends KeyAdapter
{
public void move()
{
setCurrentImage(image[index]);
index++;
if( index == 4 )
index = 0;
}
public void keyPressed(KeyEvent e)
{
super.keyPressed(e);
int key = e.getKeyCode();
if(key == Key.Event.VK_LEFT)
move();
}
}
}
然后使用数组循环通过 paintComponent 绘制所有图像。
我还声明了扩展 KeyAdapter 的 class。
一切似乎都很好,我的动画也能正常工作,但问题是它的工作并不像我想要的那样顺利。当我按住 键时,图像变化太快,过程看起来不自然。例如,我想要每秒更改 3 或 4 个图像而不是 20 个。
我可以用错误的方法添加计时器吗?可能有时间延迟之类的东西。我不知道它到底是如何工作的,我应该提到哪个监听器作为定时器中的参数。
P.s。我只是初学者,我的代码在编码标准方面可能看起来不正确。此外,我只写了项目中代表问题的关键部分。我希望你能帮我解决这个问题。提前致谢。
动画是一门复杂的学科,有很多枯燥的理论。基本上,动画是随时间变化的幻觉。这非常重要,因为您在动画中所做的一切都将基于时间。
在游戏之类的东西中,您将拥有一堆实体,它们都以不同的时间速率进行游戏。挑战之一是花时间设计一个解决方案,允许实体在与刷新周期(即帧数)分离的同时播放一段时间,除非你有正确帧数的精灵来匹配你的刷新循环,但即便如此,我还是担心,因为系统不够灵活,无法适应 OS 和硬件跟不上的情况。
下面是一个简单的示例,它采用精灵 sheet(存储在单个图像中的一系列图像)、预期的数量 images/frames 和完成一个完整周期的时间。
它计算单个帧大小和 returns 一个基于 sprite 动画的时间量的帧...
public class Sprite {
private BufferedImage source;
private int imageCount;
private int imageWidth;
// How long it takes to play a full cycle
private Duration duration;
// When the last cycle was started
private Instant startedAt;
public Sprite(BufferedImage source, int imageCount, int cycleTimeInSeconds) throws IOException {
this.source = source;
this.imageCount = imageCount;
imageWidth = source.getWidth() / imageCount;
duration = Duration.ofSeconds(cycleTimeInSeconds);
}
public BufferedImage getFrame() {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration timePlayed = Duration.between(startedAt, Instant.now());
double progress = timePlayed.toMillis() / (double)duration.toMillis();
if (progress > 1.0) {
progress = 1.0;
startedAt = Instant.now();
}
int frame = Math.min((int)(imageCount * progress), imageCount - 1);
return getImageAt(frame);
}
protected BufferedImage getImageAt(int index) {
if (index < 0 || index >= imageCount) {
return null;
}
int xOffset = imageWidth * index;
return source.getSubimage(xOffset, 0, imageWidth, source.getHeight());
}
}
nb:它还需要一种重置或停止的方法,这样你就可以强制精灵回到开始,但我会把它留给你
接下来,我们需要一些方法来播放动画
public class TestPane extends JPanel {
private Sprite sprite;
public TestPane(Sprite sprite) {
this.sprite = sprite;
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
BufferedImage img = sprite.getFrame();
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
这里没有什么特别的,它是一个简单的 Swing Timer
设置为高分辨率(5 毫秒),不断更新 UI,从精灵请求下一帧并绘制它。
这里重要的部分是精灵和刷新周期是独立的。想要角色走得更快,改变精灵持续时间,想要角色走得更慢,改变精灵持续时间,刷新周期不需要改变(或任何其他实体)
所以,从...开始...
相同的循环,第一次超过 1 秒,第二次超过 5 秒
您还可以查看类似 How to create a usable KeyReleased method in java 的内容,它演示了键绑定和集中式 Set
作为 "action" 存储库的使用
我正在尝试使用 JPanel 和 painComponent 创建一些动画(只要按下键就会运行的男孩)。 所以,首先我声明了一些图像、图像数组和一些绘图方法。我创建了计时器并将其添加到构造函数中。
Image img1;
Image img2;
Image img3;
Image img4;
int index = 0;
Image currentImage l;
Image[] images = new Image[4]
public class Animation extends JPanel implements ActionListener
{
public Animation()
{
loadImages();
getTimer();
imgAdder();
addFirstImage();
}
public void serCurrentImage(Image currentImage)
{
this.currentImage = currentImage;
}
public void getTimer()
{
Timer timer = new Timer(20,this)
timer.start();
}
public void imgAdder()
{
images[1] = img1;
...
}
public void addFirstImage()
{
currentImage = img1;
}
private void loadImages()
{
try
{
BufferedImage img = ImageIO.read(getClass.getResource(“/resources/images/img1.png”);
img1 = img;
// and so on for every image
}catch(IOexception ioe )
ioe.printStackTrace();
}
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.drewImage(currentImage,0,0,this);
requestsFocus();
}
public class FieldKeyListener extends KeyAdapter
{
public void move()
{
setCurrentImage(image[index]);
index++;
if( index == 4 )
index = 0;
}
public void keyPressed(KeyEvent e)
{
super.keyPressed(e);
int key = e.getKeyCode();
if(key == Key.Event.VK_LEFT)
move();
}
}
}
然后使用数组循环通过 paintComponent 绘制所有图像。 我还声明了扩展 KeyAdapter 的 class。 一切似乎都很好,我的动画也能正常工作,但问题是它的工作并不像我想要的那样顺利。当我按住 键时,图像变化太快,过程看起来不自然。例如,我想要每秒更改 3 或 4 个图像而不是 20 个。 我可以用错误的方法添加计时器吗?可能有时间延迟之类的东西。我不知道它到底是如何工作的,我应该提到哪个监听器作为定时器中的参数。 P.s。我只是初学者,我的代码在编码标准方面可能看起来不正确。此外,我只写了项目中代表问题的关键部分。我希望你能帮我解决这个问题。提前致谢。
动画是一门复杂的学科,有很多枯燥的理论。基本上,动画是随时间变化的幻觉。这非常重要,因为您在动画中所做的一切都将基于时间。
在游戏之类的东西中,您将拥有一堆实体,它们都以不同的时间速率进行游戏。挑战之一是花时间设计一个解决方案,允许实体在与刷新周期(即帧数)分离的同时播放一段时间,除非你有正确帧数的精灵来匹配你的刷新循环,但即便如此,我还是担心,因为系统不够灵活,无法适应 OS 和硬件跟不上的情况。
下面是一个简单的示例,它采用精灵 sheet(存储在单个图像中的一系列图像)、预期的数量 images/frames 和完成一个完整周期的时间。
它计算单个帧大小和 returns 一个基于 sprite 动画的时间量的帧...
public class Sprite {
private BufferedImage source;
private int imageCount;
private int imageWidth;
// How long it takes to play a full cycle
private Duration duration;
// When the last cycle was started
private Instant startedAt;
public Sprite(BufferedImage source, int imageCount, int cycleTimeInSeconds) throws IOException {
this.source = source;
this.imageCount = imageCount;
imageWidth = source.getWidth() / imageCount;
duration = Duration.ofSeconds(cycleTimeInSeconds);
}
public BufferedImage getFrame() {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration timePlayed = Duration.between(startedAt, Instant.now());
double progress = timePlayed.toMillis() / (double)duration.toMillis();
if (progress > 1.0) {
progress = 1.0;
startedAt = Instant.now();
}
int frame = Math.min((int)(imageCount * progress), imageCount - 1);
return getImageAt(frame);
}
protected BufferedImage getImageAt(int index) {
if (index < 0 || index >= imageCount) {
return null;
}
int xOffset = imageWidth * index;
return source.getSubimage(xOffset, 0, imageWidth, source.getHeight());
}
}
nb:它还需要一种重置或停止的方法,这样你就可以强制精灵回到开始,但我会把它留给你
接下来,我们需要一些方法来播放动画
public class TestPane extends JPanel {
private Sprite sprite;
public TestPane(Sprite sprite) {
this.sprite = sprite;
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
BufferedImage img = sprite.getFrame();
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
这里没有什么特别的,它是一个简单的 Swing Timer
设置为高分辨率(5 毫秒),不断更新 UI,从精灵请求下一帧并绘制它。
这里重要的部分是精灵和刷新周期是独立的。想要角色走得更快,改变精灵持续时间,想要角色走得更慢,改变精灵持续时间,刷新周期不需要改变(或任何其他实体)
所以,从...开始...
相同的循环,第一次超过 1 秒,第二次超过 5 秒
您还可以查看类似 How to create a usable KeyReleased method in java 的内容,它演示了键绑定和集中式 Set
作为 "action" 存储库的使用