La tua domanda è abbastanza ampia. Alla fine stai cercando un'architettura fattibile per un tipo di gioco multiplayer basato su server-client. Una risposta completa richiederebbe di disegnare alcuni diagrammi e scrivere un codice scheletrico. Sono sicuro che ci sono alcuni esempi abbastanza dettagliati da qualche parte.
Tuttavia, c'è un aspetto particolare del design che dovresti considerare se la tua applicazione sarà un gioco multiplayer locale o distribuito o altro.
Sembra che tu stia per mescolare il modello e il controllo . Consiglio vivamente di separarli. Il modulo che contiene il modello per il gioco non dovrebbe avere alcuna conoscenza su server o client e così via. Le regole di un gioco di carte non dipendono dal fatto che l'architettura dell'applicazione di gioco sia distribuita o meno. In realtà, non dipendono nemmeno dal fatto che tu scriva un'applicazione per giocare al computer o per simulare il gioco o per altri scopi come tenere traccia e tenere il punteggio di una partita dal vivo.
Quindi, inizierei scrivendo per la prima volta un modello funzionante per il gioco di carte stesso. Fintanto che mantieni il modello separato, non dovrai più pensare a come implementerai un fantastico gioco multiplayer distribuito in seguito.
Il modello di un gioco di carte consiste di pochi elementi: un modello per le carte e possibili gettoni di gioco, un modello per lo stato di gioco e un modello per le regole. Dato che stiamo parlando di un gioco a turni, le regole del gioco riguardano principalmente la definizione di azioni legali e gli effetti che le azioni hanno sullo stato del gioco. Mi aspetto che il modello contenga classi come
-
Card
(per le carte)
-
Deck
(per un mazzo di carte)
-
GameState
(aggregato per lo stato di gioco, preferibilmente serializzabile)
-
PlayerState
(per mantenere lo stato di un giocatore, figlio di GameState)
-
TableState
(per mantenere lo stato del tavolo da gioco se ce n'è uno; figlio di GameState)
-
Action
(per azioni che un giocatore potrebbe prendere)
Il modello di regole dovrebbe consistere in metodi e classi che controllano quale tipo di Action
s è disponibile in qualsiasi stato di un gioco e in che modo un Action
scelto cambierebbe lo stato del gioco. Il modello di regole dovrebbe offrire metodi come:
-
List<Action> DetermineAvailableActions(GameState gameState)
-
GameState ResolveGameStateAfterAction(GameState initialGameState, Action action)
Un semplice modello di regole potrebbe consistere in una classe ( GameRules
) che implementa i metodi precedenti.
Se scegli di scrivere un GameState mutabile (che non consiglierei) la seconda firma del metodo sarebbe come
-
void ApplyAction(GameState gameState, Action action)
Il modello per il gioco dovrebbe essere assolutamente agnostico sul resto dell'applicazione, sull'ambiente e in particolare sull'interfaccia utente. Lo stesso modello dovrebbe funzionare per un'applicazione di gioco single player locale e per un'applicazione di gioco client-server multiplayer. Dovresti essere in grado di eseguire simulazioni di interi giochi dall'inizio alla fine usando solo il modello e un semplice ciclo di gioco, alcuni logging e un'implementazione minimamente semplice di un'IA o piuttosto un idiota artificiale che seleziona azioni casuali.
Dopo aver scritto e testato il modello stesso del gioco, puoi continuare a scrivere un controller di gioco. Il controller dovrebbe contenere il loop principale reale del gioco. Dovrebbe essere in grado di
- prendi le impostazioni iniziali del gioco (numero di giocatori, tipi di giocatori, ecc.)
- inizializza lo stato del gioco secondo le impostazioni date
- controlla il flusso del gioco e la comunicazione tra i moduli
- invia messaggi ai giocatori richiamando metodi di alcuni oggetti
PlayerController
- ascolta i messaggi inviati dai giocatori
I dettagli del controller dipendono dall'architettura dell'applicazione di gioco. Il controller dovrebbe conoscere tutti i moduli coinvolti. Tuttavia, se giochi bene le tue carte, puoi usare lo stesso controller per controllare un semplice gioco o simulazione locale e un gioco multiplayer distribuito.
Un ciclo di gioco semplificato potrebbe essere simile a questo:
while (!currentGameState.IsFinished()) {
activePlayerController = playerControllers.Get(currentGameState.GetActivePlayerId());
List<Action> availableActions = rules.DetermineAvailableActions(currentGameState);
activePlayerController.SendPleaseChooseActionMessage(currentGameState, actions);
Action chosenAction = ReceiveChosenAction();
currentGameState = rules.ResolveGameStateAfterAction(currentGameState, chosenAction);
gameStateHistory.Push(currentGameState);
for (PlayerController c: playerControllers) {
c.SendGameStateUpdatedMessage(currentGameState, chosenAction);
}
}
Se non sei un programmatore esperto, ti suggerisco di provare prima a scrivere un semplice gioco locale. Basta mantenere separati il modello, il controller e l'interfaccia utente.