Suporte a redimensionamento de telas grandes

Ao expandir de smartphones para diferentes formatos de tela grande, é preciso considerar como o jogo lida com o gerenciamento de janelas. No Googlebook, no ChromeOS e no Google Play Games no PC, seu jogo pode ser executado em um modo de janela sobre uma interface principal de computador. Em novos tablets e dispositivos dobráveis Android com Android 12L (nível 32 da API) ou mais recente e largura de tela > 600 dp, o jogo pode ser executado lado a lado no modo de tela dividida com outros aplicativos, ser redimensionado e até mesmo movido entre a tela interna e externa em dispositivos dobráveis, resultando em uma mudança de configuração no tamanho da janela e, em alguns dispositivos, na orientação.

Capacidade de 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 oferecer suporte ao redimensionamento, verifique se o manifesto do jogo define explicitamente as proporções mínima e máxima aceitas:

<!-- 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 lida com o redimensionamento da janela respeitando a proporção especificada. O tamanho da janela é bloqueado automaticamente nas dimensões ideais. É necessário oferecer suporte a pelo menos a proporção de 16:9 se a orientação principal for paisagem e 9:16 se o jogo for no modo retrato. Para ter a melhor experiência, ofereça suporte explícito às proporções 21:9, 16:10 e 3:2 para um jogo no modo paisagem. O redimensionamento da janela não é obrigatório aqui, mas ainda é bom ter 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

Para maximizar a área visível do jogo em tela cheia em tablets de tela grande, dobráveis, Googlebook, ChromeOS e telas conectadas, ofereça suporte ao modo imersivo de tela cheia e oculte as barras de sistema definindo flags em decorView, visibilidade da interface do sistema ou pela API WindowInsetsCompat. Também é recomendável processar com eficiência eventos de configuração de rotação e redimensionamento ou evitar que eles aconteçam.

Em dispositivos Android de tela grande, o jogo pode ser executado em configurações que você ainda não processa. Se o jogo não for compatível com todas as configurações de tamanho e orientação da janela, a plataforma vai adicionar barras pretas ao jogo no modo de compatibilidade e, se necessário, vai pedir permissão ao 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, uma opção para recarregar o jogo e recriar a atividade para se adequar melhor ao novo layout da janela pode aparecer, o que prejudica a experiência de jogo. Teste o jogo em várias configurações do modo de várias janelas (tamanho de janela de 2/3, 1/2 e 1/3) e verifique se nenhum elemento de jogabilidade ou da interface é cortado ou fica inacessível. Além disso, teste como o jogo responde à continuidade dobrável ao passar da tela interna para a externa em dispositivos dobráveis. Se você encontrar 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. Interfaces diferentes em computadores e dispositivos 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 suporte explícito para "orientation", "screenSize", "smallestScreenSize", "screenLayout" e "density" no atributo android:configChanges do elemento <activity> do manifesto do jogo para receber todos os eventos de configuração da tela grande:

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

    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 no novo tamanho da janela e coloque barras pretas no conteúdo principal da jogabilidade. Se houver limitações técnicas ou de design que impeçam qualquer uma das abordagens, faça o letterboxing no próprio 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.

Independente da abordagem, teste o jogo em várias configurações (dobrar e desdobrar, diferentes mudanças de rotação, modo de tela dividida) e verifique se não há elementos de corte ou sobreposição na interface do jogo, problemas com acessibilidade de toque ou problemas de proporção que causem esticamento, achatamento ou distorção do jogo.

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 em buffers de renderização reduzidos ou recursos de baixa resolução. Use seus recursos de mais alta qualidade em dispositivos de tela grande e crie um perfil de desempenho do jogo para garantir que não haja problemas. Se o jogo tiver suporte a vários níveis de qualidade, verifique se ele é compatível com 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 de acordo com as diferentes versões do Android. Consulte Ciclo de vida da atividade no modo de várias janelas em Suporte ao modo de 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 avançada de redimensionamento em telas grandes. Uma mudança de configuração também acontece quando o jogador redimensiona o jogo ou o coloca de volta 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 confie no evento de aquisição de foco (onWindowFocusChanged() com valor de foco como "true") 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 janela para computador no Googlebook, no ChromeOS e em tablets e dobráveis de tela grande, as dimensões iniciais da janela são importantes. Um jogo não precisa ser em tela cheia, e você vai querer declarar qual deve ser o tamanho da janela nesse caso. Há duas maneiras recomendadas de fazer isso.

A primeira opção usa atributos específicos na tag <layout> no manifesto do Android. Os atributos defaultHeight e defaultWidth controlam as dimensões iniciais. Também fique atento aos atributos minHeight e minWidth para evitar que os jogadores redimensionem a janela do jogo para dimensões que você não oferece suporte. 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 usando 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 usa 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, use uma versão recente (Unity 2019.4.40 e Unreal 5.3 ou mais recente) que ofereça um bom suporte para o modo de várias janelas.

Suporte para postura dobrável

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

Figura 3. Jogo em postura de mesa com a 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);
}