Perché ho bisogno di un ciclo di gioco con stati, invece di avviare direttamente lo stato successivo nello stato corrente?

4

Ad esempio, è normale vedere giochi con loop di gioco e stati:

stateChanged(){
  switch(state){
    STATE.PLAYER_SELECT_CHARACTER:
      this.currentController=new PlayerSelectCharacterController();
      break;
    STATE.CHARACTER_MOVE:
      this.currentController=new CharacterMoveController();
      break;
  }
  this.replaceController(this.currentController);
}

class PlayerSelectCharacterController{
  .
  .
  .
  characterSelected(int charIndex){
    this.globalData.charIndex=charIndex;
    this.globalData.state=STATE.CHARACTER_MOVE;
    this.parentController.stateChanged();
  }
}

ma la mia domanda è, perché non avviare direttamente il prossimo stato agli ultimi stati? Penso che sia meglio, soprattutto quando ho bisogno di passare i parametri ai seguenti stati:

class PlayerSelectCharacterController{
  characterSelected(int charIndex){
    this.parentController.replaceController(new CharacterMoveController(charIndex));
  }
}    
    
posta mmmaaa 10.08.2018 - 03:43
fonte

2 risposte

7

Concentriamo l'attenzione sull'evidente vantaggio in primo luogo: nel primo esempio, i controller sono per lo più indipendenti l'uno dall'altro. PlayerSelectCharacterController non ha bisogno di conoscere la classe CharacterMoveController o viceversa. Ciascuna di queste classi può essere sviluppata separatamente, verificata separatamente o, se necessario, collocata in diverse unità di compilazione.

Questo non è possibile nel secondo esempio: PlayerSelectCharacterController deve sapere su CharacterMoveController , questa è una dipendenza hardcoded.

Con solo due classi, questo potrebbe non valere la pena di pensarci, ma se il tuo gioco ha 20 o più stati, puoi facilmente ottenere un enorme albero di dipendenza, dove ogni controller dipende da ogni altro controller, forse in qualche modo ciclico .

Cosa può essere meno ovvio: nei giochi, le transizioni di stato spesso non sono così semplici come nell'esempio della tua domanda. Potrebbero esserci regole complesse su come passare da uno stato all'altro. Per un certo tipo di complessità, potrebbe essere desiderabile separare le due preoccupazioni di

  • decidere sul nuovo stato e
  • cambia effettivamente lo stato.

In realtà mi aspetterei che la logica che ci hai mostrato in characterSelected si verifichi in qualche luogo al di fuori del PlayerSelectCharacterController , forse nel parentController ( characterSelected forse una funzione che restituisce il nuovo stato, ma l'assegnazione a globalData dovrebbe non succede direttamente in PlayerSelectCharacterController ). Ciò eviterebbe anche la necessità di chiamare parentController.stateChanged() da PlayerSelectCharacterController , ciò potrebbe essere fatto dal ciclo esterno, che può prevenire alcuni potenziali problemi con la ricorsione e la duplicazione del codice.

Come strategia generale, prova a costruire i singoli controllori come blocchi predefiniti , senza effetti collaterali sui dati globali e senza dipendenze dirette con altri controller dello stesso tipo. Altrimenti rischi di ottenere una "grande palla di fango" non conservabile.

    
risposta data 10.08.2018 - 09:04
fonte
0

Se studi il Pattern di stato, puoi vedere che è valido sia che l'oggetto stato corrente scelga lo stato successivo sia che il contesto possa farlo anche tu. In genere, se ci si trova in uno stato, la successiva transizione di stato è determinata dagli eventi nello stato corrente.

IMHO è più simile a OOP per rendere ogni oggetto di stato responsabile della transizione successiva, piuttosto che di un corpo esterno (il contesto). Inoltre puoi passare i parametri o effettuare iniezioni proprio come suggerisci!

La conseguenza è che ogni oggetto stato deve essere programmato correttamente per fare il suo bit nella macchina a stati complessivi.

La conseguenza di eseguire le transizioni esternamente è che qualche codice esterno deve conoscere l'intera macchina a stati (forse buono o cattivo) ma anche come suggerisci, devi effettuare il marshalling di parametri e iniezioni tra stati.

Preferisco decisamente dare questa responsabilità agli oggetti di Stato come suggerisci tu.

    
risposta data 10.08.2018 - 04:07
fonte