Desenvolver jogos para todas as telas

Ao desenvolver um jogo para Android, é importante prever a variedade de experiências possíveis para os jogadores e permanecer adaptável às necessidades de interação deles em tempo real. Ao oferecer compatibilidade com diferentes experiências do jogador, você torna a jogabilidade mais flexível, ajudando a ampliar o alcance do seu jogo.

As diferenças específicas na experiência dos jogadores incluem as seguintes:

  • Formatos de dispositivos: embora os smartphones ofereçam a experiência de dispositivo tradicional do Android, é possível interagir com jogos em outros formatos. Os dispositivos ChromeOS podem executar um contêiner Android que exibe seu jogo. Os tablets que executam o Android são compatíveis com vários níveis diferentes de fidelidade. Os dispositivos Android TV são compatíveis com experiências mais detalhadas e imersivas. Os jogadores podem simular um ambiente de várias janelas usando uma ferramenta de extensão de tela. E, ao usar dispositivos dobráveis, os jogadores podem mudar o tamanho da tela durante uma sessão de jogo.
  • Métodos de interação: os jogadores podem fornecer informações tocando na tela do dispositivo, mas também podem usar um mouse, touchpad, teclado ou controle. Além disso, a disponibilidade das ferramentas de extensão de tela e dos dispositivos dobráveis permite que os jogadores aproveitem seu jogo em uma tela maior, tornando as sessões de jogo mais longas e as interfaces complexas mais viáveis.
  • Compatibilidade de hardware: alguns dispositivos com Android não contam com o hardware mais comum encontrado em dispositivos portáteis, como câmera traseira, GPS e conectividade de rede. Seu jogo precisa se adaptar ao hardware disponível e lidar com situações em que certos recursos não podem ser usados.

Este guia apresenta as práticas recomendadas relacionadas ao desenvolvimento do seu jogo para diferentes tipos de telas e interações do usuário. Aqui, você também encontrará sugestões sobre como projetar seu jogo e desenvolver uma estratégia de testes eficiente.

Práticas recomendadas para criação de jogos

Ao planejar o design e a arquitetura do seu jogo, siga as práticas recomendadas descritas nas seções a seguir.

Responder manualmente a mudanças de configuração

Quando o sistema Android detecta uma mudança na configuração, como no tamanho da tela, na orientação da tela ou no método de entrada, o sistema reinicia a atividade atual por padrão. Para preservar o estado em um app ou jogo, por padrão, a atividade chama onSaveInstanceState() antes de reiniciar e onRestoreInstanceState() após a reinicialização. Esse processo, no entanto, requer que a atividade atualize todos os serviços e recursos associados. Para saber mais sobre esse comportamento padrão, consulte o guia sobre como lidar com mudanças de configuração.

Uma sessão típica de jogo sofre várias mudanças de configuração. Se o jogo permitir que o sistema lide com cada uma delas, o cenário do jogo será destruído e reiniciado diversas vezes, reduzindo o desempenho. Por isso, é altamente recomendável que você lide com essas mudanças de configuração no seu jogo por conta própria.

Para aprender a adicionar essa lógica de mudança de configuração ao jogo, consulte a seção sobre como criar gerenciadores personalizados de mudanças de configuração.

Criar uma arquitetura flexível

Para que seu jogo seja compatível com o maior número possível de dispositivos, siga estas práticas recomendadas:

  • Implantar Android App Bundles em vez de APKs individuais. Os Android App Bundles permitem empacotar artefatos de resoluções diferentes e modelos de arquitetura distintos, como x86 e ARM, em um único artefato. Melhor ainda, os Android App Bundles são compatíveis com limites de tamanho maiores para seu jogo. Cada APK básico pode ter até 150 MB, e o pacote pode ter muitos gigabytes.
  • Adicionar compatibilidade para arquiteturas x86. Essa etapa melhora o desempenho do seu jogo em dispositivos que não são compatíveis com ARM, uma vez que esses dispositivos agora podem executar instruções sem ter que traduzi-las primeiro.

Adicionar compatibilidade com o Vulkan

Ao oferecer compatibilidade com o Vulkan, seu jogo pode atingir um desempenho gráfico muito melhor. A maioria dos dispositivos é compatível com essa API gráfica.

Criar gerenciadores personalizados de mudanças de configuração

Para declarar os tipos de mudanças de configuração que o jogo processa, adicione o atributo android:configChanges a cada elemento <activity> no manifesto que representa uma tela ou interface complexa.

O snippet de código a seguir demonstra como declarar que seu jogo processa mudanças no tamanho e na orientação da tela, bem como no método de entrada:

<activity ...
    android:configChanges="screenSize|orientation|keyboard|keyboardHidden">
</activity>

Agora, quando as mudanças de configuração declaradas ocorrem, o sistema invoca um método diferente, o onConfigurationChanged(). Dentro desse método, adicione lógica para atualizar a IU do jogo:

  • Atualize o fator de escala e orientação da tela. Lembre-se de que, para fins de desempenho, pode ser melhor mudar apenas uma dimensão da IU do jogo.
  • Identifique o método de entrada ideal para o jogador usar.

Lidar com mudanças de configuração da tela

Seu jogo processa manualmente as mudanças de tamanho e de orientação da tela sempre que você inclui os valores screenSize e orientation, respectivamente, em um atributo android:configChanges. Você pode usar esses novos valores para atualizar o conteúdo do cenário e as áreas de entrada do jogador. Para aprender a projetar o layout do jogo de modo que ele possa ser atualizado mais facilmente, consulte o guia sobre como oferecer compatibilidade para diferentes tamanhos de tela.

Na implementação de onConfigurationChanged() do jogo, use o objeto Configuration transmitido e o objeto Display do gerenciador de janelas para determinar os valores atualizados para o tamanho e a orientação da tela, respectivamente.

O snippet de código a seguir mostra como ver o tamanho e a orientação atualizados da tela do seu jogo:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    val density: Float = resources.displayMetrics.density
    val newScreenWidthPixels = (newConfig.screenWidthDp * density).toInt()
    val newScreenHeightPixels = (newConfig.screenHeightDp * density).toInt()

    // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or
    // Configuration.ORIENTATION_LANDSCAPE.
    val newScreenOrientation: Int = newConfig.orientation

    // Get general rotation; one of: ROTATION_0, ROTATION_90, ROTATION_180,
    // or ROTATION_270.
    val newScreenRotation: Int = windowManager.defaultDisplay.rotation
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    float density = getResources().getDisplayMetrics().density;
    int newScreenWidthPixels = (int) (newConfig.screenWidthDp * density);
    int newScreenHeightPixels = (int) (newConfig.screenHeightDp * density);

    // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or
    // Configuration.ORIENTATION_LANDSCAPE.
    int newScreenOrientation = newConfig.orientation;

    // Get general rotation; one of: ROTATION_0, ROTATION_90, ROTATION_180,
    // or ROTATION_270.
    int newScreenRotation = getWindowManager().getDefaultDisplay()
            .getRotation();
}

A mudança da posição de um dispositivo dobrável muda a configuração, mesmo que o app seja executado no modo de tela cheia. Como resultado, se o usuário dobrar ou desdobrar o dispositivo enquanto o jogo estiver em execução, talvez o app precise processar mudanças no tamanho da tela ou na densidade de pixels.

Qualidades de tela específicas do jogo

As seções a seguir descrevem como ajustar a maneira como o jogo reage às mudanças no tamanho ou na orientação da tela, dependendo das qualidades dele:

Modo de tela cheia

Em algumas plataformas, como o ChromeOS, os apps e jogos Android podem ser janelas e redimensionáveis por padrão. Se seu jogo precisa ser sempre executado no modo tela cheia, defina o atributo android:resizeableActivity como false em um dos elementos <activity>, conforme mostrado no snippet de código a seguir:

<activity ...
    android:resizeableActivity="false">
</activity>

Também é possível definir o atributo android:resizeableActivity como false para evitar que mudanças de configuração baseadas em tamanho ocorram. A menos que seu jogo seja sempre executado no modo de tela cheia, adicione esse atributo apenas como uma correção temporária para fins de teste.

Orientação da tela

Se o seu jogo precisa que os sensores do dispositivo tenham uma orientação específica, defina um valor para android:screenOrientation na atividade do jogo, conforme mostrado no snippet de código a seguir. Essa configuração ajuda a evitar que um cenário do jogo vire de cabeça para baixo inesperadamente.

<activity ...
    android:screenOrientation="landscape">
</activity>

Qualidades da tela específicas do dispositivo

As seções a seguir descrevem como lidar com mudanças de configuração baseadas na tela, dadas as qualidades específicas de alguns dispositivos.

Proporção

Alguns dispositivos são compatíveis com proporções diferentes. Por exemplo, dispositivos dobráveis são projetados para ter compatibilidade com uma proporção de 21:9 no estado dobrado. Para lidar com essa possível variação na proporção, siga pelo menos uma das ações a seguir:

  • Ter o Android 8.0 (API de nível 26) ou versões mais recentes como destino.
  • Fazer com que o cenário e a interface do jogo possam ser redimensionados. Defina android:resizeableActivity como true em dispositivos com o Android 7.0 (API de nível 24) ou versões mais recentes.
  • Declarar uma proporção máxima compatível. Em um atributo <meta-data> associado ao jogo, defina android.max_aspect como 2.4, conforme mostrado no snippet de código a seguir. No entanto, lembre-se de que as proporções maiores que aquela especificada fazem com que o jogo apareça com efeito letterbox na tela.

    <application>
    <meta-data android:name="android.max_aspect"
               android:value="2.4" />
    </application>
    

Várias atividades visíveis simultaneamente

Muitos dispositivos modernos são compatíveis com diversos layouts de tela, incluindo tela dividida, picture-in-picture e telas grandes. Ao usar um desses layouts, o sistema pode tornar várias atividades visíveis ao mesmo tempo.

Em dispositivos com o Android 9 (API de nível 28) ou versões mais recentes, é possível retomar todas as principais atividades visíveis ao mesmo tempo. Entretanto, para que esse comportamento funcione, o jogo e o OEM do dispositivo precisam ativar a funcionalidade. Para adicionar a compatibilidade no jogo, defina android.allow_multiple_resumed_activities como true no manifesto dele, conforme mostrado no snippet a seguir:

<application>
    <meta-data android:name="android.allow_multiple_resumed_activities"
               android:value="true" />
</application>

Então, você poderá testar seu jogo em diferentes dispositivos para ver qual deles oferece a compatibilidade de OEM necessária para que a retomada de várias atividades funcione corretamente.

Para mais informações sobre como configurar seu jogo de modo que ele apareça como parte de uma exibição de várias janelas, consulte o guia sobre como adicionar compatibilidade para várias janelas.

Lidar com diferentes tipos de modelos de interação

Seu jogo lida manualmente com a presença e a disponibilidade do teclado sempre que você inclui os valores keyboard e keyboardHidden, respectivamente, em um atributo android:configChanges. Use esses novos valores para atualizar o método de entrada principal do seu jogo.

Ao configurar seu jogo para oferecer compatibilidade com diversos tipos de entrada do usuário, lembre-se de:

  • detectar métodos de entrada em vez de dispositivos individuais. Essa mentalidade facilita a melhoria da experiência do jogador sem dar muito foco ao dispositivo específico que o jogador possa ter;
  • incluir o atributo keyboardHidden na lista de mudanças de configuração gerenciadas manualmente. Assim, seu jogo pode detectar quando um teclado é fisicamente conectado ao dispositivo, mas não pode ser usado;
  • determinar os métodos de entrada disponíveis no momento. Para fazer isso, chame getInputDeviceIds() na inicialização do jogo e depois de cada mudança na configuração.

    Muitas vezes, é possível determinar como o jogador pretende interagir com seu jogo com base no dispositivo de entrada preferido:

    • Os jogadores geralmente usam um teclado ou um controle para executar sequências rápidas de botões.
    • Os jogadores geralmente usam uma tela touchscreen ou um touchpad para realizar gestos mais complexos.
    • Os jogadores geralmente usam um mouse para executar entradas com maior precisão.

As seções a seguir trazem práticas recomendadas para tipos específicos de dispositivos de entrada.

Teclado

Ao criar um layout de teclado para seu jogo, pense em como o jogador navega por um determinado cenário e como ele interage com as configurações do jogo.

As teclas WASD ou de setas geralmente são as mais indicadas para controlar o movimento dos personagens. Também é recomendável atribuir uma tecla específica para cada ação ou habilidade importante que um personagem controlável pode executar dentro do jogo. Para maximizar a experiência do jogador, adicione compatibilidade para combinações de teclas personalizadas no jogo.

Os jogadores também precisam conseguir abrir os menus do jogo e navegar por eles usando o teclado. A tecla Esc é comumente usada para pausar uma cena e mostrar o menu do jogo.

Para saber mais sobre como oferecer compatibilidade com entrada de teclado no jogo, consulte o guia sobre como adicionar compatibilidade para navegação com teclado e também o guia sobre como gerenciar ações de teclado.

Controle de jogos

Para mais informações sobre como lidar com entradas de controles no jogo, consulte o guia sobre como oferecer compatibilidade para controles de jogo.

Mouse ou touchpad

Se seu jogo for compatível com a entrada do jogador por um mouse ou touchpad, lembre-se de que os jogadores interagem com o dispositivo de outras maneiras além do jogo. É importante saber que, ao solicitar a captura do ponteiro, toda a entrada do mouse é direcionada ao jogo. Portanto, depois que o jogo tiver as informações necessárias, libere a captura do ponteiro para que os jogadores recuperem o controle padrão do mouse no dispositivo.

Em dispositivos com o Android 8.0 (API de nível 26) e versões mais recentes, você pode usar a API Mouse Capture para ajudar no processo de captura de ponteiro. Em jogos que reagem à entrada de alta precisão, é possível ter acesso às coordenadas atuais do ponteiro chamando os métodos getX() e getY().

Para mais informações sobre como adicionar compatibilidade para entradas de mouse e touchpad no jogo, consulte o guia sobre como rastrear movimentos de toque e ponteiro e também o guia sobre como gerenciar gestos multitoque.

Testar o jogo

Antes de iniciar o jogo, teste como ele responde a mudanças de configuração, executando as etapas descritas nas seções a seguir.

Atualizar o plano de teste

Ao validar a funcionalidade do jogo, inclua os seguintes casos de teste:

  • Minimize e maximize a janela que contém o jogo. Essa ação não é válida se o jogo estiver sempre no modo de tela cheia.
  • Mude o tamanho da tela.
  • Mude a orientação da tela. Essa ação não é válida se o jogo tiver uma orientação fixa.
  • Conecte e desconecte dispositivos de entrada, como teclados e mouses.
  • Faça a retomada de várias atividades, se seu jogo for compatível com esse recurso.

Além disso, recomendamos que você atualize o sistema de controle de qualidade do jogo para otimizá-lo para uma variedade ainda maior de experiências do jogador.

Para ver práticas recomendadas relacionadas a testes de jogos, consulte o guia Conceitos básicos de testes.

Usar ferramentas de teste e depuração

Você pode realizar testes usando diversas ferramentas compatíveis com a plataforma: