Анимация по фоновому изображению

Я создал окно с большой JPanel, занимающей большую часть области, которая будет использоваться для анимации набора фейерверков. У меня есть частный внутренний класс AnimationPanel, который расширяет JPanel. Я переопределил paintComponent(), чтобы использовать буферизованное изображение в качестве фона. После нажатия кнопки запуска запустится симуляция, которая оживит набор фейерверков. Прямо сейчас эта симуляция выводит позиции на консоль и создает коллекцию, называемую моментальным снимком, содержащую все фейерверки в определенный момент времени.

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

Частный внутренний класс AnimationPanel

private class AnimationPanel extends JPanel {

    private BufferedImage background;

    public AnimationPanel() {
        super();
        try {
            background = ImageIO.read(new File("background.jpg"));
        } catch(IOException error) {
            error.printStackTrace();
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background,0,0,getWidth(),getHeight(),this); //this shows up
        g.setColor(Color.RED);
        g.fillRect(getWidth()/2, getHeight()-30, 10, 30); //this shows up
        paintFireWorks(g); //this doesn't show up
    }

    private void paintFireWorks(Graphics g) {
        if(snapshot != null) {
            for(Firework item: snapshot) {
                double[] position = item.getPosition();
                if(item instanceof Star){
                    int x = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int y = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int r = ((Star) item).getRenderSize();
                    g.fillOval(x,y,r,r);
                } else if(item instanceof Spark){
                    int x1 = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int x2 = (int)Math.round(position[0])-((Spark)item).getRenderSize();
                    int y1 = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int y2 = animationPanel.getHeight()-(int)Math.round(position[1])+((Spark)item).getRenderSize();
                    g.drawLine(x1,y1,x2,y2);
                }
            }
        }
    }

}

Весь класс FireWorksWindow:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class FireWorksWindow extends JFrame {

//DIMENSIONS AND POSITIONS
private final int STARTING_POS_X = 1600;
private final int STARTING_POS_Y = 100;

//CONTAINERS AND COMPONENTS
private AnimationPanel animationPanel;
private JPanel controlPanel, sliderPanel;
private Box controlBox;
private JLabel windSliderLabel, angleSliderLabel;
private JSlider windSpeed, launchAngle;
private JButton startButton, pauseButton, exitButton;

//TIMER
private Timer animationTimer;

//COLLECTIONS
ArrayList<Particle> fireworks;
ArrayList<Particle> snapshot;

//CONSTRUCTOR
public FireWorksWindow() {
    setTitle("Roman Candle Animation");
    setLocation(STARTING_POS_X,STARTING_POS_Y);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    sliderPanel = new JPanel();
    GridBagLayout sliderGrid = new GridBagLayout();
    sliderPanel.setLayout(sliderGrid);
    initSliders();
    initLabels();
    addComponent(sliderGrid, windSliderLabel,       1,1,new Insets(5,10,5,10));
    addComponent(sliderGrid, angleSliderLabel,      1,2,new Insets(5,10,5,10));
    addComponent(sliderGrid, windSpeed,             2,1,new Insets(5,10,5,10));
    addComponent(sliderGrid, launchAngle,           2,2,new Insets(5,10,5,10));

    controlPanel = new JPanel();
    controlPanel.setLayout(new BoxLayout(controlPanel,BoxLayout.X_AXIS));
    initButtons();
    initControlBox();
    controlPanel.add(controlBox);
    controlPanel.setBorder(BorderFactory.createLineBorder(Color.MAGENTA , 3 ));

    animationPanel = new AnimationPanel();
    animationPanel.setSize(new Dimension(750,500));
    animationPanel.setMinimumSize(new Dimension(animationPanel.getWidth(),animationPanel.getHeight()));
    animationPanel.setPreferredSize(new Dimension(animationPanel.getWidth(),animationPanel.getHeight()));

    add(animationPanel,BorderLayout.CENTER);
    add(controlPanel,BorderLayout.SOUTH);
    pack();
    setMinimumSize(new Dimension(getWidth(),getHeight()));
    sliderPanel.setMaximumSize(new Dimension(sliderPanel.getWidth(), sliderPanel.getHeight()));
}

//CONVENIENCE METHODS
private void addComponent(GridBagLayout layout, Component component, int row, int column, Insets padding) {
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = column;
    constraints.gridy = row;
    constraints.insets = padding;
    layout.setConstraints(component, constraints);
    sliderPanel.add(component);
}

private void initLabels() {
    windSliderLabel = new JLabel("Wind Speed");
    angleSliderLabel = new JLabel("Launch Angle");
}

private void initSliders() {
    windSpeed = new JSlider(-20,20);
        windSpeed.setMajorTickSpacing(10);
        windSpeed.setMinorTickSpacing(5);
        windSpeed.setPaintTicks(true);
        windSpeed.setPaintLabels(true);
    launchAngle = new JSlider(-15,15);
        launchAngle.setMajorTickSpacing(5);
        launchAngle.setMinorTickSpacing(1);
        launchAngle.setPaintTicks(true);
        launchAngle.setPaintLabels(true);
}

private void initButtons() {
    startButton = new JButton("Start");
    startButton.addActionListener(new StartHandler());
    pauseButton = new JButton("Pause");
    pauseButton.addActionListener(new PauseHandler());
    exitButton  = new JButton("Exit" );
    exitButton.addActionListener(new ExitHandler());
}

private void initControlBox() {
    controlBox = Box.createHorizontalBox();
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(sliderPanel);
    controlBox.add(Box.createHorizontalStrut(30));
    controlBox.add(startButton);
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(pauseButton);
    controlBox.add(Box.createHorizontalGlue());
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(Box.createHorizontalGlue());
    controlBox.add(exitButton);
    controlBox.add(Box.createHorizontalStrut(10));
}

//ACTION LISTENERS
public class WindAdjustListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {

    }

}

public class AngleAdjustListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {

    }

}

public class StartHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("START Pressed");
        startButton.setText("Reset");
        repaint();
        runAnimation();
        startButton.setText("Start");
    }
}

public class PauseHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("PAUSE Pressed");
    }
}

public class ExitHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        if(animationTimer != null)
            if(animationTimer.isRunning())
                animationTimer.stop();
        System.exit(0);
    }
}

public class AnimationClock implements ActionListener {
    public void actionPerformed(ActionEvent arg0) {

    }   
}

//ACCESSORS
public double[] getSliderValues() {
    double[] sliders = new double[2];
    sliders[0] = windSpeed.getValue();
    sliders[1] = launchAngle.getValue();
    return sliders;
}

//OTHER METHODS
public void runAnimation() {
    double[] inputs = getSliderValues();
    double wind = inputs[0];
    double launchAngle = inputs[1];
    double timeInterval = 0.05;     // seconds
    ParticleManager manager = null;
    try {
        manager = new ParticleManager(wind, launchAngle);
    } catch (EnvironmentException except) {
        System.out.println(except.getMessage());
        return;
    } catch (EmitterException except) {
        System.out.println(except.getMessage());            
        return;
    }
    System.out.println("Counts:");
    System.out.println("time\tStars\tSparks\tLaunchSparks");
    double time = 0;
    manager.start(time);
    fireworks = manager.getFireworks(time);
    do {
        //if (time % 0.5 == 0)
            showTypesCount(fireworks, time);
        if (Math.abs(time - 2.0) < timeInterval)
            snapshot = fireworks;
        time += timeInterval;
        fireworks = manager.getFireworks(time);
    } while (fireworks.size() > 0);
    showFireworks(snapshot, 2.0);
    animationPanel.repaint();
}

public void showFireworks(ArrayList<Particle> fireworks, double time) {
    if (fireworks == null)
        return;
    System.out.printf("\nAt time%5.2f seconds:\n", time);
    System.out.println("Type\t\tPosition (metres)");
    for (Particle firework : fireworks)
        System.out.println(firework);
}

public void showTypesCount(ArrayList<Particle> fireworks, double time) {
    int starCount = 0;
    int sparkCount = 0;
    int launchSparkCount = 0;
    for (Particle firework : fireworks) {
        if (firework instanceof Star)
            starCount++;
        else if (firework instanceof LaunchSpark)
            launchSparkCount++;
        else
            sparkCount++;
    }
    System.out.printf("%5.2f\t", time);
    System.out.println(starCount + "\t" + sparkCount + "\t" + launchSparkCount);
}

private class AnimationPanel extends JPanel {

    private BufferedImage background;

    public AnimationPanel() {
        super();
        try {
            background = ImageIO.read(new File("background.jpg"));
        } catch(IOException error) {
            error.printStackTrace();
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background,0,0,getWidth(),getHeight(),this); //this shows up
        g.setColor(Color.RED);
        g.fillRect(getWidth()/2, getHeight()-30, 10, 30); //this shows up
        paintFireWorks(g); //this doesn't show up
    }

    private void paintFireWorks(Graphics g) {
        if(snapshot != null) {
            for(Firework item: snapshot) {
                double[] position = item.getPosition();
                if(item instanceof Star){
                    int x = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int y = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int r = ((Star) item).getRenderSize();
                    g.fillOval(x,y,r,r);
                } else if(item instanceof Spark){
                    int x1 = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int x2 = (int)Math.round(position[0])-((Spark)item).getRenderSize();
                    int y1 = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int y2 = animationPanel.getHeight()-(int)Math.round(position[1])+((Spark)item).getRenderSize();
                    g.drawLine(x1,y1,x2,y2);
                }
            }
        }
    }

}

}

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


person Yak Attack    schedule 31.03.2014    source источник
comment
1) Чтобы быстрее получить помощь, опубликуйте MCVE (минимальный полный и проверяемый пример). 2) Один из способов получить изображение (я) для примера - это горячая ссылка на изображения, показанные в этом ответе.   -  person Andrew Thompson    schedule 31.03.2014
comment
@Andrew Thompson Теперь так и будет - спасибо за ссылки по теме   -  person Yak Attack    schedule 01.04.2014


Ответы (1)


Основываясь на этом...

public class StartHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("START Pressed");
        startButton.setText("Reset");
        repaint();
        runAnimation();
        startButton.setText("Start");
    }
}

Вы вызываете runAnimation из контекста потока диспетчеризации событий.

В вашем методе runAnimation вы делаете это...

do {
   //...
} while (fireworks.size() > 0);

Что блокирует EDT и предотвращает обработку новых событий, включая запросы на перерисовку.

Взгляните на параллелизм в Swing.

Простым решением будет использование Swing Timer, которое позволит вы выгружаете небольшое ожидание в фоновый режим, но оно запускается в контексте EDT...

person MadProgrammer    schedule 31.03.2014
comment
Спасибо за быстрый ответ. Однако меня больше интересовало получение рисунка определенного момента, чем анимация в этот момент. Как оказалось, причина, по которой рисунок не отображался, заключалась в том, что я неправильно преобразовывал высоты, что я обнаружил вскоре после публикации этой публикации. Теперь я вижу свои рисунки. Извините за потраченное время. - person Yak Attack; 31.03.2014