Domanda

sto sviluppando una strategia in tempo reale di gioco clone sulla piattaforma Java e ho alcune domande concettuali su dove mettere e come gestire lo stato del gioco. Il gioco utilizza Swing / Java2D come rendering. Nella fase di sviluppo attuale, nessuna simulazione e non c'è intelligenza artificiale e solo l'utente è in grado di cambiare lo stato del gioco (per esempio, costruire / demolire un edificio, add-rimuovere linee di produzione, assemblaggio flotte e attrezzature). Pertanto, la manipolazione stato del gioco può essere eseguito in thread evento spedizione senza alcuna ricerca di rendering. Lo stato del gioco viene anche utilizzato per visualizzare varie informazioni aggregate per l'utente.

Comunque, come ho bisogno di introdurre la simulazione (ad esempio, la costruzione di progresso, i cambiamenti demografici, i movimenti della flotta, processo di produzione, ecc), cambiare lo stato del gioco in un timer e EDT sicuramente rallentare il rendering.

Diciamo che l'operazione di simulazione / AI viene eseguita in ogni 500ms e uso SwingWorker per il calcolo di circa 250ms di lunghezza. Come posso assicurare, che non esiste alcuna condizione di competizione per quanto riguarda lo stato del gioco si legge tra la simulazione e la possibile interazione con l'utente?

So che il risultato della simulazione (che è piccola quantità di dati) può essere efficacemente spostato indietro al EDT attraverso lo SwingUtilities.invokeLater () chiamata.

Il modello di stato del gioco sembra essere troppo complesso per essere fattibile solo utilizzando le classi di valori immutabili ovunque.

C'è un approccio relativamente corretta per eliminare questa condizione di gara di lettura? Forse facendo un / stato di gioco parziale piena di clonazione su ogni tick timer o modificare lo spazio vitale dello stato gioco da EDT in qualche altro thread?

Aggiornamento: (dai commenti che ho dato) Il gioco funziona con 13 giocatori controllati dall'intelligenza artificiale, 1 giocatore umano e conta circa 10000 oggetti del gioco (pianeti, edifici, attrezzature, ricerca, ecc). Un oggetto gioco per esempio ha i seguenti attributi:

World (Planets, Players, Fleets, ...)
Planet (location, owner, population, type, 
    map, buildings, taxation, allocation, ...)
Building (location, enabled, energy, worker, health, ...)

In uno scenario, l'utente costruisce un nuovo edificio su questo pianeta. Questa operazione viene eseguita in EDT come deve essere cambiato la mappa e gli edifici di raccolta. Parallelamente a questo, una simulazione viene eseguito su ogni 500ms per calcolare l'allocazione di energia agli edifici su tutti i pianeti di gioco, che ha bisogno di attraversare la collezione edifici per la raccolta delle statistiche. Se l'allocazione viene calcolata, si è presentato al EDT e il campo energetico di ogni edificio viene assegnato.

Solo interazioni giocatore umano hanno questa proprietà, perché i risultati del calcolo AI vengono applicati alle strutture in CEST comunque.

In generale, il 75% degli attributi degli oggetti sono statici e utilizzata solo per il rendering. Il resto è modificabile sia tramite l'interazione dell'utente o di simulazione decisione / AI. E 'inoltre garantito, che nessuna nuova simulazione / AI passo si avvia fino a quando la precedente ha scritto di nuovo tutte le modifiche.

I miei obiettivi sono:

  • non ritardare l'interazione con l'utente, ad esempio, utente posiziona l'edificio sul pianeta e solo dopo 0.5s ottiene il feedback visivo
  • Non ostruire l'EDT con calcolo, di attesa di blocco, ecc.
  • Evitare problemi di concorrenza con la raccolta attraversamento e la modifica, attribuire modifiche

Opzioni:

  • Belle bloccaggio oggetto grana
  • collezioni immutabili
  • campi volatili
  • Snapshot parziale

Tutti questi hanno vantaggi, svantaggi e fa sì che il modello e il gioco.

Aggiornamento 2: di cui sto parlando questo gioco . Il mio clone è qui . Le schermate potrebbero contribuire a immaginare le interazioni di rendering e modello dei dati.

Aggiornamento 3:

Cercherò di dare un esempio di codice piccolo per chiarire il mio problema come sembra dai commenti si è frainteso:

List<GameObject> largeListOfGameObjects = ...
List<Building> preFilteredListOfBuildings = ...
// In EDT
public void onAddBuildingClicked() {
    Building b = new Building(100 /* kW */);
    largeListOfGameObjects.add(b);
    preFilteredListOfBuildings.add(b);
}
// In EDT
public void paint(Graphics g) {
    int y = 0;
    for (Building b : preFilteredListOfBuildings) {
        g.drawString(Integer.toString(b.powerAssigned), 0, y);
        y += 20;
    }
}
// In EDT
public void assignPowerTo(Building b, int amount) {
    b.powerAssigned = amount;
}
// In simulation thread
public void distributePower() {
    int sum = 0;
    for (Building b : preFilteredListOfBuildings) {
        sum += b.powerRequired;
    }
    final int alloc = sum / (preFilteredListOfBuildings.size() + 1);
    for (final Building b : preFilteredListOfBuildings) {
        SwingUtilities.invokeLater(=> assignPowerTo(b, alloc));            
    }
}

Quindi, la sovrapposizione è tra l'onAddBuildingClicked () e distributePower (). Ora immaginate il caso in cui si ha il 50 di questo tipo di sovrapposizioni tra le varie parti del modello di gioco.

È stato utile?

Soluzione

Questo suona come potrebbe trarre vantaggio da un approccio client / server:

Il giocatore è un cliente - l'interattività e il rendering accadere su quel fine. Così il giocatore preme un pulsante, la richiesta va al server. La risposta dal server torna, e lo stato del giocatore viene aggiornato. In qualsiasi punto tra queste cose che accadono, lo schermo può essere ri-verniciato, e riflette lo stato del gioco come il client attualmente lo sa.

L'intelligenza artificiale è anche un cliente - è l'equivalente di un bot

.

La simulazione è il server. Si ottiene gli aggiornamenti dai suoi clienti in vari momenti e aggiorna lo stato del mondo, quindi invia questi aggiornamenti a tutti a seconda dei casi. Ecco dove si lega con la vostra situazione: la simulazione / AI richiede un mondo statico, e molte cose stanno accadendo in una sola volta. Il server può semplicemente mettere in coda le richieste di modifica e applicarli prima di inviare gli aggiornamenti al client (s). Quindi per quanto riguarda il server di cui trattasi, il mondo di gioco non sta effettivamente cambiando in tempo reale, che sta cambiando ogni volta che il server decide maledettamente bene che è.

Infine, sul lato client, è possibile evitare il ritardo tra la pressione del tasto e vedere il risultato facendo alcuni calcoli approssimativi rapido e la visualizzazione di un risultato (per cui la necessità immediata è soddisfatta) e quindi la visualizzazione del risultato più corretto quando il server ottiene intorno a parlare con te.

Si noti che questo in realtà non deve essere implementato in una rete TCP / IP over-the-internet sorta di passaggio, solo che aiuta a pensare in questi termini.

In alternativa, è possibile inserire la responsabilità di mantenere i dati coerenti durante la simulazione su un database, come sono già costruiti con bloccaggio e la coerenza in mente. Qualcosa come sqlite potrebbe funzionare come parte di una soluzione non in rete.

Altri suggerimenti

Non sono sicuro di comprendere appieno il comportamento che stai cercando, ma suona come avete bisogno di qualcosa come un cambiamento di stato filo / coda in modo che tutti i cambiamenti di stato sono gestite da un unico filo.

Crea un api, forse come SwingUtilities.invokeLater () e / o SwingUtilities.invokeAndWait () per la vostra coda di cambiamento di stato per gestire le vostre richieste di modifica dello stato.

Come che si riflette nella GUI penso che dipende dal comportamento che si sta cercando. vale a dire, non può ritirare i soldi, perché allo stato attuale è di $ 0, o pop indietro per l'utente che l'account era vuoto quando la richiesta di ritirare è stato elaborato. (Probabilmente non con questa terminologia ;-))

L'approccio più semplice è quello di rendere la simulazione abbastanza veloce per l'esecuzione in EDT. Preferisco programmi che funzionano!

Per il modello a due fili, cosa che suggerisco è sincronizzare il modello di dominio con un modello di rendering. Il modello di rendering dovrebbe mantenere i dati su quello che è venuto dal modello di dominio.

Per un aggiornamento: Nel thread simulazione bloccare il modello di rendering. Traverse il render modello di aggiornamento in cui le cose sono diverse da ciò che è previsto l'aggiornamento del modello di rendering. Una volta terminato l'attraversamento, sblocca il modello di rendering e pianificare una ridipingere. Si noti che in questo approccio non è necessario un bazillion ascoltatori.

Il modello rendering può avere diverse profondità. Ad un estremo potrebbe essere un'immagine e l'operazione di aggiornamento è solo per sostituire un solo riferimento con il nuovo oggetto immagine (questo voleva maniglia, per esempio, il ridimensionamento o altra interazione superficiale molto bene). Si potrebbe non preoccuparsi di verificare se un elemento è cambiare e solo aggiornare apparteneva.

Se la modifica lo stato del gioco è veloce (una volta che sai cosa cambiare a) si può trattare lo stato del gioco come gli altri modelli di altalene e unico cambiamento o di visualizzare lo stato del EDT. Se la modifica lo stato del gioco non è veloce, allora è possibile sincronizzare il cambiamento di stato e farlo in altalena lavoratore / timer (ma non l'EDT) oppure è possibile farlo in thread separato che si trattano in modo simile al EDT (a cui si punta guardare con un BlockingQueue per gestire le richieste di modifica). L'ultima è più utile se l'interfaccia utente non deve recuperare le informazioni da parte dello Stato gioco, ma invece ha i cambiamenti di rendering inviati tramite ascoltatori o osservatori.

E 'possibile aggiornare in modo incrementale lo stato del gioco e hanno ancora un modello che è coerente? Per esempio ricalcolare per un sottoinsieme di pianeta / lettore / oggetti flotta tra rende aggiornamenti / utente.

Se è così, è possibile eseguire aggiornamenti incrementali nel EDT che solo calcolare una piccola parte dello stato prima di consentire l'EDT di elaborare gli input dell'utente e rendere.

A seguito di ogni aggiornamento incrementale nel EDT si avrebbe bisogno di ricordare quanto il modello rimane da aggiornare e pianificare una nuova SwingWorker sul EDT di continuare questa elaborazione dopo eventuali input dell'utente in sospeso e il rendering è stato eseguito.

Ciò dovrebbe consentire di evitare la copia o il blocco del modello di gioco, pur mantenendo le interazioni degli utenti reattivo.

Penso che non dovreste avere mondiale memorizza alcun dato o apportare modifiche a qualsiasi stesso oggetto, esso dovrebbe essere utilizzato solo per mantenere un riferimento a un oggetto e quando l'oggetto deve essere cambiato, avere il Player rendere il cambiamento cambiamento direttamente. In tal caso, l'unica cosa che devi fare è sincronizzare ogni oggetto nel mondo di gioco in modo che quando un giocatore sta facendo un cambiamento, nessun altro giocatore può fare così. Ecco un esempio di quello che sto pensando:

Il giocatore A ha bisogno di sapere di un pianeta, quindi chiede mondo per quella del pianeta (come dipende l'implementazione). Mondiale restituisce un riferimento all'oggetto Pianeta giocatore A ha chiesto. Il giocatore A decide di fare un cambiamento, così lo fa. Diciamo che aggiunge un edificio. Il metodo per aggiungere un edificio per il Pianeta è sincronizzato in modo che solo un giocatore può farlo in un momento. L'edificio sarà tenere traccia del proprio tempo di costruzione (se presente) in modo metodo di costruzione add del pianeta sarebbe liberato quasi subito. In questo modo i più giocatori possono chiedere informazioni sullo stesso pianeta, allo stesso tempo senza influenzare l'un l'altro ed i giocatori possono aggiungere edifici quasi contemporaneamente senza molta apparenza di lag. Se due giocatori sono alla ricerca di un posto dove mettere l'edificio (se questo è parte del vostro gioco), quindi verificare l'idoneità di una posizione non sarà una richiesta di un cambiamento.

Mi dispiace se questo non risposta che stai domanda, non sono sicuro se ho capito correttamente.

Come circa l'attuazione di un'architettura di tubi e filtri. Tubi collegano filtri insieme e le richieste di coda se il filtro non è abbastanza veloce. Trasformazione avviene filtri all'interno. Il primo filtro è il motore AI mentre il motore di rendering è attuato da una serie di filtri successivi.

In ogni tick timer, il nuovo stato mondo dinamico viene calcolato sulla base di tutti gli ingressi (tempo è anche un ingresso) ed un copia inserito nel primo condotto.

Nel caso più semplice il vostro motore di rendering è implementato come un singolo filtro. Ci vuole solo le istantanee di stato dal tubo di ingresso e lo rende insieme con lo stato statico. In una partita dal vivo, il motore di rendering potrebbe voler saltare gli stati se non ci sono più di uno nel tubo, mentre se si sta facendo un punto di riferimento o output di un video che si vorrà rendere ogni uno.

Più filtri si possono scomporre il vostro motore di rendering in, meglio il parallelismo sarà. Forse è anche possibile scomporre il motore AI, per esempio si consiglia di separare stato dinamico in rapida evoluzione e lo stato che cambia lentamente.

Questa architettura si dà una buona parallelismo senza un sacco di sincronizzazione.

Un problema con questa architettura è che la raccolta dei rifiuti è andare a correre spesso congelare tutti i fili ogni volta, possibili uccidendo qualsiasi vantaggio ottenuto da multi-threading.

Sembra che avete bisogno di un CodaConPriorita di mettere gli aggiornamenti per il modello in poi, in cui aggiorna l'utente frmo essere prioritario rispetto agli aggiornamenti dalla simulazione e gli altri ingressi. Quello che mi sento dire è che l'utente deve sempre un feedback immediato sulle sue azioni wheras gli altri ingressi (simulazione, altro) potrebbe avere lavoratori che possono richiedere più di un passo di simulazione. Quindi sincronizzare sul CodaConPriorita.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top