Фабрика для расстановки объектов игровой сцены

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

На первоначальном этапе разработке вполне нормально увидеть в игре такой код:

Однако, рано или поздно игра вырастает больше чем в техническую демку и нам будет нужно редактировать уровни чаще чем код. Так или иначе, нам придётся хранить уровни в каком-то внешнем хранилище, будь то база данных, xml-файлы или же бинарные файлы своего формата.

Дело ясно: нам нужен менеджер уровней, который будет считывать данные об объектах из внешнего хранилища и создавать их на уровне.

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

switch-case

Для простоты предположим, что файл уровня имеет следующую структуру

Самый простой способ обработать такие данные, это использовать конструкцию switch-case (if .. else if для строковых идентификаторов в нашем случае)

Первое время такой код может выглядеть достаточно неплохо, но с рано или поздно он непременно разрастётся в огромное полотно кода, в котором будет сложно что-то найти.

Паттерн фабрика объектов

Данный паттерн достаточно подробно описан в книге Александреску А. «Современное проектирование на C++». Поэтому, я не буду особенно касаться реализации данного паттерна.

В конечном счёте, я реализовал свою Фабрику аналогично той, что описана в книге. Но изначально я не понял идеи синглтона и реализовал методы фабрики в виде «статического класса», а затем поплатился за это.

Суть фабрики в следующем:

  • Фабрика хранит конструкторы в талбице вида «ключ-значение», где ключом является идентификатор нужного типа.
  • Перед использованием фабрики необходимо зарегистрировать в ней все конструкторы.

Конструкторы могут быть зарегистрированы в любом месте программы, однако, наиболее удобным я считаю регистрацию в *.cpp файле того класса, чей конструктор регистрируется. Это позволяет исключать регистрацию в фабрике вместе с удалением класса, а так же, держать весь зависимый код в одном месте.

Полный исходный код

 

В каждый файл *.cpp классов, которые должны быть зарегистрированы в фабрике, добавляется следующий код.

[свернуть]

UPD: Проблема со статическими библиотеками

Проявилась следующая проблема:

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

Решения подсказали два:

  1. Выносить регистрации в отдельный файл в проекте конечного приложения.
  2. Использовать параметр компоновщика, запрещающий исключать код библиотек при линковке.

Решения не очень красивые, но в первом варианте в отличие от случая со switch-case, регистрации можно производить в конечной программе, что не так затратно как модификация блоков swith-case в библиотеке.

Поделиться

Оставить ответ