Движущиеся черные и белые шары

1

Я новичок в программировании на Java и OO, вот код проблемы с черно-белыми шарами. Сначала позвольте мне объяснить программу, которую я хочу в выходе: в окне есть n мячей (например, 6 шаров), один черный и один белый, в каждом ходу нам разрешено перемещать только один шар, и это движение должно быть показаны на экране, а в конце все белые шары должны быть на одной стороне, а все черные шары должны быть на другой стороне. Вот пример из шести шаров:

Изображение 174551

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

код:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.*;

public class DrawPanel extends JPanel implements ActionListener
{
Timer myTimer = new Timer(2000, this);
public static final int NUMBER_OF_CIRCLES = 10; //number of circles which are to moved
static int[] circles = new int[NUMBER_OF_CIRCLES];

public void paintComponent(Graphics g)
{
    int x = 0; //start point of circles;
    int length = 40; //diagonal of the circles

    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    Ellipse2D circle;

    //painting n circles based on the array
    for(int index = 0; index<10; index++)
    {
        if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle

            circle = new Ellipse2D.Double(x, 120, length, length);
            g2.draw(circle);
        }
        else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle
            circle = new Ellipse2D.Double(x, 120, length, length);
            g2.fill(circle);
        }
        x += 45; //increas start pont of the next circle 45 pixles
    }
    myTimer.start();
}

public void actionPerformed(ActionEvent e)
{
    int tmp; //template for swaping elements
    int condition; //condition of the forS

    arrayFill(circles); //fills the array based on the writen method, one 1 and one 0 like: 0 1 0 1 0 1 0 1

    //here is the part which works good, it changes palces of an elemen at time.
    //at the end of this part the array would be like: 1 1 1 1 0 0 0 0
    if(NUMBER_OF_CIRCLES % 2 == 0)
        condition = circles.length/2 -1;
    else
        condition = circles.length/2;
    for(int i = circles.length-1, k = 1; i>condition; i--, k++)
    {
        for(int j = i - k; j<i ;j++)
        {
            tmp = circles[j];
            circles[j] = circles[j+1];
            circles[j+1] = tmp;
            //if we call arrayPrint method it will print the array but I don't know why repaint is not working here
            //arrayPrint(circles);
            repaint();
        }
    }
}

//fills the array, one 1 and one 0. Example: 0 1 0 1 0 1 0 1 0 1
public static void arrayFill(int[] array)
{
    for(int i = 0; i<array.length; i++)
    {
        if( i%2 == 0)
            array[i] = 0;
        else 
            array[i] = 1;
    }
}

}//end of class

И основной класс:

import javax.swing.JFrame;
public class BlackAndWhiteBallsMoving {

public static void main(String[] args)
{
    DrawPanel myPanel = new DrawPanel();
    JFrame myFrame = new JFrame();

    myFrame.add(myPanel);
    myFrame.setSize(600, 500);
    myFrame.setTitle("Black And White Balls Moving");
    myFrame.setVisible(true);
    myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

}//end of class
  • 0
    Вы вызываете arrayFill(circles) с каждым тиком, поэтому вы всегда начинаете сначала.
Теги:
swing

3 ответа

1
Лучший ответ

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

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

public class DrawPanel extends JPanel implements ActionListener {
    public static final int NUMBER_OF_CIRCLES = 10;

    Timer myTimer = new Timer(500, this);
    int[] circles = new int[NUMBER_OF_CIRCLES];

    public DrawPanel() {
        arrayFill(circles);

        if(NUMBER_OF_CIRCLES % 2 == 0) {
            condition = circles.length/2 -1;
        } else {
            condition = circles.length/2;
        }

        i = circles.length - 1;
        k = 1;

        myTimer.start();
    }

    int i, j, k;
    int condition;
    boolean outer = true;

    @Override
    public void actionPerformed(ActionEvent e) {
        if(outer) {
            if(i > condition) {
                j = i - k;      // set j
                outer = false;  // and move to the inner loop swap
            } else {
                myTimer.stop(); // the outer loop is done so stop the timer
            }
        }
        if(!outer) {
            int tmp = circles[j];
            circles[j] = circles[j+1];
            circles[j+1] = tmp;
            repaint();

            j++;
            if(j >= i) {
                i--;
                k++;
                outer = true; // move to the outer condition
            }                 // next time the timer triggers
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        int x = 0;
        int length = 40;

        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        Ellipse2D circle;
        for(int index = 0; index<10; index++) {
            if(circles[index] == 0){
                circle = new Ellipse2D.Double(x, 120, length, length);
                g2.draw(circle);
            } else if(circles[index] == 1){
                circle = new Ellipse2D.Double(x, 120, length, length);
                g2.fill(circle);
            }
            x += 45;
        }
        //myTimer.start();
    }

    public static void arrayFill(int[] array) {
        for(int i = 0; i<array.length; i++) {
            if( i%2 == 0) {
                array[i] = 0;
            } else {
                array[i] = 1;
            }
        }
    }
}

(Я уверен, что это может быть учтено другим способом.)

Также:

  • Я добавил @Override аннотации, которые вы должны использовать. Это будет предупреждать вас, когда вы совершаете определенные ошибки. (Подобно неправильному написанию имени метода или некорректному объявлению его подписи).
  • Я переместил circles в переменную экземпляра, потому что я не вижу причины, по которой она должна быть статичной. Он является частью состояния экземпляра DrawPanel.
  • Я создал конструктор, который инициализирует переменные, такие как circles.
  • paintComponent - protected метод, и он должен оставаться таким, если нет оснований рекламировать его public.

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

В качестве дополнительной заметки вы должны прочитать начальные темы учебника. Вы не создаете свой графический интерфейс в потоке событий Swing. В основном вам нужно обернуть свой код в main внутри вызова invokeLater:

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            // create and show your GUI
        }
    });
}
0

добавьте краску. он всегда должен вызывать перерисовку(), например,

new Thread(){  // this can be started on main or constructor of object
 public void run(){
    while(true){
       repaint();
       try {
         Thread.sleep(50);
        } catch(Exception e){ } 
     }
   }
  }.start();

а затем, при выполнении действия, отметьте движущиеся объекты, такие как movingObjects, сохраните animate_x = 0 и сохраните логическую переменную типа existAnimation

затем на paintComponent, увеличьте animate_x

animate_x = animate_x + 1;
if (animate_x >= MAX_WIDTH_OF_ANIMATION){
  existAnimation = false;
}

и используйте это existAnimation, animate_x и movingObjects

как,

public void paintComponent(Graphics g)
{
    int x = 0; //start point of circles;
    int length = 40; //diagonal of the circles

    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    Ellipse2D circle;

    //painting n circles based on the array
    for(int index = 0; index<10; index++)
    {

        int paint_x = x;
        if (movingObjects.has(circles[index])){
            paint_x += animate_x;
        }
        if(circles[index] == 0){ //if the element of the arrayy is 0 then draw a void circle

            circle = new Ellipse2D.Double(paint_x, 120, length, length);
            g2.draw(circle);
        }
        else if(circles[index] == 1){ //if the element of the array is 1 them draw a filled circle
            circle = new Ellipse2D.Double(paint_x, 120, length, length);
            g2.fill(circle);
        }
        x += 45; //increas start pont of the next circle 45 pixles
    }
    myTimer.start();
}
0

Основная проблема заключается в вашем методе actionPerformed. Ваши два for циклов очень быстро перестраивают массив до его окончательной компоновки. Каждая итерация цикла займет от наносекунд до миллисекунд (это зависит от того, как работает метод repaint()). Весь процесс завершен менее чем за 50 миллисекунд или около того. Это слишком быстро для ваших глаз, чтобы идти в ногу.

В принципе, метод repaint() работает, но он работает слишком быстро, чтобы глаза человека не отставали.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню