многопоточность с использованием Java Swing для простой 2-мерной анимации - PullRequest
1 голос
/ 01 октября 2009

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

package javagamestutos;



import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;


public class Board extends JPanel implements Runnable {


    private Star star;
    private Thread animator;
    ArrayList<Star> items=new ArrayList<Star>();


    public Board() {
        setBackground(Color.BLACK);
        setDoubleBuffered(true);
        star=new Star(25,0,0);
        Star star2=new Star(50,20,25);
        items.add(star2);
        items.add(star);
    }

    public void addNotify() {
        super.addNotify();
        animator = new Thread(this);
        animator.start();
    }

    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D g2d = (Graphics2D)g;



        for (Star s : this.items) {
            g2d.drawImage(s.starImage, s.x, s.y, this);
        }


        Toolkit.getDefaultToolkit().sync();
        g.dispose();
    }

    public void run() {

        while(true){
            try {
                for (Star s : this.items) {
                    s.move();
                }
                repaint();

                Thread.sleep(star.delay);
            } catch (InterruptedException ex) {
                Logger.getLogger(Board.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }


}

вот звездный класс, который является движущимся элементом.

package javagamestutos;

import java.awt.Image;
import javax.swing.ImageIcon;

/**
 *
 * @author fenec
 */
public class Star {
    Image starImage;
    int x,y;
    int destinationX=200,destinationY=226;
    boolean lockY=true;
    int delay;


    public Star(int delay,int initialX,int initialY){
        ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png"));
        starImage = ii.getImage();
        x=initialX;
        y=initialY;
        this.delay=delay;
    }


    void moveToX(int destX){
        this.x += 1;
    }


    boolean validDestinatonX(){
        if(this.x==this.destinationX){
                this.lockY=false;
                return true;
            }
        else
            return false;
    }

    void moveToY(int destY){
        this.y += 1;
    }


    boolean validDestinatonY(){
        if(this.y==this.destinationY)
            return true;
        else
            return false;
    }

    void move(){

        if(!this.validDestinatonX() )
            x+=1;
        if(!this.validDestinatonY() && !this.lockY)
            y+=1;

        /*if(!this.validDestinatonY())
            y+=1;
        */
    }


}

и вот скелет анимации, расширяющий JFrame:

package javagamestutos;
import javax.swing.JFrame;

public class Skeleton extends JFrame {

public Skeleton() {
        add(new Board());
        setTitle("Stars");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(300, 280);
        setLocationRelativeTo(null);
        setVisible(true);
        setResizable(false);
    }
    public static void main(String[] args) {
        new Skeleton();
    }
}

Есть ли у вас какие-либо идеи, как достичь моих целей? Я использую темы правильно? заранее спасибо.

Ответы [ 5 ]

2 голосов
/ 01 октября 2009

Это потому, что вы вызываете метод «move» с фиксированной скоростью, определенной задержкой первого «start»

  Thread.sleep(star.delay);

Так что, если вы перемещаете их немного каждые "n" миллисекунд, они, похоже, будут двигаться в том же покое.

Если вы хотите, чтобы они двигались с разной скоростью, вы должны перемещать их в другой ветке (сейчас вы используете только одну). Запомните комментарий: omry,

EDIT

Я сделал нечто подобное совсем недавно

У меня есть две разные вещи, которые так оживляют, поэтому у меня есть два таймера (таймеры используют потоки внизу, но они могут повторять код выполнения каждую фиксированную скорость).

Первый применяет текст к JLabel каждую секунду (1000 мс)

    final Timer timer = new Timer();
    timer.scheduleAtFixedRate( new TimerTask() {
        public void run(){      
            setText();
        }
    }, 0, 1000 );

И другие меняют отображаемое изображение каждые 10 секунд (10000 мс)

    final Timer imageTimer = new Timer();
    imageTimer.scheduleAtFixedRate( new TimerTask() {
        public void run() {
            setImage();
        }
    }, 0, 10000 );

Здесь у меня есть видео результата:

enter image description here

Для более продвинутого (и приятного) управления временем вы ДОЛЖНЫ взглянуть на проект "Timing Framework" , который добавляет дополнительные возможности для таймеров.

1 голос
/ 23 октября 2009

Я бы посоветовал вам взглянуть на библиотеку с открытым исходным кодом trident , которая делает именно это, ее автор, Кирилл Гручников, хорошо известен в мире Swing (он является автором известного вида Substance и чувствовать).

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

1 голос
/ 01 октября 2009

Обычно компоненты Swing следует использовать из потока обработки событий AWT (EDT). repaint - это один из методов, который предположительно можно использовать вне EDT. Однако ваш Star не является и не должен быть потокобезопасным.

Самый простой подход - использовать только EDT (по крайней мере, для начала). Вместо использования Thread используйте javax.swing.Timer, который срабатывает на EDT.

Разные комментарии: Ваш метод paint не должен нуждаться в удалении отправленного ему графического объекта или в синхронизации с использованием Toolkit. Компонент не должен быть установлен с двойной буферизацией, но должен быть установлен непрозрачным (JPanel не гарантированно будет непрозрачным). Вы должны просто расширить JComponent вместо JPanel, так как это не панель. Как правило, для внешних классов не очень хорошая идея реализовать Runnable. Предпочитаю приватные переменные.

1 голос
/ 01 октября 2009

Вы должны рисовать в AWTDispatchThread. Для этого вам нужно использовать что-то вроде SwingUtilities.invokeLater(Runnable); Это относится не только к вашей анимации, но и к созданию и настройке, видимой для вашего JFrame. Невыполнение этого требования может привести к взаимоблокировке с потоком рисования. Кроме того, при перемещении операций рисования в методы SwingUtilites вы не захотите включать какие-либо циклы while(true), так как это приведет к управлению вашим потоком рисования.

0 голосов
/ 01 октября 2009

, если вы уверены, что хотите рисовать в темах, вы можете использовать:

update(getGraphics());

вместо перекраски. это обычно считается плохой практикой, так как вы обычно рисуете материал в потоке AWT.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...