Suporte à capacidade de redimensionamento de telas grandes

A expansão de smartphones para diferentes formatos de tela grande introduz considerações sobre como seu jogo lida com o gerenciamento de janelas. No ChromeOS e no Google Play Games no PC, seu jogo pode ser executado no modo de janela em uma interface principal para computadores. 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 ser movido entre a tela interna e externa em dispositivos dobráveis, resultando em uma mudança de configuração do tamanho da janela e, em alguns dispositivos, da orientação.

Configuração básica de tela grande

Declare se o jogo é capaz de lidar com a capacidade de redimensionamento:

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

Se não for possível usar o redimensionamento, 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

Para o Google Play Games no PC, a plataforma lida com a capacidade de redimensionamento da janela, respeitando a proporção especificada. O tamanho da janela é bloqueado automaticamente para as dimensões ideais. Você precisa oferecer suporte a 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 às proporções 21:9, 16:10 e 3:2 em um jogo no modo paisagem. O redimensionamento de janela não é obrigatório aqui, mas ainda é adequado para outras compatibilidades de formato.

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 em tela cheia e oculte as barras de sistema definindo flags no decorView, a visibilidade da interface do sistema ou a API WindowInsetsCompat. Gerencie corretamente os eventos de configuração de rotação e redimensionamento ou evite que eles aconteçam em dispositivos ChromeOS.

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

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

Em alguns dispositivos, quando um jogador muda para uma configuração sem suporte, ele pode receber uma solicitação para recarregar o jogo e recriar a atividade para se ajustar melhor ao layout da nova 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, 1/3 de tamanho da janela) e verifique se nenhum elemento da interface ou do jogo está cortado ou inacessível. Além disso, teste como o jogo responde à continuidade dobrável ao se mover entre a tela interna e externa em dispositivos dobráveis. Se houver problemas, processe explicitamente esses eventos de configuração e adicione suporte avançado para redimensionamento de telas grandes.

Redimensionamento avançado de telas grandes

Figura 2. IUs diferentes no computador e dobrável na postura 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 suporte explícito para "Orientation", "screenSize", "smallestScreenSize", "screenLayout" e "depth" 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. Usando esses metadados, verifique as novas dimensões da janela e renderize para o tamanho total da janela. Isso pode não funcionar em todos os casos devido a diferenças na proporção. Como alternativa, fixe a interface do jogo ao novo tamanho da janela e use o efeito letterbox no conteúdo principal da jogabilidade. Se houver limitações técnicas ou de design impedindo qualquer uma das abordagens, faça seu próprio efeito letterbox no mecanismo para preservar a proporção e dimensione para as melhores dimensões possíveis, declarando resizeableActivity = false e evitando o modo de configuração.

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

Além disso, telas maiores geralmente significam pixels maiores, porque você tem o mesmo número de pixels para uma área muito maior. Isso pode causar pixelização para buffers de renderização ou recursos de resolução mais baixa. Use recursos da mais alta qualidade em dispositivos de tela grande e perfil de desempenho do seu jogo para garantir que não haja problemas. Se o jogo oferece 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 é diferente 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 Redimensionamento avançado de tela grande. 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 app 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 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á sendo executada no modo de várias janelas.

Com o modo de várias janelas no ChromeOS, as dimensões iniciais da janela se tornam uma consideração importante. Um jogo não precisa estar em tela cheia, e é necessário declarar o tamanho da janela para esse caso. Há duas maneiras recomendadas de fazer 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. Considere também 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 área da tela a janela aparece quando iniciada. Confira um exemplo de tag de layout que usa estes 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 Unity ou Unreal, verifique se está usando uma versão recente (Unity 2019.4.40 e Unreal 5.3 ou mais recente) com suporte adequado para o modo de várias janelas.

Suporte à posição dobrável

Use a biblioteca de layout WindowManager do Jetpack para oferecer suporte a posições dobráveis, como sobre uma mesa, a fim de 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);
}