Анимиране върху фоново изображение

Създадох прозорец, който има голям JPanel, заемащ по-голямата част от областта, която ще се използва за анимиране на набор от фойерверки. Имам частен вътрешен клас AnimationPanel, който разширява JPanel. Замених paintComponent(), за да използвам буферирано изображение като фон. При натискане на бутона за стартиране ще стартира симулация, която ще анимира набора от фойерверки. В момента тази симулация извежда позициите към конзолата и генерира колекция, наречена моментна снимка, съдържаща всички фойерверки в определен момент от време.

В момента успешно нарисувах фона и малък правоъгълник, за да представя изстрелващата тръба, но когато се опитам да нарисувам фойерверките от моментната снимка, нищо не се показва. На този етап не съм загрижен за анимацията (все още се уча как да правя това с помощта на класа Timer), но трябва да знам, че тегленето ще работи, така че просто се опитвам да нарисувам моментната снимка. Прерисувам animationPanel в края на симулацията след получаване на моментната снимка.

Частният вътрешен клас 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);
                }
            }
        }
    }

}

}

Някакви идеи защо рисунките на фойерверките не се показват? Опитах се да се адаптирам към методите, показани в този отговор (Dynamic Graphics Object Painting) и този (панел с фоново изображение и рисуване с мишката), но не са имали успех. Все още остана с фон и правоъгълник.


person Yak Attack    schedule 31.03.2014    source източник
comment
1) За по-добра помощ по-рано, публикувайте MCVE (минимален пълен и проверим пример). 2) Един от начините да получите изображение(а) за пример е да поставите гореща връзка към изображенията, виждани в този отговор.   -  person Andrew Thompson    schedule 31.03.2014
comment
@Andrew Thompson Absolutely ще го прави отсега нататък - благодаря за връзките по темата   -  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 и предотвратява обработката на нови събития, включително заявки за пребоядисване

Разгледайте Concurrency в Swing

Едно просто решение би било да се използва Swing Timer, което би позволило прехвърляте малко изчакване във фонов режим, но което се задейства в контекста на EDT...

person MadProgrammer    schedule 31.03.2014
comment
Благодаря за бързия отговор. Бях по-заинтересован да получа чертежа на конкретен момент, отколкото анимирането в този момент. Както се оказа, причината, поради която чертежът не се показваше, беше, че не преобразувах височините правилно, което открих малко след тази публикация. Сега виждам рисунките си. Съжалявам за загубеното време. - person Yak Attack; 31.03.2014