Unity, Android: как правильно настроить аспект / масштаб игры

#android #unity3d #game-development #aspect-ratio

#Android #unity3d #разработка игр #соотношение сторон

Вопрос:

Я разработал игру для Android с использованием Unity. И, в конце концов, я столкнулся со следующей проблемой:

Каждый уровень моей игры имеет строгие границы (например, прямоугольник) по бокам экрана. Я разрабатывал игру с аспектом 9: 16 (рис. 1). Когда я собрал ее и запустил на своем телефоне (сейчас 9:19.5), я обнаружил, что крайние левая и крайняя правая части уровней находятся вне экрана (рис. 2).

Как я могу это исправить, чтобы игра соответствовала любому соотношению сторон?

Я пробовал разные решения, но ничто меня не устраивало. Я в таком отчаянии, что уже согласен просто уменьшить масштаб с 9: 16, чтобы он соответствовал ШИРИНЕ экрана, плюс добавить черные полосы вверху и внизу (рис. 3). Высота не так важна в моей игре, но ширина имеет решающее значение. Или есть какое-то лучшее решение?

введите описание изображения здесь
введите описание изображения здесь
введите описание изображения здесь

ОБНОВЛЕНИЕ 2: дизайн моего проекта (своего рода серая коробка): введите описание изображения здесьвот как это выглядит ХОРОШО с разными аспектами в режиме воспроизведения: введите описание изображения здесьИ вот как это выглядит ПЛОХО с разными аспектами в режиме воспроизведения: введите описание изображения здесь

Комментарии:

1. Это зависит от того, как вы настраиваете свой холст и какие ограничения вы добавили к своим основным представлениям. Можете ли вы поделиться этой деталью?

2. Я имею в виду Anchor Presets . Вот где происходит волшебство. Используя их, вы объявляете, как ваш View должен быть размещен внутри вашего Canvas . Например: верхний правый, нижний левый, растягивать по горизонтали или вертикали и т. Д. Если вы не против, было бы лучше поделиться кратким обзором вашего дизайна, чтобы помочь его улучшить.

3. О, так вы разрабатываете макет своего уровня в 3d с помощью кубов и пытаетесь разместить его на своем экране с правильным соотношением сторон? Вам действительно нужно создавать это с помощью кубов? Мне кажется, что это 2d-игра.

4. Если это будет 2D-игра, нет необходимости использовать 3D-объекты для проектирования уровня. Будет намного проще добиться одинакового дизайна, используя спрайты и элементы пользовательского интерфейса, которые будут правильно масштабироваться на каждом экране.

5. Да, белые границы могут быть простым изображением внутри холста, и уровень не должен быть частью самого холста. А другая часть (уровни, персонажи и т. Д.) Может использоваться как 2D-спрайт.

Ответ №1:

Камера Unity предполагает, что игры будут воспроизводиться в альбомной ориентации. Поэтому размер игрового мира, отображаемого по вертикали, всегда будет одинаковым на любом экране, независимо от соотношения сторон. Если вы ограничиваете воспроизведение своей игры только на мобильных платформах и только в портретном режиме, тогда вы захотите заставить Unity сделать обратное. Вы хотите, чтобы камера всегда отображала одинаковую ширину и отображала переменную высоту в зависимости от соотношения сторон. (Полезный совет: разрабатывайте с наименьшим соотношением сторон, которое вы ожидаете, например, 4: 5, а не 9: 16. Настройте все соотношения сторон или разрешения, которые вы хотите протестировать, в выпадающем меню в верхней части игрового окна. Переключайтесь между ними для тестирования и разрешайте вашему пользовательскому интерфейсу и игровому представлению расширяться для более высоких соотношений сторон. В противном случае ваш пользовательский интерфейс, вероятно, будет работать вместе с меньшим соотношением сторон. Смотрите мой пост на CanvasScaler здесь.)

В любом случае, чтобы изменить поведение камеры по умолчанию, вам нужно добавить скрипт в вашу камеру. Что-то вроде этого должно сделать:

Редактировать: я изменил компонент для обработки как перспективных, так и ортогональных камер. Судя по вашей игре, похоже, что вы используете или, возможно, должны использовать ортографическую камеру, которую мой предыдущий ответ не обрабатывал. Кроме того, у компонента больше нет свойств. Просто подключитесь к камере, затем используйте свойства камеры как обычно. Кроме того, теперь он обрабатывает видовые экраны, поэтому работает с камерами, которые не занимают весь экран.

Есть одна известная проблема. При использовании ортогональной камеры, если вы пытаетесь настроить видовой экран так, чтобы он был больше, чем весь размер экрана, что-то внутреннее в Unity сбрасывает матрицу проекции и переопределяет поведение этого компонента. Чтобы исправить, просто переключите компонент или перезагрузите сцену или редактор, все, что заставляет компонент снова выполнять свои функции.

 using UnityEngine;

[ExecuteAlways]
[RequireComponent(typeof(Camera))]
public class HorizontallyAlignedCamera : MonoBehaviour
{
    private Camera _camera;
    private float _aspectRatio;
    private float _fieldOfView;
    private bool _isOrthographic;
    private float _orthographicSize;

    private void Awake()
    {
        _camera = GetComponent<Camera>();
    }

    private void OnEnable()
    {
        CachePropertiesAndRecalculate();
    }

    private void LateUpdate()
    {
        if(CameraPropertyHasChanged())
            CachePropertiesAndRecalculate();
    }

    private void OnDisable()
    {
        _camera.ResetProjectionMatrix();
    }

    private bool CameraPropertyHasChanged()
    {
        bool hasChanged = (_aspectRatio != _camera.aspect
            || _fieldOfView != _camera.fieldOfView
            || _isOrthographic != _camera.orthographic
            || _orthographicSize != _camera.orthographicSize);

        return hasChanged;
    }

    private void CacheCameraProperties()
    {
        _aspectRatio = _camera.aspect;
        _fieldOfView = _camera.fieldOfView;
        _isOrthographic = _camera.orthographic;
        _orthographicSize = _camera.orthographicSize;
    }

    private void CachePropertiesAndRecalculate()
    {
        CacheCameraProperties();

        if(_camera.orthographic)
            RecalculateOrthographicMatrix();
        else
            RecalculatePerspectiveMatrix();
    }

    private void RecalculatePerspectiveMatrix()
    {
        float near = _camera.nearClipPlane;
        float nearx2 = near * 2.0f;
        float far = _camera.farClipPlane;
        float halfFovRad = _camera.fieldOfView * 0.5f * Mathf.Deg2Rad;

        // This is what aligns the camera horizontally.
        float width = nearx2 * Mathf.Tan(halfFovRad);
        float height = width / _camera.aspect;

        // This is the default behavior.
        //float height = nearx2 * Mathf.Tan(halfFovRad);
        //float width = height * _camera.aspect;

        float a = nearx2 / width;
        float b = nearx2 / height;
        float c = -(far   near) / (far - near);
        float d = -(nearx2 * far) / (far - near);

        Matrix4x4 newProjectionMatrix = new Matrix4x4(
            new Vector4(a, 0.0f, 0.0f, 0.0f),
            new Vector4(0.0f, b, 0.0f, 0.0f),
            new Vector4(0.0f, 0.0f, c, -1.0f),
            new Vector4(0.0f, 0.0f, d, 0.0f));

        _camera.projectionMatrix = newProjectionMatrix;
    }

    private void RecalculateOrthographicMatrix()
    {
        // This is what aligns the camera horizontally.
        float width = 2.0f * _camera.orthographicSize;
        float height = width / _camera.aspect;

        // This is the default behavior.
        //float height = 2.0f * _camera.orthographicSize;
        //float width = height * _camera.aspect;

        float near = _camera.nearClipPlane;
        float far = _camera.farClipPlane;
        float a = 2.0f / width;
        float b = 2.0f / height;
        float c = -2.0f / (far - near);
        float d = -(far   near) / (far - near);

        Matrix4x4 newProjectionMatrix = new Matrix4x4(
            new Vector4(a, 0.0f, 0.0f, 0.0f),
            new Vector4(0.0f, b, 0.0f, 0.0f),
            new Vector4(0.0f, 0.0f, c, 0.0f),
            new Vector4(0.0f, 0.0f, d, 1.0f));

        _camera.projectionMatrix = newProjectionMatrix;
    }
}
 

Комментарии:

1. Вау, это что-то! Я определенно собираюсь попробовать, хотя это, как вы сказали, «большой и страшный» для меня и займет некоторое время, чтобы понять. Большое вам спасибо!

Ответ №2:

Итак, я нашел способ достичь цели. Я добавил пустой игровой объект в сцену и добавил к нему всю структуру уровней, плюс добавил две черные полосы (в виде простых кубов) выше и ниже уровня. При таком подходе я могу масштабировать этот родительский игровой объект так, чтобы вся структура уровней масштабировалась пропорционально. Все, что мне нужно сделать, это создать скрипт для масштабирования родительского игрового объекта в соответствии с шириной экрана.