Unity 6.3
0 онлайн 86 гостей 3 в системе
Вход
Модульная игровая архитектура на ScriptableObjects Глава 8 из 12 Оригинал, стр. 59

Паттерн «Команда»

Русский

Паттерн «Команда»

Вместо прямого вызова метода паттерн «Команда» позволяет инкапсулировать один или несколько вызовов методов в «объекте команды».

Затем объекты команд сохраняются в коллекции, например в очереди или стеке, которая служит небольшим буфером. Это дает дополнительный контроль над выполнением каждой команды. Типичные сценарии включают воспроизведение последовательности действий с заданными интервалами и поддержку их отмены.

Вероятно, вы уже встречали этот прием в любимых игровых жанрах: - В стратегии в реальном времени паттерн «Команда» можно использовать, чтобы помещать в очередь действия юнитов и зданий. Игра будет выполнять команды для зданий по мере появления ресурсов, а для юнитов - последовательности перемещений. - В пошаговой стратегии игрок может выбрать юнита, а затем сохранить его перемещения или действия в очереди либо другой коллекции. В конце хода игра выполнит все команды из очереди игрока. - В головоломке паттерн «Команда» позволяет игроку отменять и повторно выполнять действия.

- В файтинге определенная последовательность нажатий кнопок или движений стика геймпада может запускать комбо и специальные приемы. Для реализации паттерна «Команда» можно использовать ScriptableObject. Например, можно создать команду, задающую перемещение Transform. Если каждое действие обернуть в отдельный объект, управлять им становится проще. Сначала определите интерфейс ICommand с методами Execute и Undo. Вместо интерфейса также можно использовать абстрактный класс:

C#
public interface ICommand {
    public abstract void Execute();
    public abstract void Undo();
}

Затем реализуйте интерфейс ICommand в ScriptableObject. Каждый объект команды получает собственные реализации методов Execute и Undo.

<<интерфейс>>

реализует

Реализация паттерна «Команда» с помощью ScriptableObject

После этого MonoBehaviour или ScriptableObject может определить буфер команд, содержащий объекты команд. В качестве буфера подойдет коллекция: список, стек, массив или очередь. <<интерфейс>>

зависит

Объект верхнего уровня

реализует

Объект нижнего уровня

CommandManager хранит коллекцию объектов ICommand.

Такая простая структура позволяет выполнять команды по очереди. Представьте обучающий эпизод или кат-сцену, в которой GameObject выполняет заданную последовательность действий или анимаций. Паттерн «Команда» отлично подходит для этой задачи. Каждая команда является отдельным объектом, поэтому их легко менять местами. Достаточно выбрать способ организации CommandBuffer: - Если использовать стек, при выполнении помещайте команды в него. При отмене извлекайте команду из основного стека и помещайте в отдельный стек повторного выполнения.

- Если используется список или массив, отслеживайте индекс текущей команды. При отмене уменьшайте индекс, а при повторном выполнении увеличивайте. Один из вариантов реализации отменяемого перемещения показан в классах MoveCommandSO и CommandManager из примера проекта. В простой обучающей сцене подписи к элементам игрового поля объясняют правила игры. Нажмите кнопку Next («Далее»), чтобы перейти к следующему пояснению. Кнопка Back («Назад») позволяет просматривать инструкции в обратном порядке.

Попадите мячом за ракетку соперника и наберите больше очков!

КАК ИГРАТЬ ДВИЖЕНИЕ РАКЕТОК

НАЗАД

ДАЛЕЕ

P1: W или S

P2: ВВЕРХ/ВНИЗ

Кнопки Назад и Далее

Простая реализация паттерна «Команда».

Подробнее о паттерне «Команда» можно узнать из электронной книги «Повышаем уровень кода с паттернами проектирования и SOLID». Также ознакомьтесь с публикацией сообщества «Паттерн "Команда" с ScriptableObject», где показана реализация этого паттерна с помощью ScriptableObject.

ScriptableObject или классы C#? Выбирая архитектуру кода для проекта, важно учитывать навыки и предпочтения команды, а также требования игры к производительности. Одним дизайнерам удобнее работать интерактивно в редакторе Unity, а другим полностью в коде C#.

Учитывайте эти факторы, чтобы с кодовой базой было удобно работать всем участникам команды. Ни один паттерн не подходит абсолютно для всех задач, поэтому перед внедрением тщательно оцените его преимущества и недостатки. Помните: «правильная» архитектура кода просто та, которая лучше всего подходит вашей команде и проекту.

English

The Command pattern

Instead of invoking a method directly, the command pattern allows you to encapsulate one or more method calls as a “command object.”

Then, you store these command objects in a collection, like a queue or a stack, which works as a small buffer. This gives you extra flexibility to control each command object’s execution. Common applications include playing back a series of actions with specific timing or making those actions undoable. You’ve likely encountered this in some of your favorite game genres: —

In a real-time strategy game, the command pattern could be used to queue up actions of units and buildings. The game would then execute each building command as resources became available or as a series of movement actions for the unit.

In a turn-based strategy game, the player could select a unit and then store its moves or actions in a queue or other collection. At the end of the turn, the game could execute all of the commands in the player’s queue.

In a puzzle game, the command pattern could allow the player to undo and redo actions.

In a fighting game, reading button presses or gamepad motions in a specific command list could perform combos and special moves.

You can use ScriptableObjects to implement the command pattern. For example, you could create a command to define a Transform’s movement. Wrapping each action within a separate object results in extra control. You’ll define an interface ICommand an Execute method and an Undo method (you could also use an abstract class):

C#
public interface ICommand {
    public abstract void Execute();
    public abstract void Undo();
}

Then, have a ScriptableObject implement the ICommand interface. Each Command object fills out its own Execute and Undo methods with its own implementation details.

Implementing the command pattern with a ScriptableObject

A MonoBehaviour or ScriptableObject can then define a command buffer that will contain the command objects. This can be a collection, such as a list, stack, array, or queue.

A CommandManager maintains a collection of ICommand objects.

This simple structure lets you execute the commands in sequence. Imagine a tutorial or cutscene that moves a GameObject through a prescribed set of actions or animations. The command pattern is well suited for that. Because each command is a separate object, it’s easy to reorder them. Just decide how you want to maintain the CommandBuffer: —

If creating it as a stack, you push commands to the stack when executing them. When undoing an action, you can pop it off and keep a separate redo stack.

If you’re using a list or array, track the current Command’s index, then increment or decrement the index as you need to undo or redo commands.

See the MoveCommandSO and CommandManager classes in the sample project for one example of undoable movement. Here, a rudimentary tutorial scene labels parts of the game board to explain how to play. Click the Next button to advance through the explanatory text. Likewise, click the Back button to cycle in reverse through the instructions.

A simple implementation of the command pattern.

You can find out more about the command pattern in the e-book Level up your code with design patterns and SOLID. Also, see this community post, Command pattern with ScriptableObjects, which demonstrates this pattern with ScriptableObjects.

ScriptableObjects or C# classes? When deciding on the right code architecture for your project, it’s important to consider the skills and preferences of your team, as well as your game’s performance requirements. While some designers may prefer to use the Unity Editor interactively, others may prefer to work entirely in C# code. Take these factors into account when creating a codebase that’s easy for everyone on your team to work with. Of course, no design pattern is a one-size-fits-all solution, and it’s important to carefully evaluate the pros and cons of each before implementing it. Remember that the “right” code architecture is just the one that works best for your team and your project.