当前位置: 首页 > 知识库问答 >
问题:

让动画更流畅

谷森
2023-03-14

我试图使用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的类。一切似乎都很好,我的动画作品,但问题是,它的工作不像我想要的那样顺利。当我按住键时,图像变化太快,过程看起来不自然。我希望,例如,每秒3或4个图像改变,而不是20个。我是否可以用错误的方法添加计时器?可能是有时间延迟之类的。我不知道它到底是如何工作的,也不知道我应该在定时器中提到哪个监听器作为参数。附言。我只是初学者,我的代码在编码标准方面可能看起来不正确。另外,我写的只是我的项目的关键部分,代表问题。我希望你能帮我。提前谢了。

共有1个答案

胡俊贤
2023-03-14

动画是一门复杂的学科,有很多枯燥的理论。基本上,动画是随着时间变化的幻觉。这是非常重要的,因为你在动画中所做的一切都将基于时间。

在一个游戏中,你会有一堆实体以不同的时间速率玩。挑战之一是花时间设计一个解决方案,允许一个实体在与刷新周期(即帧计数)解耦的同时播放一段时间,除非你有与刷新周期匹配的正确帧数的雪碧,但即使这样,我也会担心,因为系统将不够灵活,无法适应操作系统和硬件跟不上的情况。

下面是一个简单的示例,它使用了一个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());
    }

}

注意:它还需要一个重置或停止的方法,这样你就可以强迫雪碧回到开始,但我会留给你

接下来,我们需要一些方法来播放动画

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定时,设置为高分辨率(5毫秒),它不断更新UI,从sprite请求下一帧并绘制它。

 类似资料:
  • 在1981年,迪士尼长期动画师Ollie Johnston 和 Frank Thomas(迪士尼九大元老的成员——1920年代和1930年代迪士尼的原始动画团队)写了一本名为《Disney Animation: The Illusion of Life》的书,概括了他们在迪士尼早期卡通工作中倡导人物动画的基本原则。 这12个基本动画原则在《Disney Animation: The Illusio

  • 我用html、jquery和css动画构建了一个“粘性头”。但有一件小事不能正常工作。 在这种情况下,棒头将是活动的,菜单的背景颜色从白色切换到绿色(带有动画)。 现在我需要一个函数,它将颜色从绿色切换回白色(与动画),如果头不再粘(=类粘将移除)。 我怎么才能意识到这一点呢? null null

  • 让游戏对象动起来 一旦你保存了新动画剪辑,就可以开始为动画剪辑添加关键帧了。开始为选中的游戏对象编辑动画剪辑之前,需要先点击动画记录按钮,进入动画记录模式,此时,对该游戏对象的修改会被记录到动画剪辑。 记录按钮 任何时候,你都可以通过再次点击 动画模式按钮,从而退出 动画记录模式。这一操作将把该 游戏对象 的状态恢复到进入动画记录模式之前。 你对该游戏对象所做的修改将被记录为关键帧,红线所指示的时

  • 问题内容: 我正在尝试从屏幕的底部到中间在图像视图上进行翻译动画。动画完成后,我希望图像视图停留在那里。我不要setFillAfter(true),因为我想更新imageview的实际位置。 目前,我通过具有2个图像视图(在动画开始时一个,在结束时一个)来做到这一点,并且我使用setVisibility来实现此目的。这是做事的正确方法吗?这是我使用的代码: 问题答案: 然后,必须为要设置动画的Vi

  • 我错过了什么? 为了便于阅读,这里提供了一个要点,并附带了一个测试用例:https://Gist.github.com/teyc/5668517

  • 动画剪辑 动画剪辑是 Unity 动画系统的核心元素。Unity 不仅支持从外部源导入动画,而且支持在编辑器的动画视图中创建和编辑动画(剪辑)。 从外部源导入动画 从外部源导入的动画剪辑可能包括: 动作捕捉工作室捕捉的人形动画 设计师通过外部 3D 程序(例如 3D Max 或 Maya)创建的动画 第三方的动画集合库(例如来自 Unity Asset store) 导入的单个时间线等分切割为多个