Имам система за верижна атака за игра, но тя става много сложна много бързо

Аз съм в курс за разработка на игри за моите бакалаври по compsci и нашият проект е да направим XNA игра. Създаваме игра в стил jrpg и измислихме страхотна идея за битката: на всеки ход поставяте 3 умения на опашка, въз основа на уменията и избрания ред могат да се прилагат различни бонус ефекти. Направихме основен с 2 умения: атака и огън.

Започвате с първото умение, след което стрелките показват второто и третото избрано умение. Кутийките на второ и 3-то ниво показват ефекта, добавен в тази точка.

Основите: Атака+Атака = По-висок критичен шанс за втората атака. Fire+Attack = Добавя някои щети от огън към първоначалните щети на атаката. Fire+Fire = Прилага състояние на изгаряне

По-трудната част е, когато ударите 3 подред, тъй като те правят нещо като специална атака. Атаката прави 1/4 шанс за критичен удар, Огънят нанася двойна щета Огнена атака (наречена Огнена топка).

Прилагането на това в if-изявления може да стане болезнено с повече умения. Броят на операторите if е равен на сумата от n^n от 1 до n, така че ако искаме 6 умения, ще трябва да напишем 6+36+216=258 оператора if! Много от които също биха били излишни! Това би било предразположено към грешки, тъй като ще трябва старателно да изработим всеки оператор if, така че да са в правилната позиция, докато кодираме нашите блок-схеми.

Ето защо решихме, че трябва да имаме някои обобщени комбинации със статичен ефект, може би увеличаване на брояч, ако може да бъде кумулативен, тогава, когато имаме 3 подред, извикване на функцията, която има специалната атака на това умение.

Първото нещо, което ми дойде на ум, беше машина с краен край. Ще може да се справи с всички случаи, с изключение на специалните според мен. Може би натискащ се автомат? Основният проблем, който имам, е, че нямам представа как бих ги внедрил в код. Класът, в който ги научих, беше теоретичен.

Има ли други по-ефективни или по-лесни за писане/кодиране методи?


person Portaljacker    schedule 15.11.2012    source източник
comment
Освен ако нямате по-добър модел, трябва да се придържате към FSM. Както вече казахте, може да стане доста голямо.   -  person John Dvorak    schedule 15.11.2012
comment
но изглежда, че уменията просто модифицират някои модификатори, които след това модифицират следващото умение, така че вашият модел може да бъде нещо като {norm-bonus, fire-bonus}; {n,f} + fire => {0,f*1.1} + {burn:f}; {n,f} + normal => {n+0.01,f*0.5} + {atk:n*f} (прочетете: ако е приложен огън, увеличете модификатора на огъня с 10% и направете малко изгаряне. Ако нормалната атака е приложен, увеличете шанса за критичен удар с 1%, намалете ефекта от огъня с 50% и нанесете някои щети, равни на шанса за критичен удар, умножен по ефекта от огъня)   -  person John Dvorak    schedule 15.11.2012


Отговори (2)


Вместо това бих измислил някакъв рекурсивен модел:

enum Outcomes { Crit, DoubleCrit, FireDMG, Burn, NoEffect }

abstract class Attack 
{ 
    public Attack() { Child = null; }

    List<Outcomes> GetOutcomes(); 
    protected virtual Attack Child; 
}
class Melee : Attack 
{ 
    public Melee() : base() { }
    public Melee(Attack child) : base() { Child = child; }

    List<Outcomes> GetOutcomes()
    {
        List<Outcomes> ret = new List<Outcomes>();
        if(Child != null) ret.Add(Child.GetOutcomes());

        if(ret.Contains(Outcomes.Crit))
            ret.Add(Outcomes.DoubleCrit);
        else
            ret.Add(Outcomes.Crit);

        return ret;
    }
}
class Fire : Attack 
{ 
    public Fire() : base() { }
    public Fire(Attack child) : base() { Child = child; }

    List<Outcomes> GetOutcomes()
    {
        List<Outcomes> ret = new List<Outcomes>();
        if(Child != null) ret.Add(Child.GetOutcomes());

        List<Outcomes> PossibleOutcomes = new List<Outcomes>();        

        PossibleOutcomes.Add(Outcomes.FireDMG);
        PossibleOutcomes.Add(Outcomes.Burn);

        if(ret.Contains(Outcomes.Burn)) PossibleOutcomes.Add(Outcomes.Fireball)
        if(ret.Contains(Outcomes.FireDMG)) PossibleOutcomes.Add(Outcomes.NoEffect);

        // Use some randomization method to select an item from PossibleOutcomes
        int rnd = 2; // Totally random number.
        ret.Add(PossibleOutcomes[rnd]);

        return ret;
    }
}

Тогава за верижни атаки просто използвайте:

Attack ChosenAttack = new Melee(new Fire(new Melee()));

Или ако приемем, че потребителят избира всеки клон, вие просто трябва да запазите последния и непрекъснато да го добавяте като дете към следващата атака, която избере.

Attack ChosenAttack = new Melee();

// Some events occur...

ChosenAttack = new Fire(ChosenAttack);

// Some more...

ChosenAttack = new Melee(ChosenAttack);

Извинявам се, ако не разбирам напълно проблема и предложението ми няма да работи.

person Spencer Ruport    schedule 15.11.2012

Звучи сякаш наистина искате да имате възможността да персонализирате какви ефекти ще имат различните избори за играча, като същевременно избягвате да пишете целия код.

Един от начините да направите това е да създадете структура от данни, която ще обработва резултата от всяка комбинация и ще направи задачата за създаване на нови комбинации по-лесна за справяне с потенциално големия брой възможности в нещо по-лесно за промяна от кода трябва да прекомпилирате като него в XML и да заявите резултат въз основа на избора на потребителите.

Може да имате клас нещо като следното, за да съхраните резултата.

public class AttackResult
{
    public int ChanceToCrit { get;set; }
    public int ChanceToBurn { get;set; }
    public int FireDmg { get;set; }
}

Що се отнася до XML, вероятно ще имате нещо подобно,

<Options>
    <Attack>
        <AttackResult>
            <ChanceToCrit>5</ChanceToCrit>
        </AttackResult>
        <Attack>
            <AttackResult>
                <ChanceTCrit>10</ChanceToCrit>
            </AttackResult>
        </Attack>
    </Attack>
    <Fire>
    </Fire>
</Options>

Схванахте идеята. Когато обработвате избора на вашите потребители (само предполагам тук), можете да съхранявате какво правят в Enum например. Така че, за да използвате XML с това, можете да направите нещо подобно на

public enum ActionType
{
    Attack,
    Fire,
    None
}

allActions ще трябва да са в съответствие, за да може този пример да работи с вашата идея за игра според мен.

public ActionResult ProcessActions(List<ActionType> allActions)
{
    string xpathquery = "Options";

    foreach(var playerAction in allActions)
    {
        if(playerAction == ActionType.None)
           break;
        xpathquery += "/" + playerAction.ToString();
    }

    //Query xmldocument of all options
    var result = Singleton.ActionsDoc.SelectSingleNode(xpathquery);
    string attackRes = result.InnerXml;
    XmlSerializer serializer = new XmlSerializer(typeof(ActionResult))
    //Might need to prepend something to attackRes before deserializing, none of this is tested
    byte[] allBytes = Encoding.UTF8.GetBytes(attackRes);
    MemoryStream ms = new MemoryStream(allBytes);

    ActionResult result = (ActionResult)serializer.Deserialize(ms);

    return result;
}

Тази опция има своите недостатъци, като например това, че XmlSerializer е доста бавен, но това означава, че можете да създадете прост инструмент, който да ви помогне да генерирате xml, необходим за всички ваши опции, и да променяте стойностите съвсем просто за балансиране на играта.

Един вариант на масата. Надявам се това да помогне.

person Darren Reid    schedule 15.11.2012