O mecanismo já usa muitos singletons. Se alguém o usou, deve estar familiarizado com alguns deles:
O desconhecimento não é a razão pela qual os singletons precisam ser evitados.
Há muitas boas razões pelas quais Singleton é necessário ou inevitável. As estruturas de jogos geralmente usam singletons porque é uma consequência inevitável de ter apenas um único hardware com estado. Não faz sentido querer controlar esses hardwares com várias instâncias de seus respectivos manipuladores. A superfície gráfica é um hardware com estado externo e inicializar acidentalmente uma segunda cópia do subsistema gráfico é nada menos que desastroso, já que agora os dois subsistemas gráficos estarão brigando entre si sobre quem deve desenhar e quando, substituindo-se incontrolavelmente. Da mesma forma com o sistema de fila de eventos, eles estarão brigando por quem recebe os eventos do mouse de maneira não-determinística. Ao lidar com um hardware externo com estado no qual existe apenas um deles, o singleton é inevitável para evitar conflitos.
Outro lugar em que Singleton é sensato é com os gerenciadores de cache. Os caches são um caso especial. Eles realmente não devem ser considerados Singleton, mesmo quando usam as mesmas técnicas que os Singletons para permanecer vivo e viver para sempre. Os serviços de armazenamento em cache são transparentes, não devem alterar o comportamento do programa; portanto, se você substituir o serviço de cache por um cache nulo, o programa ainda funcionará, exceto pelo fato de ser executado mais lentamente. A principal razão pela qual os gerenciadores de cache são exceção ao singleton é porque não faz sentido desligar um serviço de cache antes de desligar o próprio aplicativo, porque isso também descartará os objetos em cache, o que impede o ponto de tê-lo como um Singleton.
Essas são boas razões para esses serviços serem singletons. No entanto, nenhum dos bons motivos para ter singletons se aplica às classes que você listou.
Porque precisarei deles em lugares muito diferentes no meu jogo, e o acesso compartilhado seria muito útil.
Essas não são razões para singletons. Essas são razões para os globais. Também é um sinal de mau design se você precisar passar muitas coisas pelos vários sistemas. A necessidade de repassar as coisas indica um alto acoplamento, o que pode ser evitado por um bom design de OO.
Apenas olhando a lista de aulas em seu post que você acha que precisam ser Singletons, posso dizer que metade delas realmente não deve ser singletons e a outra metade parece que elas nem deveriam estar lá em primeiro lugar. O fato de você precisar distribuir objetos parece ser devido à falta de encapsulamento adequado, em vez de bons casos de uso reais para singletons.
Vejamos suas aulas pouco a pouco:
PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)
Apenas pelos nomes das classes, eu diria que essas classes têm cheiro de antipadrão do Modelo de Domínio Anêmico. Objetos anêmicos geralmente resultam em muitos dados que precisam ser passados, o que aumenta o acoplamento e torna o restante do código complicado. Além disso, a classe anêmica oculta o fato de que você provavelmente ainda está pensando procedimentalmente, em vez de usar a orientação a objetos para encapsular detalhes.
IAP (for in purchases)
Ads (for showing ads)
Por que essas classes precisam ser singletons? Parece-me que essas classes devem ter vida curta, que devem ser criadas quando necessárias e desconstruídas quando o usuário concluir a compra ou quando os anúncios não precisarem mais ser exibidos.
EntityComponentSystemManager (mananges entity creation and manipulation)
Em outras palavras, um construtor e uma camada de serviço? Por que essa classe sem limites claros nem propósito existe mesmo em primeiro lugar?
PlayerProgress (passed levels, stars)
Por que o progresso do jogador é separado da classe Player? A classe Player deve saber como acompanhar seu próprio progresso. Se você deseja implementar o rastreamento de progresso em uma classe diferente da classe Player para separar responsabilidades, PlayerProgress deve ficar atrás do Player.
Box2dManager (manages the physics world)
Não posso comentar mais sobre esse assunto sem saber o que essa classe realmente faz, mas uma coisa é clara é que essa classe tem um nome ruim.
Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
Esta parece ser a única classe em que Singleton pode ser razoável, porque os objetos de conexão social não são realmente seus objetos. As conexões sociais são apenas uma sombra de objetos externos que vivem em um serviço remoto. Em outras palavras, esse é um tipo de cache. Apenas certifique-se de separar claramente a parte de armazenamento em cache com a parte de serviço do serviço externo, porque a última parte não precisa ser um singleton.
Uma maneira de evitar passar instâncias dessas classes é usando a passagem de mensagens. Em vez de fazer uma chamada direta para instâncias dessas classes, você envia uma mensagem endereçada ao serviço Analytic e SocialConnection de forma assíncrona, e esses serviços são assinados para receber essas mensagens e agir sobre a mensagem. Como a fila de eventos já é um singleton, trampolim a chamada, você evita a necessidade de passar uma instância real ao se comunicar com um singleton.