Suporte à capacidade de redimensionamento de telas grandes

A expansão de smartphones para diferentes formatos de tela grande apresenta considerações sobre como seu jogo lida com o gerenciamento de janelas. No ChromeOS e no Google Play Games no PC, o jogo pode ser executado no modo de janela em uma interface principal da área de trabalho. Em novos tablets e dispositivos dobráveis Android com o Android 12L (nível 32 da API) ou versões mais recentes com largura de tela maior que 600 dp, o jogo pode ser executado lado a lado no modo de tela dividida com outros apps, ser redimensionado e até mesmo movido entre as telas interna e externa em dispositivos dobráveis, resultando em uma mudança de configuração para o tamanho da janela e, em alguns dispositivos, a orientação.

Redimensionamento com jogos do Unity

Configuração básica de tela grande

Declare se o jogo consegue lidar com o redimensionamento:

<android:resizeableActivity="true" or "false" />

Se não for possível fazer isso, confira se o manifesto do jogo define explicitamente as proporções mínima e máxima com suporte:

<!-- Render full screen between 3:2 and 21:9 aspect ratio -->
<!-- Let the platform letterbox otherwise -->
<activity android:minAspectRatio="1.5">
<activity android:maxAspectRatio="2.33">

Google Play Games no PC

No Google Play Games no PC, a plataforma processa o redimensionamento da janela respeitando a proporção especificada. O tamanho da janela é bloqueado automaticamente para as dimensões ideais. É necessário oferecer compatibilidade com uma proporção de pelo menos 16:9 se a orientação principal for paisagem e 9:16 se o jogo estiver no modo retrato. Para ter a melhor experiência, ofereça suporte explícito às proporções 21:9, 16:10 e 3:2 em jogos no modo paisagem. O redimensionamento da janela não é obrigatório aqui, mas ainda é bom para compatibilidade com outros formatos.

Para mais informações e práticas recomendadas, consulte Configurar gráficos para o Google Play Games no PC.

Telas grandes do ChromeOS e do Android

Para maximizar a área visível do jogo em tela cheia no ChromeOS e em dispositivos Android de tela grande, ofereça suporte ao modo imersivo de tela cheia e oculte as barras de sistema definindo flags no decorView, na visibilidade da interface do sistema ou usando a API WindowInsetsCompat. Você também precisa gerenciar corretamente os eventos de configuração de rotação e redimensionamento ou impedir que eles aconteçam nos dispositivos ChromeOS.

Em dispositivos Android de tela grande, o jogo pode ser executado em configurações que talvez você ainda não tenha. Se o jogo não oferecer suporte a todas as configurações de tamanho e orientação de janela, o jogo terá efeito letterbox da plataforma no modo de compatibilidade e, se necessário, avisará o jogador antes de mudar para uma configuração sem suporte.

Figura 1. Caixa de diálogo de compatibilidade da configuração.

Em alguns dispositivos, quando um jogador muda para uma configuração não compatível, ele pode receber uma opção para recarregar o jogo e recriar a atividade para se ajustar melhor ao novo layout da janela, o que interrompe a experiência de jogo. Teste seu jogo em várias configurações do modo de várias janelas (2/3, 1/2 e 1/3) e verifique se nenhuma jogabilidade ou elemento da interface está cortado ou inacessível. Além disso, teste como o jogo responde à continuidade do dispositivo dobrável ao alternar entre as telas interna e externa. Se você encontrar problemas, processe explicitamente esses eventos de configuração e adicione suporte avançado ao redimensionamento de tela grande.

Redimensionamento avançado para telas grandes

Figura 2. Diferentes interfaces no computador e dobráveis na posição de mesa.

Para sair do modo de compatibilidade e evitar a recriação de atividades, faça o seguinte:

  1. Declare sua atividade principal como redimensionável:

    <android:resizeableActivity="true" />
    
  2. Declare o suporte explícito a "orientation", "screenSize", "smallestScreenSize", "screenLayout" e "densidade" no atributo android:configChanges do elemento <activity> do manifesto do jogo para receber todos os eventos de configuração de tela grande:

    <android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard |
                            keyboardHidden | density" />
    
  3. Modifique onConfigurationChanged() e processe o evento de configuração, incluindo a orientação atual, o tamanho da janela, a largura e a altura:

    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()
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       val newScreenOrientation: Int = newConfig.orientation
    
       // 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);
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       int newScreenOrientation = newConfig.orientation;
    
       // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270
       int newScreenRotation = getWindowManager().getDefaultDisplay()
               .getRotation();
    }
    

Você também pode consultar o WindowManager para verificar a rotação atual do dispositivo. Com esses metadados, verifique as dimensões da nova janela e renderize-a no tamanho total da janela. Isso pode não funcionar em todos os casos devido a diferenças de proporção. Como alternativa, fixe a interface do jogo no novo tamanho da janela e use o efeito letterbox no conteúdo principal do jogo. Se houver limitações técnicas ou de design impedindo qualquer uma das abordagens, faça o efeito letterbox no mecanismo para preservar a proporção e dimensione para as melhores dimensões possíveis ao declarar resizeableActivity = false e evitar o modo de configuração.

Seja qual for a abordagem adotada, teste seu jogo em várias configurações (dobra e desdobramento, mudanças de rotação diferentes, modo de tela dividida) e verifique se não há cortes ou sobreposição de elementos da interface no jogo, problemas com a acessibilidade da área de toque ou problemas de proporção que fazem o jogo ficar esticado, comprimido ou distorcido de outra forma.

Além disso, telas maiores geralmente significam pixels maiores, porque você tem o mesmo número de pixels em uma área muito maior. Isso pode causar pixelização em buffers de renderização ou recursos de resolução mais baixa. Use os recursos da mais alta qualidade em dispositivos de tela grande e o perfil de desempenho do jogo para garantir que não haja problemas. Caso seu jogo ofereça suporte a vários níveis de qualidade, verifique se ele atende a dispositivos de tela grande.

Modo de várias janelas

O modo de várias janelas permite que vários apps compartilhem a mesma tela simultaneamente. O modo de várias janelas não muda o ciclo de vida da atividade. No entanto, o estado retomado dos apps em várias janelas varia em diferentes versões do Android. Consulte Ciclo de vida da atividade no modo de várias janelas em Suporte a várias janelas.

Quando o jogador coloca um app ou jogo no modo de várias janelas, o sistema notifica a atividade sobre uma mudança de configuração, conforme especificado na seção Capacidade de redimensionamento de tela grande avançada. Uma mudança de configuração também acontece quando o jogador redimensiona o jogo ou o coloca novamente no modo de tela cheia.

Não há garantia de que o aplicativo vai recuperar o foco quando for colocado no modo de várias janelas. Portanto, se você usar qualquer um dos eventos de estado do app para pausar o jogo, não dependa do evento de aquisição de foco (onWindowFocusChanged() com o valor de foco definido como verdadeiro) para retomar o jogo. Em vez disso, use outros manipuladores de eventos ou de mudança de estado, como onConfigurationChanged() ou onResume(). Você sempre pode usar o método isInMultiWindowMode() para detectar se a atividade atual está em execução no modo de várias janelas.

Com o modo de várias janelas no ChromeOS, as dimensões da janela inicial se tornam uma consideração importante. Um jogo não precisa estar em tela cheia, e é necessário declarar o tamanho da janela nesse caso. Há duas maneiras recomendadas de abordar isso.

A primeira opção funciona usando atributos específicos na tag <layout> no manifesto do Android. Os atributos defaultHeight e defaultWidth controlam as dimensões iniciais. Tenha cuidado com os atributos minHeight e minWidth para evitar que os jogadores redimensionem a janela do jogo para dimensões não compatíveis. Por fim, há o atributo gravity, que determina em que parte da tela a janela aparece quando iniciada. Veja um exemplo de tag de layout que usa esses atributos:

<layout android:defaultHeight="500dp"
        android:defaultWidth="600dp"
        android:gravity="top|end"
        android:minHeight="450dp"
        android:minWidth="300dp" />

A segunda opção para definir o tamanho da janela funciona usando limites de inicialização dinâmicos. Ao usar setLaunchBounds(Rect)⁠⁠, você pode definir as dimensões da janela inicial. Se um retângulo vazio for especificado, a atividade será iniciada em um estado maximizado.

Além disso, se você estiver usando os mecanismos de jogo do Unity ou Unreal, confira se está usando uma versão recente (Unity 2019.4.40 e Unreal 5.3 ou mais recente) que oferece um bom suporte ao modo de várias janelas.

Suporte à postura dobrável

Use a biblioteca de layout WindowManager do Jetpack para oferecer suporte a posições dobráveis, como uma mesa, para aumentar a imersão e o engajamento do jogador:

Figura 3. Jogo na posição de mesa com visualização principal na parte vertical da tela e controles na parte horizontal.

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}