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


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.