#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:
Итак, я нашел способ достичь цели. Я добавил пустой игровой объект в сцену и добавил к нему всю структуру уровней, плюс добавил две черные полосы (в виде простых кубов) выше и ниже уровня. При таком подходе я могу масштабировать этот родительский игровой объект так, чтобы вся структура уровней масштабировалась пропорционально. Все, что мне нужно сделать, это создать скрипт для масштабирования родительского игрового объекта в соответствии с шириной экрана.