Шаблон виконання ігрових дій


11

Чи існує загальноприйнята модель для виконання різних дій у грі? Спосіб, коли гравець може виконувати дії, а також те, що AI може виконувати такі дії, як рух, атака, самознищення тощо.

В даний час у мене є абстрактна BaseAction, яка використовує .NET-дженерики для визначення різних об'єктів, які повертаються різними діями. Це все реалізовано за схемою, подібною до команди, де кожна дія відповідає за себе і робить усе необхідне.

Моє міркування бути абстрактним таким чином, що у мене може бути один ActionHandler, і AI може просто викласти в чергу різні дії, реалізуючи baseAction. А загальна причина полягає в тому, що різні дії можуть повернути інформацію про результат, що стосується дії (оскільки різні дії можуть мати абсолютно різні результати в грі), а також деякі загальні реалізації перед діями та після виконання.

Отже ... чи є більш прийнятий спосіб зробити це, чи це звучить добре?


Це добре звучить, питання - що ви розумієте під чергою? Більшість ігор мають дуже швидку реакцію? "AI може
чергувати

Гарна думка. Черги немає. Він просто повинен знати, чи зайнятий, а якщо ні, виконувати дії.
Arkiliknam

Відповіді:


18

Я не думаю, що існує один прийнятий спосіб реалізації цієї концепції, але я дуже хотів би поділитися тим, як я зазвичай маю справу з цим у своїх іграх. Це трохи поєднання шаблону дизайну Command та композиційного шаблону дизайну.

У мене є абстрактний базовий клас для дій, який є не що інше, як обгортку навколо Updateметоду, який називається кожен кадр, і Finishedпрапор, який вказує, коли дія закінчиться.

abstract class Action
{
    abstract void Update(float elapsed);
    bool Finished;
}

Я також використовую композитний шаблон дизайну, щоб створити тип дій, який здатний приймати та виконувати інші дії. Це теж абстрактний клас. Зводиться до:

abstract class CompositeAction : Action
{
    void Add(Action action) { Actions.Add(action); }
    List<Action> Actions;
}

Тоді у мене є дві реалізації складених дій, одна для паралельного виконання та одна для послідовного виконання . Але краса полягає в тому, що оскільки паралельність і послідовність є самими діями, їх можна поєднувати , щоб створити більш складні потоки виконання.

class Parallel : CompositeAction
{
    override void Update(float elapsed) 
    {
        Actions.ForEach(a=> a.Update(elapsed));
        Actions.RemoveAll(a => a.Finished);
        Finished = Actions.Count == 0;
    }
}

І той, що керує послідовними діями.

class Sequence : CompositeAction
{
    override void Update(float elapsed) 
    {
        if (Actions.Count > 0) 
        {
            Actions[0].Update(elapsed);
            if (Actions[0].Finished)
                Actions.RemoveAt(0);
        }
        Finished = Actions.Count == 0;
    }
 }

З цим на місці просто питання створення конкретних реалізацій дій та використання операцій Parallelта Sequenceдій для управління потоком виконання. Я закінчу прикладом:

// Create a parallel action to work as an action manager
Parallel actionManager = new Parallel();

// Send character1 to destination
Sequence actionGroup1 = new Sequence();
actionGroup1.Add(new MoveAction(character1, destination));
actionGroup1.Add(new TalkAction(character1, "Arrived at destination!"));
actionManager.Add(actionGroup1);

// Make character2 use a potion on himself
Sequence actionGroup2 = new Sequence();
actionGroup2.Add(new RemoveItemAction(character2, ItemType.Potion));
actionGroup2.Add(new SetHealthAction(character2, character2.MaxHealth));
actionGroup2.Add(new TalkAction(character2, "I feel better now!"));
actionManager.Add(actionGroup2);

// Every frame update the action manager
actionManager.Update(elapsed);

Раніше я успішно використовував цю систему для керування всім ігровим процесом у графічній пригоді, але це, мабуть, мало б працювати майже все. Також було досить просто додати інші типи складених дій, які використовувались для створення циклів виконання та умовних умов.


Це виглядає як дуже приємне рішення. З цікавості, як тоді ви даєте інтерфейсу знати, що малювати? Чи містять ваші ігрові об'єкти (як символи) стан, який використовується для ідентифікації того, що сталося для цілей візуалізації, чи це робить саме дія?
Аркілікнам

1
Зазвичай мої дії змінюють лише стан сутностей, і будь-які зміни в наданому результаті відбуваються як наслідок зміни цього стану, а не за допомогою самих дій. Наприклад, для рендерінга в режимі прямого режиму не потрібен додатковий крок, оскільки Drawметод вже побудований на основі стану сутності, а зміни є автоматичними. У рендері, який зберігається в режимі, наприклад Flash, ви можете використовувати шаблон, який можна спостерігати, щоб змінити ваші сутності, розповсюджуючись на об'єкти відображення, або здійснити з'єднання вручну всередині сутності.
David Gouveia

1
У першій ситуації скажімо, що у вашого Characterкласу є Positionвластивість та Drawметод, який читає, що таке поточне значення Position, і малює там правильне зображення. У цій ситуації вам потрібно лише оновити значення, Positionщоб результат автоматично відображався на екрані.
Девід Гувейя

1
Друга ситуація полягає в тому, що у вас Characterє Positionвластивість, але він делегує візуалізацію на якийсь Spriteоб'єкт, який автоматично відображається графіком сцени чи іншим. У цій ситуації ви повинні переконатися, що і положення персонажа, і положення спрайта завжди синхронізовані, що передбачає трохи більше роботи. Але в будь-якому випадку я не бачу, чому менеджер дій повинен мати щось із цим. :)
David Gouveia

1
Обидва способи мають свої переваги та недоліки. Я пішов із другим методом для своєї 2D гри, і часом шкодував про це, оскільки суттєво складніше тримати все в синхронізації. Але є і переваги, наприклад, коли ви намагаєтесь визначити, на яку особу було натиснуто, або що слід або не слід малювати, оскільки все, що буде надано, міститься в одній структурі даних, а не розкидано між N типами сутності.
Девід Гувейя
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.