Переход с телефонов на различные форм-факторы больших экранов требует рассмотрения того, как ваша игра обрабатывает управление окнами. В ChromeOS и Google Play Games for PC ваша игра может работать в оконном режиме через основной интерфейс рабочего стола. На новых планшетах и складных устройствах Android под управлением Android 12L (уровень API 32) или выше с шириной экрана > 600 точек ваша игра может работать параллельно в режиме разделенного экрана с другими приложениями, изменять ее размер и даже перемещаться между внутренней и внешней частью экрана. отображение на складных устройствах , что приводит к изменению конфигурации размера окна и, на некоторых устройствах, ориентации.
Изменение размера в играх 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 Games для ПК платформа обрабатывает изменение размера окна с соблюдением указанного соотношения сторон. Размер окна автоматически фиксируется до оптимального размера. Вам необходимо поддерживать соотношение сторон не менее 16:9, если ваша основная ориентация — альбомная, и соотношение сторон 9:16, если ваша игра — портретный режим. Для достижения наилучших результатов явно поддерживайте соотношения сторон 21:9, 16:10 и 3:2 для ландшафтной игры. Изменение размера окна здесь не требуется, но его все равно полезно иметь для совместимости с другими форм-факторами.
Дополнительную информацию и рекомендации см. в разделе Настройка графики для Google Play Games на ПК.
Большие экраны ChromeOS и Android
Чтобы максимизировать область просмотра вашей игры в полноэкранном режиме на устройствах ChromeOS и Android с большим экраном, поддержите полноэкранный режим погружения и скройте системные панели, установив флаги в decorView
, видимости системного пользовательского интерфейса или с помощью API WindowInsetsCompat
. Вам также понадобится корректно обрабатывать события конфигурации вращения и изменения размера или предотвращать их возникновение на устройствах ChromeOS.
Обратите внимание, что на устройствах 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 }
Ява
@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()
, чтобы определить, выполняется ли текущее действие в многооконном режиме.
В многооконном режиме в ChromeOS первоначальные размеры окна становятся важным фактором. Игра не обязательно должна быть полноэкранной, и вам нужно указать, каким должен быть размер окна в этом случае. Есть два рекомендуемых способа сделать это.
Первый вариант работает с использованием определенных атрибутов тега <layout>
в манифесте Android. Атрибуты defaultHeight
и defaultWidth
управляют исходными размерами. Также помните об атрибутах minHeight
и minWidth
, чтобы ваши игроки не могли изменить размер окна игры до размеров, которые вы не поддерживаете. Наконец, есть атрибут gravity
, который определяет, где на экране появится окно при запуске. Вот пример тега макета, использующего эти атрибуты:
<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 }
Ява
boolean isTableTopPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL); }