2015-01-26 14 views
6

Ho un momento orribile in arrivo con una buona domanda Titolo ... mi spiace, per favore modifica se il tuo cervello è meno sparato del mio.strategia di chunking della mappa, problema di ritardo di Rientro

Sto riscontrando alcuni problemi nella gestione del lato client delle mappe del mio gioco. Il mio gioco è basato su tile con tessere di 32x32 pixel. La mia prima mappa di gioco era di 1750 x 1750 tessere. Avevo un mucchio di strati di client side, ma sono riuscito a ridurlo a 2 (terreno e edifici). Stavo caricando in precedenza gli strati dell'intera mappa in memoria (array corti). Quando ho saltato a 2200 x 2200 piastrelle ho notato un vecchio pc con alcuni problemi con memoria insufficiente (1 GB +). Vorrei che ci fosse un tipo di dati tra byte e short (sto mirando a ~ 1000 piastrelle diverse). Il mio gioco supporta più risoluzioni in modo che lo spazio visibile dei giocatori possa mostrare 23,17 tessere per una risoluzione di 800x600 fino a 45,29 tessere per risoluzioni 1440x1024 (più). Uso Tiled per disegnare le mie mappe e generare i 2 livelli in file di testo separati usando un formato simile al seguente (0, 0, 2, 0, 3, 6, 0, 74, 2 ...) tutti su una riga.

Con l'aiuto di molte domande SO e alcune ricerche ho trovato una strategia di chunking della mappa. Usando le coordinate attuali del giocatore, come punto centrale, carico abbastanza tessere per 5 volte la dimensione della mappa visiva (il più grande sarebbe 45 * 5,29 * 5 = 225,145 tessere). Il giocatore è sempre disegnato al centro e il terreno si muove sotto di lui/lei (quando cammini ad est il terreno si muove verso ovest). La minimappa viene disegnata mostrando una schermata in tutte le direzioni, per essere tre volte la dimensione della mappa visibile. Si prega di vedere la seguente rappresentazione visiva (molto ridotta) per spiegare meglio di quanto probabilmente l'ho spiegato.

enter image description here

Il mio problema è questo: quando il giocatore muove "1/5 ° le piastrelle di dimensioni pezzo di distanza" dal punto centrale originale (chunkX/Y) coordinate, che io chiamo per il gioco di ri scansione del file . La nuova scansione utilizzerà le coordinate correnti del lettore come punto centrale. Attualmente il problema che sto riscontrando è che il rechunking prende come .5s sul mio pc (che è una specifica piuttosto alta). La mappa non si aggiorna per 1-2 spostamenti di tessere uguali.

Per risolvere il problema precedente, ho provato a eseguire la scansione dei file in un nuovo thread (prima di toccare il 1/5 punto) in un buffer di array temporaneo. Quindi, una volta eseguita la scansione, avrei copiato il buffer nell'array reale e chiamato repaint(). Casualmente ho visto alcuni problemi saltati con questo che non era un grosso problema. Peggio ancora, l'ho visto disegnare una parte casuale della mappa per 1-2 fotogrammi. Esempio di codice riportato di seguito:

private void checkIfWithinAndPossiblyReloadChunkMap(){ 
    if (Math.abs(MyClient.characterX - MyClient.chunkX) + 10 > (MyClient.chunkWidth/5)){ //arbitrary number away (10) 
     Runnable myRunnable = new Runnable(){ 
      public void run(){ 
       logger.info("FillMapChunkBuffer started."); 

       short chunkXBuffer = MyClient.characterX; 
       short chunkYBuffer = MyClient.characterY; 

       int topLeftChunkIndex = MyClient.characterX - (MyClient.chunkWidth/2) + ((MyClient.characterY - (MyClient.chunkHeight/2)) * MyClient.mapWidth); //get top left coordinate of chunk 
       int topRightChunkIndex = topLeftChunkIndex + MyClient.chunkWidth - 1; //top right coordinate of chunk 

       int[] leftChunkSides = new int[MyClient.chunkHeight]; 
       int[] rightChunkSides = new int[MyClient.chunkHeight]; 

       for (int i = 0; i < MyClient.chunkHeight; i++){ //figure out the left and right index points for the chunk 
        leftChunkSides[i] = topLeftChunkIndex + (MyClient.mapWidth * i); 
        rightChunkSides[i] = topRightChunkIndex + (MyClient.mapWidth * i); 
       } 

       MyClient.groundLayerBuffer = MyClient.FillGroundBuffer(leftChunkSides, rightChunkSides); 
       MyClient.buildingLayerBuffer = MyClient.FillBuildingBuffer(leftChunkSides, rightChunkSides); 

       MyClient.groundLayer = MyClient.groundLayerBuffer; 
       MyClient.buildingLayer = MyClient.buildingLayerBuffer; 
       MyClient.chunkX = chunkXBuffer; 
       MyClient.chunkY = chunkYBuffer; 

       MyClient.gamePanel.repaint(); 

       logger.info("FillMapChunkBuffer done."); 
      } 
     }; 
     Thread thread = new Thread(myRunnable); 
     thread.start(); 
    } else if (Math.abs(MyClient.characterY - MyClient.chunkY) + 10 > (MyClient.chunkHeight/5)){ //arbitrary number away (10) 
     //same code as above for Y 
    } 
} 

public static short[] FillGroundBuffer(int[] leftChunkSides, int[] rightChunkSides){ 
    try { 
     return scanMapFile("res/images/tiles/MyFirstMap-ground-p.json", leftChunkSides, rightChunkSides); 
    } catch (FileNotFoundException e) { 
     logger.fatal("ReadMapFile(ground)", e); 
     JOptionPane.showMessageDialog(theDesktop, getStringChecked("message_file_locks") + "\n\n" + e.getMessage(), getStringChecked("message_error"), JOptionPane.ERROR_MESSAGE); 
     System.exit(1); 
    } 
    return null; 
} 

private static short[] scanMapFile(String path, int[] leftChunkSides, int[] rightChunkSides) throws FileNotFoundException { 
    Scanner scanner = new Scanner(new File(path)); 
    scanner.useDelimiter(", "); 

    int topLeftChunkIndex = leftChunkSides[0]; 
    int bottomRightChunkIndex = rightChunkSides[rightChunkSides.length - 1]; 

    short[] tmpMap = new short[chunkWidth * chunkHeight]; 
    int count = 0; 
    int arrayIndex = 0; 

    while(scanner.hasNext()){ 
     if (count >= topLeftChunkIndex && count <= bottomRightChunkIndex){ //within or outside (east and west) of map chunk 
      if (count == bottomRightChunkIndex){ //last entry 
       tmpMap[arrayIndex] = scanner.nextShort(); 
       break; 
      } else { //not last entry 
       if (isInsideMapChunk(count, leftChunkSides, rightChunkSides)){ 
        tmpMap[arrayIndex] = scanner.nextShort(); 
        arrayIndex++; 
       } else { 
        scanner.nextShort(); 
       } 
      } 
     } else { 
      scanner.nextShort(); 
     } 

     count++; 
    } 

    scanner.close(); 
    return tmpMap; 
} 

Sono davvero alle mie spalle. Voglio essere in grado di andare oltre questa schifezza della GUI e lavorare su vere meccaniche di gioco. Qualsiasi aiuto sarebbe enormemente apprezzato. Scusate per il post lungo, ma credetemi un sacco di pensieri/notti insonni è andato in questo. Ho bisogno delle idee degli esperti di SO. Grazie mille!!

p.s. Sono venuto con alcuni potenziali idee di ottimizzazione (ma non è sicuro queste sarebbero risolvere alcuni della questione):

  • dividere i file delle mappe in più righe in modo da poter chiamare scanner.nextLine() 1 volta, piuttosto che lo scanner .next() 2200 volte

  • trovare una formula che dati i 4 angoli del blocco della mappa saprà se una determinata coordinata si trova all'interno di essa. questo mi permetterebbe di chiamare scanner.nextLine() quando si trova nel punto più lontano del blocco per una determinata linea. questo richiederebbe l'approccio di file di mappe multilinea sopra.

  • buttare via solo 1/5 del pezzo, spostare l'array, e caricare il prossimo 1/5 del pezzo

risposta

1

Assicurarsi che la scansione del file ha terminato prima di iniziare una nuova scansione.

Attualmente inizierai nuovamente la scansione (probabilmente in ogni fotogramma) mentre il centro è troppo lontano dal centro di scansione precedente. Per risolvere il problema, ricorda di eseguire la scansione di prima ancora di iniziare e migliorare di conseguenza le tue condizioni lontane.

// MyClient.worker represents the currently running worker thread (if any) 
if(far away condition && MyClient.worker == null) { 
    Runnable myRunnable = new Runnable() { 
     public void run(){ 
      logger.info("FillMapChunkBuffer started."); 

      try { 
       short chunkXBuffer = MyClient.nextChunkX; 
       short chunkYBuffer = MyClient.nextChunkY; 

       int topLeftChunkIndex = MyClient.characterX - (MyClient.chunkWidth/2) + ((MyClient.characterY - (MyClient.chunkHeight/2)) * MyClient.mapWidth); //get top left coordinate of chunk 
       int topRightChunkIndex = topLeftChunkIndex + MyClient.chunkWidth - 1; //top right coordinate of chunk 

       int[] leftChunkSides = new int[MyClient.chunkHeight]; 
       int[] rightChunkSides = new int[MyClient.chunkHeight]; 

       for (int i = 0; i < MyClient.chunkHeight; i++){ //figure out the left and right index points for the chunk 
        leftChunkSides[i] = topLeftChunkIndex + (MyClient.mapWidth * i); 
        rightChunkSides[i] = topRightChunkIndex + (MyClient.mapWidth * i); 
       } 

       // no reason for them to be a member of MyClient 
       short[] groundLayerBuffer = MyClient.FillGroundBuffer(leftChunkSides, rightChunkSides); 
       short[] buildingLayerBuffer = MyClient.FillBuildingBuffer(leftChunkSides, rightChunkSides); 


       MyClient.groundLayer = groundLayerBuffer; 
       MyClient.buildingLayer = buildingLayerBuffer; 
       MyClient.chunkX = chunkXBuffer; 
       MyClient.chunkY = chunkYBuffer; 
       MyClient.gamePanel.repaint(); 
       logger.info("FillMapChunkBuffer done."); 
      } finally { 
       // in any case clear the worker thread 
       MyClient.worker = null; 
      } 
     } 
    }; 

    // remember that we're currently scanning by remembering the worker directly 
    MyClient.worker = new Thread(myRunnable); 
    // start worker 
    MyClient.worker.start(); 
} 

Prevenire una nuova scansione prima che la nuova analisi precedente ha completato presenta un'altra sfida: che cosa fare se si cammina in diagonale cioè si raggiunge la situazione in cui in x stai incontrando la condizione lontano, avviare la scansione e durante quel scansione incontrerai la condizione per y per essere lontano. Dal momento che scegli il prossimo centro di scansione in base alla tua posizione corrente, questo problema non dovrebbe sorgere se hai una dimensione del blocco abbastanza grande.

Ricordare direttamente al lavoratore viene fornito con un bonus: che cosa fai se hai bisogno di teletrasportare il lettore/videocamera ad un certo punto mentre stai effettuando la scansione? Ora puoi semplicemente terminare il thread di lavoro e avviare la scansione nella nuova posizione: dovrai controllare manualmente il flag di terminazione in MyClient.FillGroundBuffer e MyClient.FillBuildingBuffer, rifiutare il risultato (parzialmente calcolato) nel Runnable e interrompere il ripristino di MyClient.worker in caso di un abortire.

Se è necessario eseguire lo streaming di più dati dal file system del gioco, pensare di implementare un servizio di streaming (estendere l'idea dell'operatore a uno che elabora processi di analisi dei file arbitrari). È inoltre necessario verificare se il disco rigido è in grado di eseguire la lettura da più file contemporaneamente più velocemente rispetto alla lettura di un singolo flusso da un singolo file.

Passare a un formato di file binario è un'opzione, ma non consente di risparmiare molto in termini di dimensioni del file. E dal momento che lo Scanner utilizza già un buffer interno per eseguire l'analisi (l'analisi di interi da un buffer è più veloce rispetto al riempimento di un buffer da un file), è necessario innanzitutto concentrarsi su come far funzionare il lavoratore in modo ottimale.

+0

OMG è arrivato il giorno (ci hanno lavorato per settimane) ... questo funziona come un fascino. Quindi ero molto vicino e questo mi rende felice: spammare i soccorritori come un matto. Grazie mille per l'aiuto. Assegnerà la taglia quando me lo consente. Se sei curioso, hai aiutato "Kisnard Online" :). Spero che tu venga un giorno a giocare e che tu possa sperimentare il tuo fantasioso caching sul lato client in azione !! Ho intenzione di menzionare te (o il tuo sito) nelle mie note di rilascio - fammi sapere se desideri che non lo faccia per qualche motivo. – KisnardOnline

+0

@KisnardOnline Grazie Sarei felice di essere accreditato come "Lukas Beyeler" e controllerò il tuo gioco non appena avrò un po 'di tempo :) – BeyelerStudios

+0

Bounty assegnato e lo farà. Sarà nelle note di rilascio per la prossima patch: D Grazie ancora Lukas. Ora che ho questa correzione per finalizzare pubblicizzerò di più. Spero che ci siano alcuni giocatori quando ti unisci :) – KisnardOnline

0

Provare a velocizzare la velocità di lettura utilizzando un file binario invece di un file csv. Utilizzare DataInputStream e readShort() per quello. (Questo ridurrà anche la dimensione della mappa.)

Puoi anche usare pezzi di piastrelle 32x32 e salvarli in diversi file. Quindi non devi caricare le tessere che sono già state caricate.

Problemi correlati