Двигатель уже использует много синглетонов. Если кто-то использовал это, то они должны быть знакомы с некоторыми из них:
Незнакомость не является причиной, по которой следует избегать синглетонов.
Есть много веских причин, почему Singleton необходим или неизбежен. В игровых фреймворках часто используются синглтоны, потому что это неизбежное последствие наличия всего одного аппаратного обеспечения с сохранением состояния. Нет смысла когда-либо хотеть управлять этим оборудованием с несколькими экземплярами соответствующих им обработчиков. Графическая поверхность - это внешнее аппаратное обеспечение с сохранением состояния, и случайная инициализация второй копии графической подсистемы является не чем иным, как катастрофой, так как теперь две графические подсистемы будут сражаться друг с другом за то, кто может рисовать и когда, неконтролируемо перезаписывая друг друга. Аналогично с системой очередей событий они будут бороться за то, кто получает события мыши недетерминированным способом. Когда речь идет о внешнем оборудовании с состоянием, в котором есть только одно из них, синглтон неизбежно предотвращает конфликты.
Другое место, где Синглтон чувствителен, это менеджеры Cache. Кеши - это особый случай. Их не следует считать Синглтоном, даже если они используют те же приемы, что и Синглтоны, чтобы остаться в живых и жить вечно. Службы кэширования являются прозрачной службой, они не должны изменять поведение программы, поэтому, если вы замените службу кэша нулевым, программа все равно должна работать, за исключением того, что она работает медленнее. Однако главная причина, по которой менеджеры кэша являются исключением для синглтона, заключается в том, что нет смысла отключать службу кэширования перед закрытием самого приложения, поскольку это также отбрасывает кэшированные объекты, что лишает смысла иметь его как синглтон.
Это веские причины, по которым эти услуги являются синглетонами. Однако ни одна из веских причин, по которой у вас есть синглтоны, не подходит для перечисленных вами классов.
Потому что они понадобятся мне в самых разных местах в моей игре, и общий доступ был бы очень удобен.
Это не повод для одиноких. Это причины для глобалов. Это также признак плохого дизайна, если вам нужно обойтись вокруг разных систем. Необходимость передавать вещи указывает на высокое сцепление, которое может быть предотвращено хорошей конструкцией OO.
Просто взглянув на список классов в вашем посте, которые, по вашему мнению, должны быть синглетонами, я могу сказать, что половина из них действительно не должна быть синглетонами, а другая половина кажется, что их вообще не должно быть. То, что вам нужно передавать объекты, кажется, связано с отсутствием надлежащей инкапсуляции, а не с реальными хорошими вариантами использования для синглетонов.
Давайте посмотрим на ваши классы по крупицам:
PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)
Просто из названий классов я бы сказал, что эти классы пахнут как антипаттерн Anemic Domain Model . Анемичные объекты обычно приводят к большому количеству данных, которые необходимо передать, что увеличивает связь и делает остальную часть кода громоздкой. Кроме того, анемичный класс скрывает тот факт, что вы, вероятно, все еще мыслите процедурно, а не используете ориентацию объекта для инкапсуляции деталей.
IAP (for in purchases)
Ads (for showing ads)
Почему эти классы должны быть одиночками? Мне кажется, что это должны быть недолговечные классы, которые следует вызывать, когда они необходимы, и деконструировать, когда пользователь завершает покупку или когда рекламу больше не нужно показывать.
EntityComponentSystemManager (mananges entity creation and manipulation)
Другими словами, конструктор и сервисный уровень? Почему этот класс не имеет четких границ и цели даже там, в первую очередь?
PlayerProgress (passed levels, stars)
Почему прогресс игрока отделен от класса Player? Класс Player должен знать, как отслеживать собственный прогресс, если вы хотите реализовать отслеживание прогресса в классе, отличном от класса Player для разделения ответственности, то PlayerProgress должен быть позади Player.
Box2dManager (manages the physics world)
Я не могу комментировать дальше, не зная, что на самом деле делает этот класс, но ясно одно - этот класс плохо назван.
Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
Похоже, что это единственные классы, где Singleton может быть разумным, потому что объекты социальных связей на самом деле не являются вашими объектами. Социальные связи - это просто тень внешних объектов, которые живут в удаленном сервисе. Другими словами, это своего рода кеш. Просто убедитесь, что вы четко отделяете кеширующую часть от служебной части внешней службы, потому что последняя часть не должна быть одиночной.
Один из способов избежать передачи экземпляров этих классов - использовать передачу сообщений. Вместо прямого вызова экземпляров этих классов вместо этого вы отправляете сообщение, адресованное службе Analytic и SocialConnection, асинхронно, и эти службы подписываются на получение этих сообщений и действуют на них. Поскольку очередь событий уже является одноэлементной, трамплинируя вызов, вы избегаете необходимости передавать фактический экземпляр при взаимодействии с одноэлементным.