Desenvolver jogos para todas as telas

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

As diferenças específicas na experiência do jogador incluem:

  • Formatos de dispositivo: embora os smartphones ofereçam a experiência de dispositivo tradicional do Android, é possível interagir com jogos em outros formatos. Dispositivos Chrome OS podem executar um contêiner Android que mostra 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 alterar 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 experimentem 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 teste 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 alterações de configuração

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

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

Para saber como adicionar essa lógica de alteração de configuração ao jogo, consulte a seção sobre como criar gerenciadores personalizados de alterações 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 maiores de tamanho para seu jogo. Cada APK básico pode ter até 150 MB, e o próprio 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 alterações de configuração

Para declarar os tipos de alterações de configuração que o próprio jogo gerencia, adicione o atributo android:configChanges a cada elemento <activity> no seu manifesto que representa uma tela ou interface complexa.

O snippet de código a seguir demonstra como declarar que seu jogo processa alterações 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 alterações de configuração declaradas ocorrem, o sistema invoca um método diferente, onConfigurationChanged(). Dentro desse método, adicione lógica para atualizar a IU do jogo:

Lidar com alterações de configuração da tela

Seu jogo processa manualmente as alterações 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 saber como projetar o layout do jogo para 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 janela 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();
    }
    

Qualidades de tela específicas do jogo

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

Modo de tela cheia

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

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

Você também pode definir o atributo android:resizeableActivity como false para evitar que alterações 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 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 alterações 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:

  • Segmentar o Android 8.0 (API nível 26) ou posterior.
  • 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 nível 24) e posterior.
  • Declarar uma proporção máxima compatível. Em um atributo <meta-data> associado ao jogo, defina android.max_aspect como 2.4, como 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 nível 28) ou posterior, é 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. Adicione compatibilidade no jogo definindo android.allow_multiple_resumed_activities como true no manifesto do jogo, como mostrado no snippet de código 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 a retomada de várias atividades funcionar corretamente.

Para mais informações sobre como configurar seu jogo para 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 de 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 sua lista de alterações de configuração gerenciadas manualmente. Assim, seu jogo pode acompanhar 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 alteração 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 de jogo para executar sequências rápidas de botões.
    • Os jogadores geralmente usam uma 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 são geralmente as mais indicadas para controlar o movimento dos personagens. Também é melhor 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 devem conseguir abrir os menus do jogo e navegar por eles usando o teclado. A tecla Esc é um mapeamento comum 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

Quando conectados a dispositivos Android, os controles de jogos servem como teclados com um layout especializado. Use as APIs InputDevice, KeyEvent e MotionEvent para transmitir eventos e nomes do dispositivo, ver mapeamentos de teclas e controlar o retorno tátil.

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 é compatível com a entrada do jogador a partir de 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 nível 26) e posterior, você pode usar a API Mouse Capture para ajudar no processo de captura de ponteiro. Nos 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 guia sobre como rastrear movimentos de toque e ponteiro e também o guia sobre como gerenciar gestos de vários toques.

Testar o jogo

Antes de iniciar o jogo, teste como ele responde a alterações 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 está sempre no modo de tela cheia.
  • Altere o tamanho da tela.
  • Altere a orientação da tela. Essa ação não é válida se o jogo tem 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, 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: