Паттерны проектирования в Cocos2d-x

в 18:57, , рубрики: Behavioral, bridge, cocos2d-x, command, component, composite, Creational Patterns, Data Locality, Decoupling Patterns, Dirty Flag, flyweight, Observer, Optimization Patterns, patterns, prototype, singleton, Structural Patterns, ооп, паттерны, Программирование, Проектирование и рефакторинг, разработка игр, шаблоны проектирования

Привет! Представляю вашем вниманию перевод статьи "Design Patterns in Cocos2d-x" автора Aleksei Pinchuk.

Статья будет интересна для разработчиков Cocos2d-x и тех, кто изучает паттерны. Она выполнена в форме краткого конспекта, в котором можно быстро посмотреть где применяется тот или иной паттерн в Cocos2d-x. Целью статьи не является полное описание каждого паттерна.

Порождающие паттерны

Prototype

Prototype определяет интерфейс копирования объекта. Создание нового объекта происходит копированием состояния объекта. Например, если мы вызовем clone() у объекта Animation мы создадим объект Animation с точно такими же параметрами.

image

Singleton

Да лаааадно.

Структурные паттерны

Flyweight

Flyweight стоит применять для раздельного использования одних и тех же ресурсов без создания большого количество экземпляров одного и того же ресурса. Это дает возможность эффективно использовать память. Этот паттерн хорошо подходит для отображения текста.

Для отображения текста используется класс Label. Чтобы отобразить текст нам нужен шрифт. Чтобы получить TTF шрифт нужно воспользоваться методом getFontAtlasTTF класса FontAtlasCache. FontAtlasCache является пулом всех шрифтов используемых в приложении. Шрифт хранится до тех пор, пока хотя бы один объект использует его. Если у нас есть 10 объектов Label с одинаковым шрифтом, то все они будут использовать один и тот же экземпляр класса FontAtlas.

image

FontAtlas содержит данные для отображения символа — текстуры и координаты. Размер текстуры 512х512 и таких текстур может быть создано несколько, если все символы не влезут в одну. Создаются только те символы, которые используются объектами ссылающимися на этот шрифт. Label получает из FontAtlas структуру FontLetterDefinition для каждого символа. Основываясь на данных из этой структуры, Label создает спрайт и помещает его в свой BatchNode. В общем, мы можем видеть насколько тщательно оптимизирован Label.

image

Еще одним хорошим примером раздельного использования ресурсов является использование текстур спрайтами. Текстура для каждого загруженного файла хранится в единственном экземпляре. Несколько спрайтов созданных из одного и того же файла, буду ссылать на один и тот же экземпляр текстуры.

image

Можем найти некоторую закономерность — если имя класса заканчивается на Cache, то он производит объекты для раздельного использования.

Bridge

Bridge разделяет абстракцию и реализацию. Этот паттерн чаще всего использует для реализации кроссплатформенных объектов. Классический Bridge можно встретить в EditBox и Downloader.

image

image

Также этот паттерн хорошо подходит для GLView, AudioEngine, Controller и WebView. Однако, в них мы можем видеть разные способов реализации одной и той же задачи — разделение абстракции от реализации.

Composite

Задачей Composite является построение деревьев и унифицирование доступа к компонентам дерева. Обычно схема Composite выглядит так:

image

Composite это составной объект состоящий из нескольких объектов унаследованных от Component. Тем самым мы можем воспринимать несколько объектов как один. В Cocos2d-x Node является и Composite и Component.

image

Благодаря Node создается граф сцена каждым узлом которой является Node.

Паттерны поведения

Command

Command инкапсулирует запрос как объект, тем самым позволяя ставить запросы в очередь. Другие варианты использования не будем рассматривать. В Cocos2d-x это паттерн используется для создания очереди для Renderer. Благодаря этому паттерну было убрано использования OpenGL API внутри объектов. Один и тот же код для OpenGL выведен в одну команду и не копируется несколько раз. Также это может облегчить переход на другие графические API, но не сделает его легким.

image

Observer

Определяет зависимость типа «один ко многим» между объектами. В Cocos2d-x субъектом (subject) является EventDispatcher, а наблюдателем(observer) EventListener. EventDispatcher не является singleton, мы можем унаследовать от него свой EventDispatcher. Director через EventDispatcher оповещает наблюдателей об изменениях состояния акселерометра, мыши, клавиатуры и т.д. Также можно создавать пользовательские сообщения EventCustom. Эти сообщения имеют имя и данные, которые надо передать наблюдателю. Это является альтернативой для NotificationCenter который уже помечен как deprecated. Director также определяет несколько полезных EventCustom, например, EVENT_BEFORE_UPDATE, EVENT_AFTER_UPDATE и другие. EVENT_BEFORE_UPDATE удобно использовать при работе с Box2D. Например, перед апдейтом физического мира менять linearVelocity какому-нибудь объекту.

image

Паттерны снижения связанности (Decoupling Patterns)

Component

Я думаю каждый разработчик игр знает про entity component systems. Программисты Unity3d точно должны знать. Такая система снижает связность компонентов и позволяет добавлять компоненты в сущность таким образом, что сущность даже не будет об этом знать. Эта система есть в Cocos2d-x. В каждом Node есть контейнер для компонентов. Свои компоненты нужно наследовать от класса Component. Каждый компонент имеет ссылку на Node который им владеет (owner), метод update, а также метод serialize для обработки сообщений между компонентами и другими объектами.

image

Паттерны оптимизации

Data Locality

Задачей этого паттерна является ускорение доступа к памяти с помощью более удобного размещения данных для кэширования процессором. Этот паттерн часто используется для создания частиц. И в Cocos2d-x он тоже используется для создания частиц. В ParticleSystem все данные о частицах хранятся в ParticleData. ParticleData содержит данные для всех частиц. Практически каждый член ParticleData является массивом. Например, в массиве posx хранятся координаты частиц по оси х.

class ParticleData
{
public:
    float* posx;
    float* posy;
	//...
}; 

Размещение данных в памяти последовательно, в порядке их обработки позволяет быстро их обрабатывать и максимально избегать кэш-промахов (cache miss).

Dirty Flag

Достаточно часто встречается в Сосоs2d-x. Этот паттерн откладывает выполнение медленной работы до тех пор, пока потребуется ее результат. Его можно встретить в классах Scene, Camera, Node, Label, ParticleSystem, Sprite и многих других.

Автор: BillPearson

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js