Abilità che interagiscono con aspetti di altre abilità - come implementare in modo efficiente

1

Nel mio gioco voglio che i personaggi siano in grado di lanciare incantesimi che infliggono una certa quantità di danno di un certo tipo ad altri personaggi nel gioco.

Voglio anche abilità che annullino una quantità di danno di un certo tipo da equipaggiare a un personaggio.

Non voglio dover programmare un enorme muro di resistenze al danno che di solito è 0 per ogni singolo personaggio nel gioco (in più voglio abilità che possono anche raddoppiare danno o trasformarlo), voglio la capacità innescata di essere in grado di modificare altre abilità. quindi ho pensato di mettere gli oggetti incantesimo in pila in modo che altre abilità possano modificarli. ma ecco il problema ogni abilità ha diversi nomi, numeri e tipi di variabili. Come posso accedere a ogni abilità che infligge danno da fuoco senza programmare un caso nella classe modificatore di abilità per ogni singola abilità che infligge una certa quantità di danno di un certo tipo. devo scrivere una lunga lista di attributi di abilità nella super classe, la maggior parte dei quali non userò? se lo faccio, è altrettanto brutto di dover scrivere un enorme elenco di variabili che modificano le abilità su ciascun personaggio. Voglio programmare un gioco senza dover scrivere lunghi elenchi di attributi su classi che non vengono utilizzate il 99% delle volte. È persino possibile?

es. Voglio un'abilità passiva che raddoppia la quantità di danni da fuoco che infliggi.

NON voglio farlo: Spell.target.fireDamageMultiplier = 2; perché poi avrò tonnellate di attributi simili su ogni singola entità

NON POSSO farlo: Spell.damage * = 2; perché non tutte le abilità hanno un attributo di danno

NON voglio farlo: Firebolt.damage * = 2; perché allora devo scrivere un caso speciale per ogni abilità che ha un attributo di danno

Ho pensato di avere diverse classi di abilità di quante ne possa ereditare la capacità, ma ho sentito di non farlo e c # non ha ereditarietà multipla. Leggo anche per favorire la composizione dell'ereditarietà, ma poi ho di nuovo il problema di avere una lunga lista di variabili (o riferimenti al tipo di incantesimo) per ogni incantesimo che nella maggior parte dei casi non userò. quindi questo non ha ancora risolto il mio problema di scrivere codice disordinato che diventa fuori controllo più abilità aggiungo.

Quindi, come faccio a modificare le abilità senza scrivere un caso speciale per ogni singola abilità?

    
posta M.Anthony 18.06.2016 - 07:51
fonte

4 risposte

1

Ho implementato qualcosa di leggermente simile e ho trovato una soluzione che potrebbe funzionare per te. Sembra che tu voglia che il personaggio A lanci una magia al personaggio B, e il personaggio B potrebbe indossare un tipo di armatura (o protezione o qualcosa che alla fine fa qualcosa come l'armatura). Quindi, strutturare il codice in questo modo.

La classe Character dovrebbe avere un elenco di oggetti CastModifier e un elenco di oggetti ReceiveModifier. (Non voglio usare il termine "incantesimo" nei nomi, poiché è ambiguo se l'incantesimo sia stato lanciato da loro o lanciato su di loro).

Quando un personaggio lancia una magia, viene inviata attraverso una funzione CastSpell che applica quei CastModifiers all'istanza dell'incantesimo. E quando un personaggio ha un incantesimo lanciato su di lui, lo riceve tramite una funzione ReceiveSpell, che scorre attraverso gli oggetti ReceiveModifier.

Quindi se A lancia una magia di fuoco con 100 danni e ha un buff di fuoco da 2 *, invierà l'oggetto con 200 danni da fuoco al personaggio B. Se B ha un buff di protezione da fuoco -50, riceve l'incantesimo tramite ReceiveSpell, che riduce il danno a 150 prima di applicare quel danno alla sua salute. Inoltre, è necessario dettare l'ordine delle operazioni all'interno delle classi CastModifier e ReceiveModifier: impostarlo in modo che il cast applichi la moltiplicazione prima dell'aggiunta e ricevere l'addizione per l'aggiunta prima della moltiplicazione, ad esempio (gli ordini devono essere invertiti tra i due). E cerca di essere coerente con le operazioni; Ho sempre moltiplicato e aggiunto, quindi a volte ho moltiplicato per una frazione o ho aggiunto un numero negativo.

Ho usato un po 'di ereditarietà nella mia implementazione, quindi ci sarebbe una classe ImmediateSpell e una classe DurationSpell per separare gli incantesimi che hanno un solo colpo da quelli che infliggono danni nel tempo. L'ereditarietà multipla sarebbe stata utile per le classi di danno, ma era sufficiente impostarla come una proprietà della magia.

    
risposta data 17.03.2017 - 21:17
fonte
0

Bene, dal punto di vista del design del gioco, il personaggio potrebbe avere statistiche di "fuoco potenziato" e "resistenza al fuoco". Quindi, potrebbero esserci delle abilità che aumentano e diminuiscono quelle statistiche e il danno di questo tipo verrebbe aumentato / diminuito di conseguenza. E se la statistica "resistenza" è negativa, allora il personaggio subirebbe più danni da quell'elemento. La maggior parte dei giochi di ruolo gestisce il danno elementale in questo modo.

    
risposta data 18.09.2016 - 10:22
fonte
0

Non penso che tu abbia bisogno di molte classi, né di membri di quelle classi, per avere un sacco di varietà.

Invece, vuoi solo identificare le direzioni di variazione, ad es. elementi (Fuoco, Ghiaccio, Ombra), effetti (Danno, Volo, Guarigione, Velocità di corsa), magnitudo (100 danni, 50% di aumento della velocità), durata (istantaneo, 100s, 1 giorno) e così via. Ciascuna di queste categorie è una variabile e i valori possono essere letti da JSON / DB / ecc.

Considera per esempio

class Spell {
    Effect effect;
    Set<Elements> elements;
    Number baseMagnitude;
    Number baseDuration;
}

class BuffEffect {
    Effect modifiedEffect;
    Element affectedElement;
    Func<Number, Number> magnitudeModifier; // or some modifier stacking scheme
    Func<Number, Number> durationModifier;
}

class Character {
    List<BuffEffect> activeBuffs;

    void castSpellOn(Spell spell, Character target) {
        Number magnitude = spell.baseMagnitude;
        Number duration = spell.baseDuration;

        var applicable = from buff in activeBuffs
                         where buff.modifiedEffect == spell.effect and spell.elements.Contains(buff.affectedElement)
                         select buff

        foreach (buff in applicable) {
            magnitude = buff.magnitudeModifier(magnitude);
            duration = buff.magnitudeDuration(duration);
            // Or apply stacking scheme
        }

        // Same for target's resistances

        // do stuff to target
    }
}
    
risposta data 16.01.2017 - 15:32
fonte
0

Un buon inizio sarebbe dare a ciascuna cosa nel gioco (sia i personaggi che gli oggetti) una lista di attributi speciali (nota: usando il contenimento qui). Tale elenco potrebbe essere vuoto o potrebbe contenere attributi ereditati da una classe base astratta (ereditarietà singola).

Questo evita qualsiasi necessità per ogni oggetto nel gioco di avere ogni attributo associato ad esso.

Ogni magia (o qualsiasi altra azione) dovrebbe sapere quali attributi influenzeranno il suo risultato. Quindi, quando lanci una magia, cerca l'incantatore e tutto ciò che stanno trasportando per gli attributi rilevanti. Quindi cerca la vittima e tutto ciò che stanno trasportando, per gli attributi pertinenti che potrebbero proteggerli. Ora puoi calcolare il risultato.

    
risposta data 15.02.2017 - 17:33
fonte

Leggi altre domande sui tag