Gestione di più modalità di gioco con un controller di gioco

1

Ho un GameController di base con una macchina a stati finiti per gestire la logica di gioco.

Aggiungo le modalità di gioco e mi imbatto in problemi che li implementano in modo pulito. La funzionalità di base del gioco rimane la stessa: fisica, gestione degli input, creazione di livelli, ma ho molti punti in cui ho bisogno di un blocco if / else perché la funzionalità è leggermente diversa. In due modalità di gioco era gestibile ma non la più bella implementazione. Sto aggiungendo una modalità di gioco extra e voglio pulirlo.

Ecco un esempio di codice semplificato (in C #):

void OnBlocksCleared(int count) {
    score += getScoreFromClearedBlocks(count);
    scoreController.setScore(score);

    if (currentMode == GameMode.Endless) {
        DataManager.Instance.AddTotalBlocksCleared(count);
    } else if (currentMode == GameMode.Rush) {
        // Do nothing
    } else if (currentMode == GameMode.TimeAttack) {
        if (allBlocks.Count <= 0) {
            currentState = GameState.Win;
        }
    }
}

Ci sono metodi che hanno molta più logica e solo una singola riga se / else per una specifica modalità di gioco (se il tempo TimeAttack è scaduto, se il livello Rush è completato, ecc.) e alcuni metodi come sopra dove il if / else i blocchi occupano molto spazio. Stavo pensando di implementare un'interfaccia per ogni modalità di gioco, ma non è stato facile da implementare in modo pulito. Ecco cosa stavo progettando:

public interface IGameBehavior {
    void SetHighscore();
    void OnBlocksCleared(int count);
    void SetRemainingTime(float remainingTime);
}

Quindi avrei EndlessBehavior, TimeAttackBehavior, RushBehavior e in fondo ai metodi corrispondenti chiamiamo il metodo specifico in questo modo.

void OnBlocksCleared(int count) {
    // Do everything that's shared

    gameBehavior.OnBlocksCleared(count);
}

Questo non sembra funzionare bene perché se il comportamento ha bisogno di modificare specifiche del gameController diventerà strettamente accoppiato. Inoltre, in molti casi solo uno o due comportamenti di gioco effettivamente fanno qualcosa.

    
posta GameDev 04.12.2018 - 14:06
fonte

1 risposta

1

Ci sono alcuni aspetti del design che devono essere strettamente accoppiati. La sfida, naturalmente, è farlo in modo tale che se cambi qualcosa in un posto, non spezzi qualcosa in un altro posto. Quando hai a che fare con scelte come questa, a volte devi fare un passo indietro. Puoi pormi domande come questa:

  • Romperebbe il mio design di gioco se inserisco il currentState in DataManager.Instance ? Se ho limitato lo stato corrente da DataManager a una proprietà sul mio controller di gioco, questo potrebbe soddisfare le mie esigenze.
  • Devo aggiungere un'interfaccia al mio controller di gioco? Ti consente di separare l'interfaccia di ciò che il tuo IGameBehavior può fare su un controller dal controller stesso.
  • C'è un modo in cui posso modificare ciò che ho in modo che i ruoli e le responsabilità siano definiti più chiaramente senza una completa riscrittura?

Sono sicuro che ci sono più domande che puoi porre in questo scenario. Non c'è nulla di intrinsecamente sbagliato nel rendere il IGameBehavior legato al controller di gioco, supponendo che sia l'unico. Tuttavia, se vuoi controllare come IGameBehavior ha influenza sul controller, allora potrebbe essere logico introdurre un'interfaccia IGameController di tua scelta.

Con l'esempio che avevi, potresti avere qualcosa di simile:

public interface IGameController
{
    GameState CurrentState { get; set; }
    ICollection AllBlocks { get; }
}

Se prendiamo il IGameBehavior definito nell'OP, potresti avere una TimeAttackBehavior implementata in questo modo:

public class TimeAttackBehavior : IGameBehavior
{
    private readonly IGameController controller;

    public TimeAttachBehavior(IGameController controllerIn)
    {
        Debug.Assert(controllerIn != null);
        controller = controllerIn;
    }

    // Skip implementations for things that are outside of what was shown.

    public void OnBlocksCleared(int count)
    {
        if (controller.AllBlocks.Count <= 0)
        {
            controller.CurrentState = GameState.Win;
        }
    }
}

Questo ovviamente semplifica il tuo metodo OnBlocksCleared a questo:

public void OnBlocksCleared(int count)
{
    score += getScoreFromClearedBlocks(count);
    scoreController.setScore(score);

    behavior.OnBlocksCleared(count);
}

C'è più di un modo per farlo, quindi sperimenta un po '. Spero che questo ti dia una buona idea di una possibile soluzione.

    
risposta data 04.12.2018 - 15:18
fonte

Leggi altre domande sui tag