Control animation speed using the Jslider component

Asked

Viewed 83 times

1

Good guys, I’m developing a little project that aims to simulate the controlled environment of train tracks, where I have 3 trains circulating in a clockwise direction, where the three pass by the same place in certain stretches. I’m having trouble implementing train speed dynamically using the component JSlider. I was able to create all three components, but I still haven’t been able to link them to my trains.

Follows the code:

public class Trens {

private static final double QUADROS_POR_SEGUNDO = 20.0;

public static void preparar() {
    JFrame t = new JFrame();
    t.setLayout(null);
    t.setSize(1200, 900);
    t.setTitle("Semáforo");
    t.setLocationRelativeTo(null); 
    t.setResizable(false);
    t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Trilho t1 = new Trilho(300, 100, 300, 170);
    Trilho t2 = new Trilho(600, 100, 300, 170);
    Trilho t3 = new Trilho(450, 270, 300, 170);
    Jslider slider = new Jslider(30, 150, 300, 170);
    Jslider slider2 = new Jslider(900, 150, 300, 170);
    Jslider slider3 = new Jslider(150, 330, 300, 170);

    Trem a = new Trem(t1, Color.BLUE, 350, 100, 100.0);
    Trem b = new Trem(t2, Color.GREEN, 650, 100, 100.0);
    Trem c = new Trem(t3, Color.RED, 450, 335, 100.0);
    t.add(a);
    t.add(b);
    t.add(c);
    t.add(t1);
    t.add(t2);
    t.add(t3);
    t.add(slider);
    t.add(slider2);
    t.add(slider3);
    Runnable moverTudo = () -> {
        EventQueue.invokeLater(() -> {
            a.mover(1 / QUADROS_POR_SEGUNDO);
            b.mover(1 / QUADROS_POR_SEGUNDO);
            c.mover(1 / QUADROS_POR_SEGUNDO);
        });
    };
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(moverTudo, 0, (int) (1000 / QUADROS_POR_SEGUNDO), TimeUnit.MILLISECONDS);
    t.setVisible(true);
}

public static void main(String[] args) {
    EventQueue.invokeLater(Trens::preparar);
}

public static class Trilho extends JComponent {
    public Trilho(int x, int y, int width, int height) {
        this.setBounds(x, y, width, height);
        this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    }
}

public static class Trem extends JComponent {
    private Color cor;
    private Trilho trilho;
    private int x;
    private int y;
    private double velocidade; // pixels por segundo
    private double restante; // Frações de pixels que faltou andar.

    public Trem(Trilho trilho, Color cor, int x, int y, double velocidade) {
        this.trilho = trilho;
        this.cor = cor;
        this.x = x;
        this.y = y;
        this.velocidade = velocidade;
        this.setBounds(x - 5, y - 5, 10, 10);

    }

    @Override
    public void paintComponent(Graphics g) {
        g.setColor(cor);
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    public void mover(double deltaT) {
        if (velocidade == 0) return;
        boolean sentidoHorario = velocidade > 0;
        double distancia = Math.abs(restante + velocidade * deltaT);
        int tLeft = trilho.getX();
        int tTop = trilho.getY();
        int tRight = tLeft + trilho.getWidth();
        int tBottom = tTop + trilho.getHeight();

        for (int i = 0; i < (int) distancia; i++) {
            // Se deve ir à esquerda:
            if (x > tLeft && y == (sentidoHorario ? tBottom : tTop)) {
                x--;
            // Se deve ir à direita:
            } else if (x < tRight && y == (sentidoHorario ? tTop : tBottom)) {
                x++;
            // Se deve ir para cima:
            } else if (y > tTop && x == (sentidoHorario ? tLeft : tRight)) {
                y--;
            // Se deve ir para baixo:
            } else if (y < tBottom && x == (sentidoHorario ? tRight : tLeft)) {
                y++;
            // Se não for nenhum dos anteriores, o trem está descarrilhado. Coloca de novo no trilho.
            } else {
                x = tLeft;
                y = tTop;
            }
        }
        restante = distancia % 1;
        setLocation(x - 5, y - 5);
    }
}

public static class Jslider extends JPanel{

    private Jslider(int x, int y, int width, int height){
        this.setBounds(x, y, width, height);
        JSlider control = new JSlider(0,100,25);
        control.setMajorTickSpacing(50);
        control.setMinorTickSpacing(10);
        control.setPaintTicks(true);
        control.setFont(new Font("Serfi", Font.ITALIC, 12));
        control.setPaintLabels(true);
        control.setSnapToTicks(true);
        add(control);

    }
}

public class EventoSlider implements ChangeListener{


    @Override
    public void stateChanged(ChangeEvent e) {
         JSlider source = (JSlider) e.getSource();
         int fps = (int) source.getValue();
        System.out.println(fps);
    }

}
}
  • Why is everything static? It may be silly, but everything being static can be one of the sources of your problem.

  • see if this helps: https://answall.com/a/208128/28595

  • This question is a follow-up https://answall.com/q/226377/132

  • How so statico? @Articuno. Not necessarily like the example you sent.

  • Help me @Victorstafusa.

  • 1

    @Carlosdiego I’ll take a look.

  • @Articuno need to implement tbm deadlock between trains could help me in logic? Regardless of the speed both cannot collide, one has to wait for the other to pass and then it will continue. The pro is to do this dynamically as I can change the speed of trains at runtime

Show 2 more comments

1 answer

2


I got it, it was simple:

package trens;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;

public class Trens {

    private static final double QUADROS_POR_SEGUNDO = 20.0;

    public static void preparar() {
        JFrame t = new JFrame();
        t.setLayout(null);
        t.setSize(1200, 900);
        t.setTitle("Semáforo");
        t.setLocationRelativeTo(null); 
        t.setResizable(false);
        t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Trilho t1 = new Trilho(300, 100, 300, 170);
        Trilho t2 = new Trilho(600, 100, 300, 170);
        Trilho t3 = new Trilho(450, 270, 300, 170);

        Trem a = new Trem(t1, Color.BLUE, 350, 100, 100.0);
        Trem b = new Trem(t2, Color.GREEN, 650, 100, 100.0);
        Trem c = new Trem(t3, Color.RED, 450, 335, 100.0);
        VelocityControl slider = new VelocityControl(a, 30, 150, 300, 170);
        VelocityControl slider2 = new VelocityControl(b, 900, 150, 300, 170);
        VelocityControl slider3 = new VelocityControl(c, 150, 330, 300, 170);
        t.add(a);
        t.add(b);
        t.add(c);
        t.add(t1);
        t.add(t2);
        t.add(t3);
        t.add(slider);
        t.add(slider2);
        t.add(slider3);
        Runnable moverTudo = () -> {
            EventQueue.invokeLater(() -> {
                a.mover(1 / QUADROS_POR_SEGUNDO);
                b.mover(1 / QUADROS_POR_SEGUNDO);
                c.mover(1 / QUADROS_POR_SEGUNDO);
            });
        };
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(moverTudo, 0, (int) (1000 / QUADROS_POR_SEGUNDO), TimeUnit.MILLISECONDS);
        t.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(Trens::preparar);
    }

    public static class Trilho extends JComponent {
        public Trilho(int x, int y, int width, int height) {
            this.setBounds(x, y, width, height);
            this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        }
    }

    public static class Trem extends JComponent {
        private Color cor;
        private Trilho trilho;
        private int x;
        private int y;
        private double velocidade; // pixels por segundo
        private double restante; // Frações de pixels que faltou andar.

        public Trem(Trilho trilho, Color cor, int x, int y, double velocidade) {
            this.trilho = trilho;
            this.cor = cor;
            this.x = x;
            this.y = y;
            this.velocidade = velocidade;
            this.setBounds(x - 5, y - 5, 10, 10);
        }

        @Override
        public void paintComponent(Graphics g) {
            g.setColor(cor);
            g.fillRect(0, 0, getWidth(), getHeight());
        }

        public void setVelocidade(double novaVelocidade) {
            this.velocidade = novaVelocidade;
        }

        public void mover(double deltaT) {
            if (velocidade == 0) return;
            boolean sentidoHorario = velocidade > 0;
            double distancia = Math.abs(restante + velocidade * deltaT);
            int tLeft = trilho.getX();
            int tTop = trilho.getY();
            int tRight = tLeft + trilho.getWidth();
            int tBottom = tTop + trilho.getHeight();

            for (int i = 0; i < (int) distancia; i++) {
                // Se deve ir à esquerda:
                if (x > tLeft && y == (sentidoHorario ? tBottom : tTop)) {
                    x--;
                // Se deve ir à direita:
                } else if (x < tRight && y == (sentidoHorario ? tTop : tBottom)) {
                    x++;
                // Se deve ir para cima:
                } else if (y > tTop && x == (sentidoHorario ? tLeft : tRight)) {
                    y--;
                // Se deve ir para baixo:
                } else if (y < tBottom && x == (sentidoHorario ? tRight : tLeft)) {
                    y++;
                // Se não for nenhum dos anteriores, o trem está descarrilhado. Coloca de novo no trilho.
                } else {
                    x = tLeft;
                    y = tTop;
                }
            }
            restante = distancia % 1;
            setLocation(x - 5, y - 5);
        }
    }

    public static class VelocityControl extends JPanel {
        private VelocityControl(Trem trem, int x, int y, int width, int height) {
            this.setBounds(x, y, width, height);
            JSlider control = new JSlider(0, 100, 25);
            control.setMajorTickSpacing(50);
            control.setMinorTickSpacing(10);
            control.setPaintTicks(true);
            control.setFont(new Font("Serif", Font.ITALIC, 12));
            control.setPaintLabels(true);
            control.setSnapToTicks(true);
            this.add(control);
            control.addChangeListener(e -> trem.setVelocidade(control.getValue()));
        }
    }
}

I changed the class name Jslider for VelocityControl not to be confused with the JSlider (note that one has uppercase S and the other a lower case).

The trick to make the chosen value reflect on the speed is the control.addChangeListener(e -> trem.setVelocidade(control.getValue())); with the lambda syntax, this is very simple and clean.

To connect the trains to the controls, it was enough to pass the train as a parameter more for the VelocityControl.

One last detail is that you had used "Serfi" instead of "Serif" in the source name, a typo.

  • Perfect @Victorstafusa, really had it wrong, already had it fixed in my code.

  • all trains are running on one thread?

  • @Carlosdiego Yes. The Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(moverTudo, 0, (int) (1000 / QUADROS_POR_SEGUNDO), TimeUnit.MILLISECONDS); creates only one thread to run periodically moverTudo. However, the actual execution takes place within the EDT.

  • in case I need to identify a probable collision between trains and cause one thread to pause while the other of the train is on its way, then the paused thread goes back.. this for the 3 trains. As this implemented has how to do this logic?

  • @Carlosdiego Trying to do this with different threads will only bring you headache and no benefit. The answer is to put some logic inside the moverTudo or the method mover of each train that ascertains whether a collision has occurred and decides what to do in that case.

  • Really, then how could it be the check to see if they can collide?

  • @Carlosdiego Trem outro = ...; boolean colidiu = Math.abs(this.x - outro.x) < 10 && Math.abs(this.y - outro.y) < 10; - In the case these values 10 occurs because this is the width and height of the trains. If the width and height can vary, then you would use (this.width - outro.width) / 2 and (this.height - outro.height) / 2.

  • In fact, as the class name already suggests, you will need to use traffic lights, literally rs. When the train arrives at the point where the two lines coincide, put a traffic light there. When the train arrives there, check if it is "FOLLOW", otherwise stay still and wait for the other train to pass. When the train passes and the sign is "FOLLOW", it moves the train and then puts "STOP" on that sign. You need 3 signals, one for each section of intersection between the train lines.

  • @Dudaskank Oce has some example that to apply in an easy way and practice to understand in this little project?

  • Great answer, I had an idea solution, but by far this is a much less laborious way and provides more control of everything, congratulations =)

Show 5 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.