Сделать анимацию более плавной - PullRequest
0 голосов
/ 28 января 2019

Я пытаюсь создать анимацию (парень, который запускается при нажатии клавиши) с использованием 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. Могу ли я добавить таймер неверным способом?Может быть, что-то вроде задержки.Я не знаю, как именно это работает, и какой слушатель я должен упомянуть в качестве аргумента в таймере.PS Я только начинающий, и мой код может выглядеть неправильно с точки зрения стандартов кодирования.Также я написал только важные части моего проекта, которые представляют проблему.Я надеюсь, вы поможете мне с этим.Заранее спасибо.

1 Ответ

0 голосов
/ 28 января 2019

Анимация - сложный предмет, с большим количеством скучной теории.По сути, анимация - это иллюзия изменений во времени.Это очень важно, так как все, что вы делаете в анимации, будет основываться на времени.

В чем-то вроде игры у вас будет множество сущностей, играющих с разной скоростью.Одна из проблем заключается в том, чтобы найти время, чтобы разработать решение, которое позволит объекту воспроизводиться в течение определенного периода времени, пока он не связан с циклом обновления (т. Е. Счетчиком кадров), если только у вас нет спрайта с правильным количеством кадров, которое соответствует обновлению.цикл, но даже тогда, я бы был обеспокоен, так как система не будет достаточно гибкой, чтобы адаптироваться к ситуациям, когда ОС и оборудование не справляются.

Ниже приведен простой пример, который требуетлист спрайтов (серия изображений, хранящихся в одном изображении), количество ожидаемых изображений / кадров и время для завершения полного цикла.

Он рассчитывает размер отдельного кадра и возвращает кадр на основе времени, в течение которого спрайт был анимирован ...

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 миллисекунд), который постоянно обновляет интерфейс, запрашивая следующий кадр у спрайта и рисуя его.

Важноечасть здесь спрайт и цикл обновления являются независимыми.Хотите, чтобы персонаж шел быстрее, измените продолжительность спрайта, хотите, чтобы персонаж шел медленнее, изменили продолжительность спрайта, цикл обновления не нужно изменять (или любой другой объект)

Итак, начиная с ...

Sprite Sheet

Тот же цикл, сначала более 1 секунды, второй более 5 секунд

FastwalkSlowWalk

Вы также можете взглянуть на что-то вроде Как создать пригодный для использования метод KeyReleased в Java , который демонстрирует использование привязок клавиш и централизованное Set как репозиторий "action"

...