Visualizzazione dei risultati da 1 a 5 su 5

Discussione: Animazione Carta

  1. #1
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361

    Animazione Carta

    Ciao a tutti,sto provando per la prima volta a far muovere un oggetto nell'interfaccia creata con swing, nello specifico vorrei creare l'animazione di una carta che viene buttata,muovendosi per ora in verticale(ma mi piacerebbe che potesse fare anche altri tragitti).

    Ho scritto un po' di codice che so già essere penoso (ho già visto che il metodo setLocation non è consigliato ad esempio) ma, anche immaginando di usare altri metodi come drawImage ,volevo sapere se ha senso ad esempio usare il metodo Tread.sleep e provare a giocare sui valori dei millisecondi e dei pixel con cui avviene lo spostamento, o se andrebbero usate classi apposta (ho trovato un esempio con Timer anche se non molto ben fatto) per cercare soprattutto di evitare gli scatti nell'animazione e cercare un movimento più fluido.

    Metto qui delle righe di esempio,che come detto so essere pessime, ma tanto per dare un idea di ciò che mi aspetto :

    codice:
    import javax.swing.*;
    import java.awt.*;
    public class Animazione
    {
        public static void main(String[] args)throws InterruptedException
        {
            JFrame f=new JFrame("Animazione");
            JPanel p=new JPanel();
            f.add(p);        
            JLabel l=new JLabel(new ImageIcon("3c.png"));
            f.setSize(1000,1000);
            p.add(l);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
            l.setLocation(1,1);
            while(true) // ovviamente in un codice reale non la farei uscire dal frame :)
            {
                Thread.sleep(2);
                l.setLocation(l.getLocation().x,l.getLocation().y+1);
            }
        }
    }
    Qualcuno ha per caso già fatto qualcosa di simile?

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320
    Prima di tutto sono necessarie delle considerazioni.

    1) Tutto quello che riguarda i componenti grafici (finestre, pulsanti, etichette, ecc) e gli eventi che questi intercettano/rilanciano sono gestiti da un unico thread: EDT (Event Dispatch Thread). Lui si occupa di tutto: dal disegno dei componenti al dispacciamento degli eventi.

    2) L'EDT interagisce con gli altri thread dell'applicazione in modo molto particolare: fa una sola cosa per volta e non deve essere tenuto occupato troppo a lungo per operazioni onerose.

    Da queste due cose deriva che il meccanismo che sta sotto alla "grafica" di Swing va trattato con i guanti e vanno seguite determinate regole ben precise quando si ha a che fare con cose come "le animazioni". Per realizzare un'animazione serve un thread che si occupi di fare i dovuti calcoli su coordinate, dimenensione e logica operativa e che "interagisca" in qualche modo con l'EDT.

    Per far interagire un thread con l'EDT si deve usare uno dei due metodi che il framework Swing mette a disposizione:
    - SwingUtilities.invokeLater()
    - SwingUtilities.invokeAndWait()

    La differenza fra i due la dice il nome: il primo mette in coda un lavoro da far fare all'EDT e prosegue, il secondo mette in coda un lavoro da far fare all'EDT ed attende che questo venga fatto.

    Quindi la tua applicazione dovrà avere almeno un thread che si occupa di spostare la carta secondo la tua logica e questo thread dovrà, ad ogni operazione di spostamento, dire all'EDT di spostare effettivamente la carta.

    Questo che ti posto è un esempio piuttosto rozzo, ma funzionante, di quello che stai cercando di fare tu (la carta l'ho realizzata con una semplice JLabel bordata, tu puoi tranquillamente usare l'immagine):


    codice:
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    
    public class PannelloCarta extends JFrame {
    
       private class Animator extends Thread {
    
          public static final int HORIZONTAL = 0;
          public static final int VERTICAL = 1;
    
          private final PannelloCarta win;
          private int x;
          private int y;
          private int width;
          private int height;
          private int maxX;
          private int maxY;
          private int direction;
          private boolean incDirection;
          private volatile boolean attivo;
    
          public Animator(PannelloCarta win, int x, int y, int width, int height, int maxX, int maxY, int direction) {
             this.win = win;
             this.x = x;
             this.y = y;
             this.width = width;
             this.height = height;
             this.maxX = maxX;
             this.maxY = maxY;
             this.direction = direction;
             incDirection = true;
          }
    
          @Override
          public void run() {
             attivo = true;
             while( attivo ) {
                switch( direction ) {
                   case HORIZONTAL:
                      if ( incDirection ) {
                         if (x + width < maxX) {
                            ++x;
                         } else {
                            invertDirection();
                         }
                      } else {
                         if (x > 0) {
                            --x;
                         } else {
                            invertDirection();
                         }
                      }
                      break;
    
                   case VERTICAL:
                      if ( incDirection ) {
                         if (y + height < maxY) {
                            ++y;
                         } else {
                            invertDirection();
                         }
                      } else {
                         if (y > 0) {
                            --y;
                         } else {
                            invertDirection();
                         }
                      }
                      break;
                }
    
                redraw();
    
                try {
                   sleep( 5 );
                } catch (InterruptedException ie) { /* ignore */ }
             }
          }
    
          public void ferma() {
             attivo = false;
          }
    
          private void invertDirection() { incDirection = !incDirection; }
    
          private void redraw() {
             SwingUtilities.invokeLater( new Runnable() {
                @Override
                public void run() {
                   win.spostaCarta(x, y);
                }
             });
          }
       }
    
       private JLabel carta;
       private JPanel jpCentrale;
       private Animator animator;
    
       public PannelloCarta() {
          initComponents();
          setTitle("Movimento carta");
          setSize(1000, 1000);
          setDefaultCloseOperation( EXIT_ON_CLOSE );
          setLocationRelativeTo( null );
       }
    
       private void initComponents() {
          Container c = getContentPane();
          c.setLayout( new BorderLayout() );
    
          carta = new JLabel("Carta");
          carta.setBounds(10, 10, 80, 120);
          carta.setSize( new Dimension(80, 120) );
          carta.setBorder( BorderFactory.createLineBorder(Color.black) );
    
          jpCentrale = new JPanel();
          jpCentrale.setLayout( null );
          jpCentrale.add( carta );
    
          JButton cmdOriz = new JButton("Muovi Orizzontale");
          cmdOriz.addActionListener( new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ae) {
                muoviOrizzontale();
             }
          });
    
          JButton cmdVertic = new JButton("Muovi Verticale");
          cmdVertic.addActionListener( new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ae) {
                muoviVerticale();
             }
          });
    
          JPanel jpComandi = new JPanel( new FlowLayout(FlowLayout.CENTER, 40, 20) );
          jpComandi.add( cmdOriz );
          jpComandi.add( cmdVertic );
    
          c.add(jpComandi, BorderLayout.NORTH);
          c.add(jpCentrale, BorderLayout.CENTER);
       }
    
       private void spostaCarta(int newX, int newY) {
          carta.setLocation(newX, newY);
       }
    
       private void muoviOrizzontale() {
          if (animator != null) {
             animator.ferma();
             animator = null;
          }
    
          animator = new Animator(this,
                                  carta.getLocation().x,
                                  carta.getLocation().y,
                                  carta.getSize().width,
                                  carta.getSize().height,
                                  jpCentrale.getSize().width,
                                  jpCentrale.getSize().height,
                                  Animator.HORIZONTAL);
          animator.start();
       }
    
       private void muoviVerticale() {
          if (animator != null) {
             animator.ferma();
             animator = null;
          }
    
          animator = new Animator(this,
                                  carta.getLocation().x,
                                  carta.getLocation().y,
                                  carta.getSize().width,
                                  carta.getSize().height,
                                  jpCentrale.getSize().width,
                                  jpCentrale.getSize().height,
                                  Animator.VERTICAL);
          animator.start();
       }
    
       public static void main(String[] args) {
          try {
             UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
          } catch (Exception e) { /* L&F */ }
    
          SwingUtilities.invokeLater( new Runnable() {
             @Override
             public void run() {
                new PannelloCarta().setVisible( true );
             }
          });
       }
    }

    Focalizza soprattutto la tua attenzione sui punti del codice in cui viene invocato SwingUtilities.invokeLater(). Anche all'inizio, nel main. Quello è il punto in cui i thread (prima di tutto il Main-Thread, poi il thread Animatore) si interfacciano con l'EDT per fargli fare qualcosa.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361
    Wow non potevo chiedere di meglio, grazie mille !!

    Hai detto e usato nel codice molte cose che non avevo ancora visto, cercherò di capire tutto quanto prima !

    In particolare ho visto che anche tu richiami il metodo sleep per il thread , mi chiedevo se la differenza con il Thread.sleep che ho usato nel codice di prova sia nel fatto che io "lancio" sempre un nuovo Thread più difficile da gestire per l'EDT?

    Infine hai usato anche tu SetLocation nel metodo spostaCarta, ti riferivi anche a quello per definire "rozzo" l'esempio ?
    Anche perchè se invece di muovere la carta in orizzontale o verticale volessi fare un percorso in diagonale (ad esempio nella situazione di avere piu' carte affiancate e volendo spostare al centro del pannello piu' in basso quella scelta) suppongo che dovrei giocare un po' con la x,y (ad esempio incrementare di 2 x e di 1 y o altro ,a seconda della distanza dal centro)...

    Comunque Grazie ancora

  4. #4
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320
    Quote Originariamente inviata da Ansharja Visualizza il messaggio
    In particolare ho visto che anche tu richiami il metodo sleep per il thread , mi chiedevo se la differenza con il Thread.sleep che ho usato nel codice di prova sia nel fatto che io "lancio" sempre un nuovo Thread più difficile da gestire per l'EDT?
    sleep() è un metodo statico della classe Thread, quindi se si è all'interno di un oggetto che estende Thread (come nel mio caso), scrivere Thread.sleep() o semplicemente sleep() non fa alcuna differenza... posso omettere il nome della classe perchè sono in un oggetto di quella classe.
    Il metodo sleep() causa l'interruzione (per N millisecondi) del thread corrente (qualunque esso sia... nel mio caso, essendo che sono in uno specifico thread, uno user-thread, causo l'interruzione proprio di quel thread).
    Questo non ha nulla a che vedere con l'EDT, che è un thread a sé stante e non risente delle chiamate a Thread.sleep() (a meno che non venga fatto nel suo contesto, cosa che non va MAI fatta).

    Infine hai usato anche tu SetLocation nel metodo spostaCarta, ti riferivi anche a quello per definire "rozzo" l'esempio ?

    No, il mio "rozzo" era riferito al fatto che il codice poteva sicuramente essere scritto meglio. Per quanto riguarda setLocation() è un metodo assolutamente valido quanto setBounds() per riposizionare un elemento... non vedo perchè non usarlo (e, sinceramente, non avevo capito la tua iniziale affermazione sul fatto che non sia consigliato: è un metodo che esiste da sempre, è lì, nessuno l'ha deprecato, fa il suo lavoro, usiamolo... chi è che ne sconsiglia l'uso e, soprattutto, perchè? Sicuro di non aver mal interpretato il consiglio di qualcuno? Ad esempio, la prima risorsa su StackOverflow relativa a setLocation è il consiglio di non usarlo per il posizionamento dei componenti, in favore dell'uso dei LayoutManager... che nulla hanno a che vedere con il tuo scenario dove il LayoutManager proprio non deve essere usato.)


    Anche perchè se invece di muovere la carta in orizzontale o verticale volessi fare un percorso in diagonale (ad esempio nella situazione di avere piu' carte affiancate e volendo spostare al centro del pannello piu' in basso quella scelta) suppongo che dovrei giocare un po' con la x,y (ad esempio incrementare di 2 x e di 1 y o altro ,a seconda della distanza dal centro)...

    Esatto... si tratta di implementare la logica relativa (piuttosto semplice).

    Ciao.
    Ultima modifica di LeleFT; 15-05-2015 a 11:55
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  5. #5
    Utente di HTML.it
    Registrato dal
    Oct 2014
    residenza
    Padova
    Messaggi
    361
    Hai ragione, avevo visto più risposte in cui setLocation veniva sconsigliato per la gestione del layout e me ne ero fatto un'idea complessiva negativa .
    Grazie per tutti i consigli e il chiarimento sui Thread

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.