Как я могу сделать объект полигона в Java пульсат (как чаша в игре Atari "Приключение") - PullRequest
0 голосов
/ 26 мая 2018

Это то, что у меня есть в моем paintComponent (большинство других вещей опущено, только то, что относится к объекту Item, называемому чашей, с полигональным полем, явные параметры оператора if не важны для этого вопроса) В настоящее время этоотображается как сплошной белый, потому что я установил цвет для всех 255, но я хочу, чтобы он постепенно плавно переходил к разным цветам, не стробировался, скорее как пульсирующий, но я действительно не знаю, как это называется.Я думал о замене явных параметров Color массивами, которые циклически перебирают числа в этом массиве и каким-то образом связывают его с TimerListener, но я новичок в графике, поэтому я не уверен, что это лучший способ сделать это.

public void paintComponent(Graphics g) {
Graphics2D sprite = (Graphics2D) g;

if (chalice.getHolding() == true || roomID == chalice.getRoomDroppedIn()) {
            sprite.setColor(new Color(255, 255, 255));
            sprite.fill(chalice.getPoly());
        }
}

1 Ответ

0 голосов
/ 26 мая 2018

Некоторые основные понятия ...

  • Пульсирующий эффект должен двигаться в двух направлениях, он должен постепенно исчезать и исчезать
  • Для того, чтобы узнать, сколько "эффекта"следует применять, две вещи должны быть известны.Во-первых, сколько времени занимает полный эффект для цикла (от полностью непрозрачного до полностью прозрачного и обратно) и как далеко проходит цикл анимации.

Это не простая вещь для управления, естьмного «информации о состоянии», которая должна управляться и поддерживаться, и обычно это делается отдельно от других эффектов или сущностей.

На мой взгляд, самое простое решение состоит в том, чтобы разработать какую-то «временную линию»который управляет ключевыми точками (ключевыми кадрами) вдоль временной шкалы, вычисляет расстояние между каждой точкой и значением, которое она представляет.

Сделайте шаг назад на секунду.Мы знаем, что при:

  • 0% мы хотим быть полностью непрозрачными
  • 50% мы хотим быть полностью прозрачными
  • 100% мы хотим быть полностью непрозрачными

Вышесказанное учитывает, что мы хотим «автоматически повернуть» анимацию.

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

TimeLine

Ниже приводится довольно простая концепция «временной шкалы».Он имеет Duration, время, в течение которого воспроизводится временная шкала, ключевые кадры, которые предоставляют ключевые значения по продолжительности временной шкалы и средства для вычисления определенного значения в определенной точке в течение срока службы временной шкалы.

Эта реализация также предоставляет возможность автоматического воспроизведения.То есть, если временная шкала воспроизводится «более», она указана Duration, а не останавливается, она автоматически сбрасывается и учитывает количество «более», как часть следующего цикла (аккуратно)

public class TimeLine {

    private Map<Float, KeyFrame> mapEvents;

    private Duration duration;
    private LocalDateTime startedAt;

    public TimeLine(Duration duration) {
        mapEvents = new TreeMap<>();
        this.duration = duration;
    }

    public void start() {
        startedAt = LocalDateTime.now();
    }

    public boolean isRunning() {
        return startedAt != null;
    }

    public float getValue() {
        if (startedAt == null) {
            return getValueAt(0.0f);
        }
        Duration runningTime = Duration.between(startedAt, LocalDateTime.now());
        if (runningTime.compareTo(duration) > 0) {
            runningTime = runningTime.minus(duration);
            startedAt = LocalDateTime.now().minus(runningTime);
        }
        long total = duration.toMillis();
        long remaining = duration.minus(runningTime).toMillis();
        float progress = remaining / (float) total;
        return getValueAt(progress);
    }

    public void add(float progress, float value) {
        mapEvents.put(progress, new KeyFrame(progress, value));
    }

    public float getValueAt(float progress) {

        if (progress < 0) {
            progress = 0;
        } else if (progress > 1) {
            progress = 1;
        }

        KeyFrame[] keyFrames = getKeyFramesBetween(progress);

        float max = keyFrames[1].progress - keyFrames[0].progress;
        float value = progress - keyFrames[0].progress;
        float weight = value / max;

        float blend = blend(keyFrames[0].getValue(), keyFrames[1].getValue(), 1f - weight);
        return blend;
    }

    public KeyFrame[] getKeyFramesBetween(float progress) {

        KeyFrame[] frames = new KeyFrame[2];
        int startAt = 0;
        Float[] keyFrames = mapEvents.keySet().toArray(new Float[mapEvents.size()]);
        while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
            startAt++;
        }

        if (startAt >= keyFrames.length) {
            startAt = keyFrames.length - 1;
        }

        frames[0] = mapEvents.get(keyFrames[startAt - 1]);
        frames[1] = mapEvents.get(keyFrames[startAt]);

        return frames;

    }

    protected float blend(float start, float end, float ratio) {
        float ir = (float) 1.0 - ratio;
        return (float) (start * ratio + end * ir);
    }

    public class KeyFrame {

        private float progress;
        private float value;

        public KeyFrame(float progress, float value) {
            this.progress = progress;
            this.value = value;
        }

        public float getProgress() {
            return progress;
        }

        public float getValue() {
            return value;
        }

        @Override
        public String toString() {
            return "KeyFrame progress = " + getProgress() + "; value = " + getValue();
        }

    }

}

Настройка временной шкалы довольно проста ...

timeLine = new TimeLine(Duration.ofSeconds(5));
timeLine.add(0.0f, 1.0f);
timeLine.add(0.5f, 0.0f);
timeLine.add(1.0f, 1.0f);

Мы даем указанное Duration и настраиваем значения ключевых кадров.После этого нам просто нужно «запустить» его и получить текущий value из TimeLine в зависимости от того, как долго он играет.

Это может показаться большой работой для того, что кажется простымпроблема, но помните, что это и динамично, и многократно используется.

Это динамично, так как вы можете предоставить любой Duration, который вы хотите, таким образом, изменяя скорость, он будет «просто работать», и повторноего можно использовать, так как вы можете создать несколько экземпляров для нескольких объектов, и он будет управляться независимо.

Пример ...

В следующем примере просто используется Swing Timer в качестве "основной цикл »для анимации.В каждом цикле он запрашивает TimeLine значение «current», которое просто действует как значение alpha для «пульсирующего» эффекта.

Сам класс TimeLine развязан настолько, что онне имеет значения, «как» вы устанавливаете свой «основной цикл», вы просто запускаете его и извлекаете из него «текущее» значение, когда это возможно ...

Pulse

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        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 TimeLine timeLine;
        private float alpha = 0;

        public TestPane() {
            timeLine = new TimeLine(Duration.ofSeconds(5));
            timeLine.add(0.0f, 1.0f);
            timeLine.add(0.5f, 0.0f);
            timeLine.add(1.0f, 1.0f);
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!timeLine.isRunning()) {
                        timeLine.start();
                    }
                    alpha = timeLine.getValue();
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
            g2d.setColor(Color.RED);
            g2d.fill(new Rectangle(45, 45, 110, 110));
            g2d.dispose();

            g2d = (Graphics2D) g.create();
            g2d.setColor(getBackground());
            g2d.fill(new Rectangle(50, 50, 100, 100));
            g2d.setColor(Color.BLACK);
            g2d.draw(new Rectangle(50, 50, 100, 100));
            g2d.dispose();
        }

    }
}

Я бы связал TimeLine как часть эффекта для указанной сущности.Это связало бы TimeLine с конкретной сущностью, а это означает, что многие сущности могут иметь свои собственные TimeLine s, вычисляющие проверку различных значений и эффектов

«Есть ли более простое решение?»

Это субъективный вопрос.«Может быть» может быть «более простой» подход, который будет выполнять ту же работу, но который не будет столь же масштабируемым или повторно используемым, как этот вид подхода.

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

Я играл с идеей сделать универсальный TimeLine, чтобы его можно было использовать для создания истинности различных значений на основена желаемый результат, что делает его гораздо более гибким и многократно используемым решением.

Смешивание цветов ....

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

Смешивание цветов несколько проблематично, я потратил много времени, пытаясьнайти достойный алгоритм, который работает для меня, который демонстрируется в алгоритм затухания цвета? и Java: плавный переход цвета

...