Анимация - сложный предмет, с большим количеством скучной теории.По сути, анимация - это иллюзия изменений во времени.Это очень важно, так как все, что вы делаете в анимации, будет основываться на времени.
В чем-то вроде игры у вас будет множество сущностей, играющих с разной скоростью.Одна из проблем заключается в том, чтобы найти время, чтобы разработать решение, которое позволит объекту воспроизводиться в течение определенного периода времени, пока он не связан с циклом обновления (т. Е. Счетчиком кадров), если только у вас нет спрайта с правильным количеством кадров, которое соответствует обновлению.цикл, но даже тогда, я бы был обеспокоен, так как система не будет достаточно гибкой, чтобы адаптироваться к ситуациям, когда ОС и оборудование не справляются.
Ниже приведен простой пример, который требуетлист спрайтов (серия изображений, хранящихся в одном изображении), количество ожидаемых изображений / кадров и время для завершения полного цикла.
Он рассчитывает размер отдельного кадра и возвращает кадр на основе времени, в течение которого спрайт был анимирован ...
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 миллисекунд), который постоянно обновляет интерфейс, запрашивая следующий кадр у спрайта и рисуя его.
Важноечасть здесь спрайт и цикл обновления являются независимыми.Хотите, чтобы персонаж шел быстрее, измените продолжительность спрайта, хотите, чтобы персонаж шел медленнее, изменили продолжительность спрайта, цикл обновления не нужно изменять (или любой другой объект)
Итак, начиная с ...
Тот же цикл, сначала более 1 секунды, второй более 5 секунд
Вы также можете взглянуть на что-то вроде Как создать пригодный для использования метод KeyReleased в Java , который демонстрирует использование привязок клавиш и централизованное Set
как репозиторий "action"