Переход от телефонов к различным форм-факторам с большими экранами вносит свои коррективы в управление окнами в играх. На GoogleBook , ChromeOS и Google Play Games на ПК ваша игра может работать в оконном режиме поверх основного интерфейса рабочего стола. На новых планшетах и складных устройствах Android под управлением Android 12L (уровень API 32) или выше с шириной экрана > 600dp ваша игра может работать параллельно с другими приложениями в режиме разделенного экрана , изменять размер окон и даже перемещаться между внутренним и внешним дисплеями на складных устройствах, что приводит к изменению конфигурации размера окна и, на некоторых устройствах, его ориентации.
Возможность изменения размера в играх Unity
Базовая конфигурация большого экрана
Укажите, поддерживает ли ваша игра изменение размера экрана:
<android:resizeableActivity="true" or "false" />
Если поддержка изменения размера недоступна, убедитесь, что в манифесте игры явно указаны минимальное и максимальное поддерживаемые соотношения сторон:
<!-- 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 на ПК
В играх Google Play на ПК платформа обеспечивает изменение размера окна с соблюдением заданного соотношения сторон. Размер окна автоматически фиксируется на оптимальных значениях. Необходимо поддерживать соотношение сторон не менее 16:9, если основная ориентация экрана — альбомная, и 9:16, если игра запущена в портретном режиме. Для наилучшего результата рекомендуется явно поддерживать соотношения сторон 21:9, 16:10 и 3:2 для игр в альбомной ориентации. Изменение размера окна здесь не является обязательным, но всё же полезно для совместимости с другими форм-факторами.
Для получения дополнительной информации и рекомендаций см. раздел «Настройка графики для игр Google Play на ПК».
Большие экраны
Чтобы максимально увеличить видимую область для вашей игры в полноэкранном режиме на планшетах с большими экранами, складных устройствах, GoogleBook, ChromeOS и подключенных дисплеях, поддерживайте полноэкранный иммерсивный режим и скрывайте системные панели, устанавливая флаги в decorView , видимости системного интерфейса или через API WindowInsetsCompat . Вам также потребуется корректно обрабатывать события поворота и изменения размера или предотвращать их.
Обратите внимание, что на устройствах Android с большими экранами ваша игра может работать в конфигурациях, которые вам, возможно, не подходят. Если ваша игра не поддерживает все размеры и ориентации окна, платформа будет отображать её в режиме совместимости с черными полосами сверху и снизу и, при необходимости, запросит подтверждение у игрока перед переключением на неподдерживаемую конфигурацию.

На некоторых устройствах при переходе игрока на неподдерживаемую конфигурацию может появиться предложение перезагрузить игру и заново создать окно, чтобы оно наилучшим образом соответствовало новой конфигурации, что нарушает игровой процесс. Протестируйте игру в различных многооконных режимах (размер окна 2/3, 1/2, 1/3) и убедитесь, что никакие элементы игрового процесса или пользовательского интерфейса не обрезаются и не становятся недоступными. Кроме того, проверьте, как ваша игра реагирует на непрерывность отображения на складных устройствах при переходе между внутренним и внешним экранами. Если вы обнаружите проблемы, явно обработайте эти события конфигурации и добавьте расширенную поддержку изменения размера экрана на больших устройствах.
Расширенные возможности изменения размера большого экрана
Чтобы выйти из режима совместимости и избежать повторного выполнения действий, выполните следующие действия:
Укажите, что размер вашего основного элемента управления можно изменять:
<android:resizeableActivity="true" />Чтобы получать все события настройки для больших экранов, укажите явную поддержку параметров "orientation", "screenSize", "smallestScreenSize", "screenLayout" и "density" в атрибуте
android:configChangesэлемента<activity>вашего игрового манифеста.<android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard | keyboardHidden | density" />Переопределите
onConfigurationChanged()и обработайте событие конфигурации, включая текущую ориентацию, размер окна, ширину и высоту:Котлин
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(); }
Вы также можете запросить WindowManager информацию о текущем повороте устройства. Используя эти метаданные, проверьте новые размеры окна и отрисуйте его до полного размера окна. Это может не сработать во всех случаях из-за различий в соотношении сторон, поэтому в качестве альтернативы, привяжите пользовательский интерфейс игры к новому размеру окна и добавьте черные полосы по краям для основного игрового контента. Если существуют технические или дизайнерские ограничения, препятствующие любому из этих подходов, выполните собственную внутриигровую обработку черных полос, чтобы сохранить соотношение сторон, и масштабируйте до наилучших возможных размеров, указав resizeableActivity = false и избегая режима конфигурации.
Независимо от выбранного подхода, протестируйте игру в различных конфигурациях (сворачивание и разворачивание, изменение поворота экрана, режим разделенного экрана) и убедитесь в отсутствии обрезанных или наложенных элементов интерфейса, проблем с доступностью сенсорных элементов или проблем с соотношением сторон, из-за которых игра может растягиваться, сжиматься или искажаться.
Кроме того, большие экраны обычно означают большие пиксели, поскольку то же количество пикселей приходится на гораздо большую площадь. Это может привести к пикселизации при использовании уменьшенных буферов рендеринга или ресурсов с более низким разрешением. Используйте ресурсы самого высокого качества на устройствах с большими экранами и проведите профилирование производительности игры , чтобы убедиться в отсутствии проблем. Если ваша игра поддерживает несколько уровней качества, убедитесь, что она учитывает особенности устройств с большими экранами.
Многооконный режим
Многооконный режим позволяет нескольким приложениям одновременно использовать один и тот же экран. Многооконный режим не изменяет жизненный цикл активности ; однако состояние возобновления работы приложений в нескольких окнах различается в разных версиях Android (см. раздел «Жизненный цикл активности в многооконном режиме» в разделе «Поддержка многооконного режима »).
Когда игрок переводит приложение или игру в многооконный режим, система уведомляет об изменении конфигурации, как указано в разделе «Расширенные настройки изменения размера для больших экранов» . Изменение конфигурации также происходит, когда игрок изменяет размер игры или возвращает игру в полноэкранный режим.
Нет гарантии, что приложение вернет фокус при переходе в многооконный режим. Поэтому, если вы используете какие-либо события состояния приложения для приостановки игры, не полагайтесь на событие получения фокуса ( onWindowFocusChanged() со значением фокуса true) для возобновления игры. Вместо этого используйте другие обработчики событий или изменения состояния, такие как onConfigurationChanged() или onResume() . Обратите внимание, что вы всегда можете использовать метод isInMultiWindowMode() для определения того, работает ли текущая активность в многооконном режиме.
При использовании оконного режима на Google Books, ChromeOS, а также планшетах и складных устройствах с большими экранами, начальные размеры окна становятся важным фактором. Игра не обязательно должна отображаться в полноэкранном режиме, и в этом случае вам потребуется указать размер окна. Для этого рекомендуется два способа.
Первый вариант работает с использованием определенных атрибутов тега <layout> в вашем Android-манифесте. Атрибуты defaultHeight и defaultWidth управляют начальными размерами. Также обратите внимание на атрибуты minHeight и minWidth , чтобы предотвратить изменение размеров игрового окна игроками до размеров, которые вы не поддерживаете. Наконец, есть атрибут gravity , который определяет, где на экране отображается окно при запуске. Вот пример тега layout с использованием этих атрибутов:
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end"
android:minHeight="450dp"
android:minWidth="300dp" />
Второй способ установки размера окна работает с использованием динамических границ запуска. С помощью функции setLaunchBounds(Rect) можно определить начальные размеры окна. Если указан пустой прямоугольник, активность запускается в развернутом на весь экран состоянии.
Кроме того, если вы используете игровые движки Unity или Unreal, убедитесь, что вы используете последнюю версию (Unity 2019.4.40 и Unreal 5.3 или новее), которая обеспечивает хорошую поддержку многооконного режима.
Складная опора для осанки
Используйте библиотеку компоновки Jetpack WindowManager для поддержки складывающихся поз, например, в виде стола, чтобы повысить погружение и вовлеченность игрока:

Котлин
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); }