0

I see a lot of videos and tutorials using timer when moving objects in GUI but I tried doing some stuff without it and it seems to work fine. I do not quite understand when exactly a timer is needed. Any help greatly appreciated

Example of a code with two moving objects without a timer

import java.awt.Color;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;







public class Test2 extends JPanel{
    int x=100,y=200,velX=5,velY=5;
    int x2=300,y2=200;
    Action upAction;
    Action downAction;
    Action leftAction;
    Action rightAction;
    Action wAction;
    Action aAction;
    Action sAction;
    Action dAction;
    JLabel label3 = new JLabel();
    
    public Test2() {
        
        
        this.setLayout(null);
        this.setFocusable(true);
        this.setBackground(Color.LIGHT_GRAY);
        JLabel label1 = new JLabel("Press WASD to control Green Circle");
        label1.setBounds(40, 30, 250, 20);
        label1.setForeground(Color.GREEN);
        this.add(label1);
        JLabel label2 = new JLabel("Press Arrows to control Blue Circle");
        label2.setBounds(250, 30, 250, 20);
        label2.setForeground(Color.BLUE);
        this.add(label2);
        label3 = new JLabel("");
        label3.setBounds(200, 200, 250, 20);
        label3.setForeground(Color.RED);
        this.add(label3);
        upAction= new upAction();
        downAction= new downAction();
        leftAction = new leftAction();
        rightAction = new rightAction();
        wAction= new WAction();
        aAction = new AAction();
        sAction = new SAction();
        dAction= new DAction();
        
        
        
        
        
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "uplol");;
        this.getActionMap().put("uplol", upAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "downlol");
        this.getActionMap().put("downlol", downAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "leftlol");
        this.getActionMap().put("leftlol", leftAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "rightlol");
        this.getActionMap().put("rightlol", rightAction);

        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('w'), "moveup");;
        this.getActionMap().put("moveup", wAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('a'),"moveleft");
        this.getActionMap().put("moveleft", aAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('s'), "movedown");
        this.getActionMap().put("movedown", sAction);
        this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('d'), "moveright");
        this.getActionMap().put("moveright", dAction);
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.GREEN);
        g.fillOval(x,y, 40, 40);
        g.setColor(Color.BLUE);
        g.fillOval(x2, y2, 40, 40);
        
    }


    
    public class upAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            y-=velY;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
                
            }
            
        }
        
    }

    public class downAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            y+=velY;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
        }
    }
        

    public class rightAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            x+=velX;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
            
        }
        
    }
    public class leftAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            x-=velX;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
            
            
        }
        
    }

    
    public class WAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            y2-=velY;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
            
        }
        
    }public class AAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            x2-=velX;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
            
        }
        
    }public class DAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            x2+=velY;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
            
            
        }
        
    }public class SAction extends AbstractAction{

        @Override
        public void actionPerformed(ActionEvent e) {
            y2+=velY;
            repaint();
            checkCollision();
            if(checkCollision()==true) {
                label3.setText("OVER");
                velY=0;
                velX=0;
                
            }
        }
        
    }
    public boolean checkCollision() {
        if((Math.pow(Math.abs(x2-x),2)+Math.pow(Math.abs(y2-y), 2))<=1600) {
            System.out.println("night");
            return true;
            
        }
        return false;
    }
    
        
    



}

Example of a code with timer

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.Timer;

import javax.swing.*;



public class Move extends JPanel implements ActionListener{
    javax.swing.Timer timer= new javax.swing.Timer(5, this);
    int y=0,x=0,velY=2,velX=2;
    
    
    
    
    @Override
    public void paint(Graphics g) {
        
        super.paint(g);
        
        this.setBackground(Color.MAGENTA);
        Graphics2D graphics2d = (Graphics2D)g;
        
        graphics2d.setColor(Color.BLUE);
        graphics2d.fill3DRect(100, 150, 100, 30, true);
        graphics2d.setColor(Color.RED);
        graphics2d.fillOval(200, 250, 28, 61);
        graphics2d.setColor(Color.BLACK);
        graphics2d.drawString("That boy is a monster", 80, 35);
        
        //timer.start();
    }
    public Move() {
        this.setLayout(null);
        JLabel rotter = new JLabel("fool");
        rotter.setBounds(140, 200, 53, 90);
        this.add(rotter);
        
        
        
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        if(x<0||x>560) {
            velX=-velX;
        }
        if(y<0|y>360) {
            velY=-velY;
        }
        x+=velX;
        y+=velY;
        repaint();
        
        
    }


}
VGR
  • 40,506
  • 4
  • 48
  • 63
sin010
  • 15
  • 5
  • Some example code would improve this Question. – Basil Bourque Aug 29 '21 at 19:09
  • 1) Don't override paint. Custom painting in Swing is done by overriding `paintComponent(...)` 2) Don't use a null layout. 3) All Swing components should determine their own preferred size. When doing custom painting you override `getPreferredSize()`. 4) A painting method is for painting only. Don't start the Timer in the painting method. – camickr Aug 29 '21 at 21:25
  • GUI's are event driven. Normally the user generates an event by using the mouse or the keyboard and the GUI reacts. So you can easily move your objects as long as the user presses a key - no Timer required. A Timer is simply used to generate an event at a set interval without user interaction. You can then do whatever you want when the event is generated. Typically this is used for animation. So, for example, you can continually animate an object moving around the frame. See: https://stackoverflow.com/a/54028681/131872 for a complete example. – camickr Aug 29 '21 at 21:38
  • Another usage for a Timer is to allow you handle multiple keys. A KeyEvent will only be generated for the last key pressed. What if you want diagonal movement in your game by hold up/right at the same time? In this case you need to track each key as it is pressed an then you a Timer to apply movement to the object in both directions. Check out the `KeyboardAnimation.java` example found in [Motion Using the Keyboard](https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/) – camickr Aug 29 '21 at 21:44

1 Answers1

4

As long as you're only

  • directly reacting to one event
  • and only update the display once

... it's completely OK to do that without timers.

The cases where you can/should use timers is

  • if you have an ongoing animation
  • if you're not directly reacting to a UI event, but call that "changes" from another thread.

Why use timers then?

  • Swing (that you're using) is not thread safe, so changing some UI elements from a Thread other than the EDT (Event Dispatch Thread) might bring disorder and chaos (really strange behaviour) into your UI.
  • If you have ongoing animations - which usually require a loop of some kind - an do all that inside the EDT, the UI will NOT be able to react to any input or even update itself properly.

So you use a timer which automatically:

  • runs in its own thread, so UI does not block
  • and calls the EDT for updates to the UI to keep things thread safe and clean
JayC667
  • 2,418
  • 2
  • 17
  • 31