Экранное пространство в мировое пространство

10

Я пишу 2D-игру, в которой в моем игровом мире ось X движется слева направо, а ось Y - сверху вниз, а ось Z - вне экрана:

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

Хотя мой игровой мир нисходящий, игра отображается с небольшим уклоном:

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

Я работаю над проецированием из мирового пространства на пространство экрана и наоборот. У меня бывший работает следующим образом:

var viewport = new Viewport(0, 0, this.ScreenWidth, this.ScreenHeight);
var screenPoint = viewport.Project(worldPoint.NegateY(), this.ProjectionMatrix, this.ViewMatrix, this.WorldMatrix);

Метод NegateY()расширения делает именно то, на что это похоже, так как XNA у оси проходит снизу вверх , а не сверху вниз. Снимок экрана выше показывает, что все это работает. По сути, у меня есть куча точек в трехмерном пространстве, которые я затем визуализирую в пространстве экрана. Я могу изменять свойства камеры в режиме реального времени и видеть ее анимацию в новой позиции. Очевидно, что моя настоящая игра будет использовать спрайты, а не точки, и положение камеры будет фиксированным, но я просто пытаюсь получить всю математику перед тем, как перейти к этому.

Теперь я пытаюсь обратить обратно в другую сторону. То есть, учитывая точки x и y в пространстве экрана выше, определяют соответствующую точку в мировом пространстве. Поэтому, если я наведу курсор, скажем, на нижний левый угол зеленой трапеции, я хочу получить показания мирового пространства (0, 480). Г координат не имеет значения. Или, скорее, координата z всегда будет нулевой при отображении обратно в мировое пространство. По сути, я хочу реализовать этот метод подписи:

public Vector2 ScreenPointToWorld(Vector2 point)

Я попробовал несколько вещей, чтобы заставить это работать, но мне просто не везет. Мое последнее мнение состоит в том, что мне нужно Viewport.Unprojectдважды позвонить с разными значениями ближнего / дальнего z , вычислить результирующий результат Ray, нормализовать его, а затем вычислить пересечение Rayс a, Planeкоторое в основном представляет уровень земли моего мира. Однако я застрял на последнем шаге и не был уверен, что я слишком усложняю вещи.

Кто-нибудь может указать мне правильное направление о том, как этого добиться?

меня--
источник

Ответы:

4

Я думаю, что ваша идея в значительной степени на месте! Сначала рассчитайте луч для вашего курсора, используя ближнюю и дальнюю плоскости в качестве значений Z для ваших 2D-координат (т.е. используйте 0 и 1 для вашей координаты Z). Вот вспомогательный метод для обработки этого:

public Ray GetScreenRay(Vector2 screenPosition, Viewport viewport, Matrix projectionMatrix, Matrix viewMatrix, Matrix worldMatrix)
{
    Vector3 nearPoint = viewport.Unproject(new Vector3(screenPosition, 0f), projectionMatrix, viewMatrix, worldMatrix);
    Vector3 farPoint = viewport.Unproject(new Vector3(screenPosition, 1f), projectionMatrix, viewMatrix, worldMatrix);
    return new Ray(nearPoint, Vector3.Normalize(farPoint - nearPoint));
}

Затем вам нужно иметь Planeэкземпляр, который соответствует вашей земле. Самый простой способ вычислить его - использовать конструктор, который берет три точки на плоскости и пропускает любые три вершины от наземного объекта. Пример того, как рассчитать наземную плоскость:

Plane groundPlane = new Plane(ground.Vertices[0], ground.Vertices[1], ground.Vertices[2]);

И, наконец, найдите точку пересечения между лучом и плоскостью, используя этот метод . Вы можете использовать параметр результата out для вычисления координат пересечения, как в примере ниже:

float? result;
mouseRay.Intersects(ref groundPlane, out result);
if(result != null)
    Vector3 worldPoint = mouseRay.Position + mouseRay.Direction * result.Value;

Возможно, вам придется что-то делать с этим NegateYметодом, но я не уверен, где.

Дэвид Гувея
источник
Это очень полезно - большое спасибо. Это была последняя строка, которую я пропустил. Я не мог понять, что делать с поплавком, когда он у меня был! Я думаю, что мне нужно, чтобы твое имя было в моих игровых титрах :)
я--
@ user13414 Действительно, документация по этому методу немного скудна и не дает никакого примера того, как использовать расстояние для возврата к точке пересечения.
Дэвид Гувейя