Ограничение орфографической камеры в изометрическом мире

Я использую Unity3D, и я создаю изометрический мир, вращая плоскость по оси Y на 4.205 единиц.

Я использую орфографическую камеру. Я хочу ограничить камеру, чтобы в окне просмотра никогда не отображалась область за пределами мира (в зеленом цвете).

 Изометрическая карта

  1. Как рассчитать угловые точки моей повернутой плоскости в координатах экрана, обозначенных зеленым?
  2. Как остановить прокрутку камеры, чтобы она не отображала область вне мира, представленную серым цветом?
4 голоса | спросил Simian 1 Mayam17 2017, 08:31:43

3 ответа


2

Ограничить только центр просмотра камеры до плоскости земли

Вы можете найти рабочий проект Unity, содержащий этот код здесь .

using UnityEngine;
using System.Collections;

public class CameraAnchorController : MonoBehaviour
{
    public float xAcceleration = 0;
    public float yAcceleration = 0;
    public float accelerationRate; //set to 1 in inspector
    public Vector2 acceleration;
    public Vector2 velocity;

    float friction = 0.98f;
    public float xMin; //set to -5 in inspector
    public float xMax; //set to 5 in inspector
    public float yMin; //set to -5 in inspector
    public float yMax; //set to 5 in inspector
    //these -5/+d5 are because a Unity plane's origin is in the centre.

    void Update()
    {
        xAcceleration = 0;
        yAcceleration = 0;

        //acceleration
        xAcceleration += Input.GetKey(KeyCode.LeftArrow)    ? -1 : 0;
        xAcceleration += Input.GetKey(KeyCode.RightArrow)   ? +1 : 0;
        yAcceleration += Input.GetKey(KeyCode.UpArrow)      ? +1 : 0;
        yAcceleration += Input.GetKey(KeyCode.DownArrow)    ? -1 : 0;
        acceleration = new Vector2(xAcceleration, yAcceleration); //use Vector2 here or we incur extra, costly sqrt calls in magnitude.
        acceleration = Vector2.ClampMagnitude(acceleration, 1.0f); //normalize it.
        acceleration *= accelerationRate;

        //speed
        velocity += acceleration;
        velocity *= friction;

        //position
        float xPos = transform.localPosition.x + velocity.x * Time.deltaTime;
        float yPos = transform.localPosition.z + velocity.y * Time.deltaTime; //note the interchanged y/z here due to Unity's coord system.

        //We'd usually just use 2 Mathf.Clamp(val, min, max) calls here, but we need to know
        //the outcome of the clamping, so as to also restrict velocity if we hit a map side.
        if (xPos < xMin)
        {
            xPos = xMin; 
            velocity = new Vector2(0, velocity.y); 
        }
        if (xPos > xMax)
        {
            xPos = xMax; 
            velocity = new Vector2(0, velocity.y);
        }
        if (yPos < yMin)
        {
            yPos = yMin; 
            velocity = new Vector2(velocity.x, 0);
        }
        if (yPos > yMax)
        {
            yPos = yMax; 
            velocity = new Vector2(velocity.x, 0);
        }

        xPos = Mathf.Clamp(xPos, xMin, xMax); //limit to the plane's x     extent in local space
        yPos = Mathf.Clamp(yPos, yMin, yMax); //limit to the plane's y (z) extent in local space

        //update transform
        transform.localPosition = new Vector3(xPos, 0, yPos);
    }
}

В единстве: создайте плоскость, создайте сферу, затем перетащите сферу на плоскость, чтобы плоскость была ее родителем. Теперь сфера движется в локальном пространстве плоскости. Вы можете назвать его «Якорь камеры». Перетащите камеру на якорь, чтобы якорь был ее родителем. Установите вращение x вашей камеры на требуемый наклон в инспекторе и его вращение до 45; нулевое положение, а затем перетащить его вдоль локальной оси z на требуемое расстояние просмотра, чтобы увидеть землю и якорь. Перетащите этот скрипт на якорь. Создайте направленный свет. Используя клавиши со стрелками, вы увидите, как привязка ограничивает вектор просмотра камеры (в центре его окна просмотра), чтобы он никогда не покидал плоскость земли. Обратите внимание, что ваши элементы управления немного смешны и обрабатывают вверх /вниз и влево /вправо, как диагональные плоскости. Исправление состоит в замене:

    xAcceleration += Input.GetKey(KeyCode.LeftArrow)    ? -1 : 0;
    xAcceleration += Input.GetKey(KeyCode.RightArrow)   ? +1 : 0;
    yAcceleration += Input.GetKey(KeyCode.UpArrow)      ? +1 : 0;
    yAcceleration += Input.GetKey(KeyCode.DownArrow)    ? -1 : 0;

с

    if (Input.GetKey(KeyCode.UpArrow))
    {
        xAcceleration += +1;
        yAcceleration += +1;
    }
    if (Input.GetKey(KeyCode.DownArrow))
    {
        xAcceleration += -1;
        yAcceleration += -1;
    }
    if (Input.GetKey(KeyCode.LeftArrow))
    {
        xAcceleration += -1;
        yAcceleration += +1;
    }
    if (Input.GetKey(KeyCode.RightArrow))
    {
        xAcceleration += +1;
        yAcceleration += -1;
    }

Обратите внимание, что вы можете эффективно увеличивать и уменьшать масштаб, меняя орфографический код size в инспекторе.

Это устраняет необходимость в выполнении матричных преобразований и решает другие проблемы (ниже).

Ограничить края камеры до границ рядом с

Предположим, что все точно так же, как в вашей диаграмме - крошечный мир относительно большого окна просмотра камеры. В этом случае я точно вижу, как вы ожидаете, что это сработает. Мы поместим красный прямоугольник вокруг прямоугольника с выравниванием по оси, который немного больше, и точно содержит изометрическую плоскость заземления. Мы будем называть этот стиль реализации A. Это то, что Bálint очень разумно предложил, учитывая вашу диаграмму. Но что, если у вас есть большая карта с меньшим видовым экраном? Я сомневаюсь, что это будет идеальным решением для вас: даже если карта размером всего в 4 раза больше, чем на вашей диаграмме, вы сможете прокручивать большие, темные пространства в верхнем левом углу, вверху справа, внизу влево и вправо справа от карты, потому что вы ограничиваетесь только прямоугольником экрана. Не хорошо?

Я предполагаю, что вам понадобится другой механизм, который блокирует камеру, чтобы ее центр не мог покинуть границы наземного плана; вы могли бы никогда не запускать камеру в большие пустотные пространства. Я поставил это выше, назовите его «Стиль реализации B».

В любой из реализаций вам понадобится достаточная фиктивная плитка на внешней поверхности земли, чтобы обеспечить максимальное масштабирование, вы никогда не увидите черное пространство. Я видел это в некоторых играх XCom /UFO. Вычисляя, сколько строк /столбцов дополнительных фрагментов вам нужно точно, будет зависеть от значения size на вашей камере (которое напрямую связывается до высоты видового экрана в единстве) и некоторый расчет вашей ширины плитки по сравнению с высотой плитки, которую можно выполнить с помощью Mathf.Sin(). Вам также может понадобиться значение size, относящееся к горизонтальному или вертикальному - это так же просто, как size * Screen.width / Screen.height. Тем не менее, все это, вероятно, не нужно, так как вы можете вручную проверить, сколько плиток стоит буферизации для вас. Протестируйте соотношение экрана 2: 1, чтобы быть уверенным, что вы его покрыли.

В приведенном выше решении, чтобы создать буферное пространство внутри вашей существующей карты плитки , замените это:

    //position
    float xPos = transform.localPosition.x + velocity.x * Time.deltaTime;
    float yPos = transform.localPosition.z + velocity.y * Time.deltaTime; //note the interchanged y/z here due to Unity's coord system.

с:

       //position
        float xPos = transform.localPosition.x + velocity.x * Time.deltaTime;
        float yPos = transform.localPosition.z + velocity.y * Time.deltaTime; //note the interchanged y/z here due to Unity's coord system.

        float buffer = 2f; //2 units in world space which might be 2 tiles across in your code.
        float xMinLimited = xMin + buffer;
        float xMaxLimited = xMax - buffer;
        float yMinLimited = yMin + buffer;
        float yMaxLimited = yMax - buffer;

        //We'd usually just use 2 Mathf.Clamp(val, min, max) calls here, but we need to know
        //the outcome of the clamping, so as to also restrict velocity if we hit a map side.
        if (xPos < xMinLimited)
        {
            xPos = xMinLimited; 
            velocity = new Vector2(0, velocity.y); 
        }
        if (xPos > xMaxLimited)
        {
            xPos = xMaxLimited; 
            velocity = new Vector2(0, velocity.y);
        }
        if (yPos < yMinLimited)
        {
            yPos = yMinLimited; 
            velocity = new Vector2(velocity.x, 0);
        }
        if (yPos > yMaxLimited)
        {
            yPos = yMaxLimited; 
            velocity = new Vector2(velocity.x, 0);
        }
ответил Arcane Engineer 9 Maypm17 2017, 19:56:18
0

Изометрическая система координат в основном представляет собой только повернутую и масштабированную систему координат.

Сначала вам нужно создать матрицу вращения с углом -45 градусов, если ваш вперед находится в верхнем левом углу и на 45 градусов, если он находится в правом верхнем углу.

Затем вы создаете матрицу масштабирования со шкалой vec3(1, d, 1), где đ зависит от угла изометрического вида. Возьмите высоту плитки и разделите ее на ширину.

Вы не можете использовать Matrix4.TRS для этого, вам нужно сначала умножить матрицу масштабирования на матрицу вращения:

screenPos = rot * scale * pos

Pos - это исходное положение угла перед вращением.

Это даст вам положение на экране углов.

ответил Bálint 4 Mayam17 2017, 09:09:50
-1

Вам нужно проверить координаты X. В приведенном выше снимке 50 единиц - это центр, который я предполагаю, и 100 единиц будут максимальными. Поэтому проверьте, прокручивает ли ваша камера более 100 единиц или менее 0 единиц. Если условие истинно, прекратите прокрутку.

ответил Saad Anees 5 Maypm17 2017, 14:00:01

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132