Является ли плохой идеей «отобразить» положение мыши на экране, чтобы обнаружение столкновений работало независимо от разрешения?

16

Рассмотрим игру с разрешением по умолчанию 800x600. Объекты с масками столкновений размещаются в игровом мире размером 800х600. Маски столкновения могут определять, когда мышь сталкивается с ними.

Теперь предположим, что мы масштабируем игру до 1024x768 (предположим, что мы масштабируем графику, просто визуализируя все в слой, а затем масштабируя весь слой сразу). У нас есть два варианта правильной работы коллизий с мышью в этом новом разрешении:

A.) Масштабируйте мир до 1024x768 и соответственно масштабируйте маску столкновения каждого объекта.

B.) «Нанесите на карту» положение мыши в исходном мире (800x600).

Под «картой» я подразумеваю просто масштабирование положения мыши в исходном мире 800x600. Так, например, если положение мыши на экране (1024, 768), то положение мыши в мире (800, 600).

Теперь , очевидно, вариант Б требует способа меньше вычислений и, вероятно , менее склонен к геометрическим ошибкам, но он также чувствует себя своим рода «хак» для меня, как есть непредвиденные последствия происходят с помощью этого метода , который будет ад , чтобы исправить позже.

Какой метод я должен использовать: A, B или что-то еще?

user3002473
источник
2
Как указывает Classic Thunder, отображаемые координаты не должны совпадать с мировыми координатами или координатами объекта. Обычно вы хотите иметь преобразования (в основном, как вы говорите, отображения , но обычно это делается с помощью математической математики).
Дан

Ответы:

38

Как правило (даже для 2d игр) для игры существует отдельная система координат, которая не зависит от разрешения, обычно ее называют мировым пространством. Это позволяет масштабировать до произвольного разрешения.

Самый простой способ добиться этого - использовать 2D-камеру, которая, по сути, представляет собой матрицу, которая определяет переход от мирового пространства (ваши произвольные единицы) к экранному пространству (пиксели на экране). Это делает работу с математикой тривиальной.

Посмотрите на приведенный ниже раздел XNA 2d Camera Scrolling - зачем использовать матричное преобразование?

Это позволяет легко конвертировать определения системы координат

Для перехода от экрана к мировому пространству просто используйте Vector2.Transform. Это обычно используется для определения местоположения мыши в мире для выбора объектов.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

Чтобы перейти от мира к экранному пространству, просто сделайте обратное.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

Нет недостатков в использовании матрицы, за исключением небольшого обучения.

ClassicThunder
источник
... и, вообще говоря, современное оборудование разработано для матричных операций с использованием векторизационных архитектур, таких как SSE, NEON и т. д. Так что это разумный выбор - вы сэкономите циклы ЦП по сравнению с невекторизованными подходами.
инженер
1
Небольшой отказ от ответственности: XNA была прекращена Microsoft, но Monogame использует тот же API.
Pharap
1
Конечно, разумно использовать матрицы для перевода между двумя системами координат. Но как это отвечает на вопрос? Вы все еще должны решить, хотите ли вы отобразить систему A в систему B или B в A и каковы будут последствия, если таковые имеются.
мастов
«Это плохая идея« отобразить »положение мыши на экране, чтобы обнаружение столкновений работало независимо от разрешения?» отвечает: «Умно использовать матрицы для перевода между двумя системами координат» ...
ClassicThunder
Я также отвечаю: «Вы все равно должны решить, хотите ли вы отображать систему А в систему В или В в А, и каковы будут последствия, если они есть». говоря, что вы должны использовать произвольный, не привязанный к разрешению и масштабу от него. Поэтому C -> A или C -> B в зависимости от того, является ли A или B разрешением. Конечно, вы можете иметь C равным базовому разрешению, для которого вы разрабатываете, и масштабировать оттуда. Точка - все математика происходит в одной и той же системе координат, и вы только масштабируете для рендеринга.
ClassicThunder,
2

Другой вариант: при каждом входном событии перемещения мыши перемещайте внутриигровой курсор мыши на количество игровых пикселей, соответствующее количеству пикселей в событии мыши. Это естественно для 3D-игр, которые фиксируют реальный указатель мыши на центре и поворачивают направление прицеливания на величину, соответствующую движению мыши при вводе, но вы можете сделать то же самое, переместив спрайт, представляющий внутриигровой курсор мыши.

Вы, очевидно, должны игнорировать любые события перемещения мыши, вызванные деформацией ее к центру, и если ваша игра имеет какую-либо задержку ввода, то неотзывчивость курсора будет самым неприятным и заметным аспектом.

Частично, какое решение вы используете, зависит от того, насколько важно положение мыши для игрового процесса. Если это RTS и игрок просто щелкает, чтобы выбрать юниты, вы, вероятно, можете просто выбрать любую из ваших A или B. Если это стрелок сверху вниз и мышь непосредственно контролирует движения персонажа, то вам, вероятно, понадобится более глубокое решение, которое ограничивает степень изменчивости в том, как персонаж может двигаться, основываясь не только на разрешении, но и на таких вещах, как движение мыши. скорость. Если вместо этого мышь контролирует направление прицеливания, вам понадобится другое решение и т. Д.

Random832
источник
0

Ответ ClassicThunder правильный, но я бы хотел привести пример альтернативного / более простого способа достижения желаемого эффекта. Это более простое решение для быстрого создания прототипов, в случаях, когда у вас нет доступа к полнофункциональной библиотеке, или в случаях, когда у вас нет доступа к графическому процессору (например, во встроенных системах).

Для выполнения указанного отображения вы можете использовать следующую функцию (предположим, что она определена в статическом классе Helper):

static float Map(float value, float fromLow, float fromHigh, float toLow, float toHigh)
{
    return ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)) + toLow;
}

(Вы не указали язык, но я вижу, что вы немного знаете C #, поэтому мой пример на C #.)

Затем вы можете использовать эту функцию следующим образом:

float mouseXInWorld = Helper.Map(Mouse.X, 0, Screen.Width - 1, camera.Bounds.X, camera.Bounds.X + camera.Bounds.Width - 1);
float mouseYInWorld = Helper.Map(Mouse.Y, 0, Screen.Height - 1, camera.Bounds.Y, camera.Bounds.Y + camera.Bounds.Height - 1);

Где camera.Boundsпрямоугольник, представляющий область мира, которую может видеть камера (то есть область, проецируемая на экран).

Если у вас есть класс Vectorили Point, вы можете еще больше упростить этот процесс, создав 2D-эквивалент функции карты, например:

static Vector Map(Vector value, Rectangle fromArea, Rectangle toArea)
{
    Vector result = new Vector();
    result.X = Map(value.X, fromArea.X, fromArea.X + fromArea.Width - 1, toArea.X, toArea.X + toArea.Width - 1);
    result.Y = Map(value.Y, fromArea.Y, fromArea.Y + fromArea.Height - 1, toArea.Y, toArea.Y + toArea.Height - 1);
    return result;
}

Что сделало бы ваш код отображения простым однострочником:

Vector mousePosInWorld = Map(Mouse.Pos, Screen.Bounds, camera.Bounds);
Pharap
источник
-1

Опция (C): изменить разрешение экрана обратно на 800x600.

Даже если вы этого не сделаете, считайте это мысленным экспериментом. В этом случае ответственность дисплея заключается в том, чтобы изменить размер графики, чтобы он соответствовал ее физическим размерам, а затем операционная система должна предоставить вам события указателя с разрешением 800x600.

Я думаю, что все зависит от того, является ли ваша графика растровой или векторной. Если они растровые, и вы выполняете рендеринг в буфер 800x600, то да, гораздо проще переназначить мышь в пространство экрана и игнорировать реальное разрешение экрана. Однако большой недостаток этого заключается в том, что масштабирование может выглядеть ужасно, особенно если вы работаете с блочной графикой в ​​стиле «8 бит».

pjc50
источник