2014-12-19 18 views
6

Ho un programma java che è fondamentalmente un gioco. Ha una classe denominata "Mondo". La classe "Mondo" ha un metodo 'levelChanger()' e un altro metodo 'makeColorArray()'.Come interrompere un thread dalla modifica di un array che viene utilizzato da un altro thread?

public class World { 

    private BufferedImage map, map1, map2, map3; 
    private Color[][] colorArray; 

    public World(int scrWd, int scrHi) { 
     try { 
      map1 = ImageIO.read(new File("map1.png")); 
      map2 = ImageIO.read(new File("map2.png")); 
      map3 = ImageIO.read(new File("map3.png")); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     map = map1; 

     makeColorArray(); 
    } 

    private void makeColorArray() { 
     colorArray = new Color[mapHi][mapWd]; // resetting the color-array 
     for(int i = 0; i < mapHi; i++) { 
      for(int j = 0; j < mapWd; j++) { 
       colorArray[i][j] = new Color(map.getRGB(j, i)); 
      } 
     } 
    } 

    //color-array used by paint to paint the world 
    public void paint(Graphics2D g2d, float camX, float camY) { 
     for(int i = 0; i < mapHi; i++) { 
      for(int j = 0; j < mapWd; j++) { 
       if(colorArray[i][j].getRed() == 38 && colorArray[i][j].getGreen() == 127 && colorArray[i][j].getBlue() == 0) { 
        //draw Image 1 
       } 
       else if(colorArray[i][j].getRed() == 255 && colorArray[i][j].getGreen() == 0 && colorArray[i][j].getBlue() == 0) { 
        //draw Image 2 
       } 
      } 
     } 
    } 

    public void levelChanger(Player player, Enemies enemies) { 
     if(player.getRec().intersects(checkPoint[0])) { 
      map = map2; 
      //calls the color-array maker 
      makeColorArray();   
     } 
     else if(player.getRec().intersects(checkPoint[1])) { 
      map = map3; 
      makeColorArray(); 
     } 
    } 

    public void update(Player player, Enemies enemies) { 
     levelChanger(player, enemies); 
    } 
} 

Procedimento makeColorArray() libera matrice 2D di tipo 'colore'. Questo array memorizza oggetti colore da un'immagine PNG. Questo array è usato dal metodo paint() di JPanel per dipingere il mondo sullo schermo.

Il metodo levelChanger() viene utilizzato per modificare i livelli (fasi) del gioco quando una determinata codifica è vera. È il metodo con cui chiama il metodo makeColorArray() per ricreare l'array di colori durante la modifica del livello di gioco.

Il problema è che ho un ciclo di gioco che gira su una discussione. Ora, la pittura di componenti swing come JPanel viene eseguita su un thread in background diverso da java. Quando viene modificato il livello del gioco, viene ricreato l'oggetto color-array. Ora, come ho detto in precedenza, l'oggetto color-array è usato dal metodo paint() per dipingere il mondo sullo schermo. A volte, l'oggetto color-array viene ancora utilizzato dal thread in background (non finito di dipingere) quando, in base alla logica di gioco, l'oggetto color-array viene ricreato ei suoi membri vengono impostati su null. Ciò causa l'eccezione null-pointer solo occasionalmente. Chiaramente una condizione di gara.

Desidero sapere come posso interrompere il thread di gioco dal ripristino della matrice di colori fino a quando il thread di oscillazione dello sfondo ha finito di dipingere.

+1

vi suggerisco di guardare in biblioteca concorrenza java: http://docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html – dckuehn

+0

La soluzione migliore è quella di non utilizzare più thread. Sostituisci il ciclo principale con 'javax.swing.Timer' o usa' SwingUtilities.invokeAndWait'. –

+0

@Banthar è una soluzione semplice per il problema ma non è la migliore per tutti i casi. Se il codice di aggiornamento è un processo che richiede molto tempo per essere elaborato, spostarlo nell'EDT impedisce che gli eventi di UI reali vengano elaborati in modo tempestivo e tempestivo. – NESPowerGlove

risposta

1

suggerimenti:

  • Usa un design Model-View-Control per il vostro programma, o utilizzare uno dei tanti varianti simili.
  • Utilizzare un timer Swing per gestire il loop di gioco della GUI, ma utilizzare le porzioni in tempo reale, non il ritardo del timer, nel modello per determinare l'intervallo di tempo tra i passaggi del ciclo.
  • Il modello deve essere eseguito nel thread dell'evento Swing della GUI.
  • Ma le attività a esecuzione prolungata devono essere eseguite nei thread in background utilizzando SwingWorker.
  • Questa è la chiave: non aggiornare i dati del modello, i dati utilizzati da JPanel per disegnare, finché il thread in background non ha completato il lavoro su di esso. A PropertyChangeListener e SwingPropertyChangeSupport possono essere utili per questo.
  • Assicurati che JPanel disegna nel suo metodo paintComponent(...), non nel suo metodo paint(...) e che chiami il metodo super.
  • Migliore se si rende l'immagine di sfondo un'Immagine raffinata e si ha il disegno di JPanel che nel suo metodo paintComponent(...) per l'efficienza.
  • Assicurarsi che tutto il codice della GUI Swing, eccetto forse le chiamate repaint(), siano richiamati sul thread dell'evento Swing.
  • E sì, sicuramente leggere il Concurrency in Swing tutorial.
1

Un modo con modifiche minime è la sincronizzazione dell'accesso all'array di colori.

Personalmente avrei estratto i dati condivisi in una singola classe che è completamente thread-safe, in questo modo non dovresti assicurarti che due parti separate della base di codice debbano sapere su cosa sincronizzare (sembra che la tua classe qui faccia più che gestire la mappa a prima vista, forse è una classe che sto descrivendo però).

private void makeColorArray() { 
    Color[][] colorArrayTemp = new Color[mapHi][mapWd]; // resetting the color-array 
    for(int i = 0; i < mapHi; i++) { 
     for(int j = 0; j < mapWd; j++) { 
      colorArrayTemp [i][j] = new Color(map.getRGB(j, i)); 
     } 
    } 
    synchronized(colorArray) 
    { 
     colorArray = colorArrayTemp; 
    } 
} 

//color-array used by paint to paint the world 
public void paint(Graphics2D g2d, float camX, float camY) { 
    synchronized(colorArray) 
    { 
     for(int i = 0; i < mapHi; i++) { 
      for(int j = 0; j < mapWd; j++) { 
       if(colorArray[i][j].getRed() == 38 && colorArray[i][j].getGreen() == 127 && colorArray[i][j].getBlue() == 0) { 
        //draw Image 1 
       } 
       else if(colorArray[i][j].getRed() == 255 && colorArray[i][j].getGreen() == 0 && colorArray[i][j].getBlue() == 0) { 
        //draw Image 2 
       } 
      } 
     } 

    } 
} 
Problemi correlati