Це погана ідея «відображення» положення миші на екрані, щоб виявлення зіткнень працювало незалежно від роздільної здатності?


16

Розглянемо гру з роздільною здатністю за замовчуванням 800x600. Об'єкти з масками зіткнення розміщуються в ігровому світі розміром 800x600. Маски зіткнення можуть виявити, коли миша стикається з ними.

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

A.) Масштабуйте світ до 1024x768 і відповідно масштабуйте маску зіткнення кожного об'єкта.

B.) "Позначте" положення миші на вихідний світ (800x600).

Під "картою" я маю на увазі просто масштабувати положення миші на вихідний світ 800х600. Так, наприклад, якщо положення миші на екрані (1024, 768), то положення миші у світі дорівнює (800, 600).

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

Який метод я повинен використовувати: A, B або щось інше?


2
Як вказує Classic Thunder, координати відображення не повинні відповідати світовим координатам або об'єктамним координатам. Зазвичай ви хочете мати перетворення (в основному, як ви кажете відображення , але зазвичай це робиться з матричною математикою).
Ден

Відповіді:


38

Зазвичай (навіть для 2d ігор) існує окрема система координат для гри, яка не залежить від роздільної здатності, це зазвичай називають світовим простором. Це дозволяє масштабувати до довільної роздільної здатності.

Найпростіший спосіб досягти цього - використовувати 2D камеру, яка по суті є матрицею, яка визначає перехід від світового простору (ваших довільних одиниць) до простору екрана (пікселів на екрані). Це робить справу з математикою дрібницею.

Подивіться нижче на розділ прокрутки камери XNA 2d - навіщо використовувати матричне перетворення?

Це робить дуже легким перетворення між визначеннями системи координат

Для переходу з екрану до світового простору просто використовуйте Vector2.Transform. Зазвичай це використовується для отримання місця миші у світі для вибору предметів.

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

Щоб перейти зі світу до простору просто, навпаки.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

Немає недоліків у використанні іншої матриці, крім того, що вона потребує невеликого вивчення.


... і справді, в цілому, сучасне обладнання призначене для матричних операцій за допомогою архітектур векторизації, таких як SSE, NEON тощо. Отже, це розумний вибір - ви будете економити цикли процесора на невекторизованих підходах.
Інженер

1
Легка відмова від відповідальності: XNA була припинена мікрософт, але Monogame використовує той же API.
Фарап

1
Звичайно, розумно використовувати матриці для перекладу між двома системами координат. Але як це відповідає на питання? Ви все ж повинні вирішити, чи хочете ви перейти від системи A до системи B або від B до A і які наслідки є, якщо такі є.
мастов

"Чи погана ідея" відображати "положення миші на екрані, щоб виявлення зіткнень працювало незалежно від роздільної здатності?" відповідає "розумно використовувати матриці для перекладу між двома системами координат" ...
ClassicThunder

Я також відповідаю: "Ви все-таки повинні вирішити, чи хочете ви зробити карту від системи A до системи B або від B до A, і які наслідки є, якщо такі є". кажучи, ви повинні використовувати довільну, не прив'язану до роздільної здатності та масштабу. Отже, C -> A або C -> B залежно від того, чи A або B є роздільною здатністю. Звичайно, ви можете мати C рівну базову роздільну здатність, яку ви розробляєте, та масштабувати звідти. Точка всьому математика відбувається в одній і тій же системі координат, і ви лише масштабуєте для візуалізації.
ClassicThunder

2

Іншим варіантом буде: На кожній події введення миші переміщуйте курсор миші в грі на кількість ігрових пікселів, що відповідає кількості пікселів у події миші. Це природно для 3D-ігор, які фіксують реальний вказівник миші на центр та обертають напрямок цілі на величину, що відповідає вхідному рухом миші, але ви могли б зробити це так само, перемістивши спрайт, що представляє ігровий курсор миші.

Ви, очевидно, повинні ігнорувати будь-які події руху миші, спричинені перекручуванням її до центру, і якщо у вашій грі є взагалі відставання введення, невідповідальність курсору буде найбільш яскравим і помітним аспектом.

Частково те, яке рішення ви використовуєте, залежить від того, наскільки важливою є позиція миші для гри. Якщо це RTS і гравець просто клацає, щоб вибрати одиниці, ви можете, ймовірно, просто піти з тим, що з ваших A або B простіше. Якщо це стрілок зверху вниз і миша безпосередньо керує рухами персонажа, то, напевно, ви хочете більш глибоке рішення, яке обмежує кількість змінності в тому, як персонаж може рухатися на основі не тільки роздільної здатності, але і таких речей, як рух миші швидкість. Якщо миша замість цього керує напрямком націлювання, то вам потрібно інше рішення тощо.


0

Відповідь ClassicThunder правильна, але я хотів би навести приклад альтернативного / більш простого способу досягнення бажаного ефекту. Це більш просте рішення для швидкого прототипування, випадків, коли ви не маєте доступу до повнофункціональної бібліотеки, або для випадків, коли у вас немає доступу до GPU (наприклад, у вбудовані системи).

Щоб виконати вказане відображення, ви можете використовувати таку функцію (припустимо, вона визначена в статичному класі 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);

-1

Варіант (C): повернення роздільної здатності екрана до 800x600.

Навіть якщо ви цього не зробите, розгляньте це як продуманий експеримент. У такому випадку на екрані лежить відповідальність за зміну розміру графіки відповідно до її фізичного розміру, а потім на відповідальність операційної системи за надання вказівних подій з роздільною здатністю 800x600.

Я думаю, що все залежить від того, ваша графіка є растровою чи векторною. Якщо вони растрові, а ви перетворюєте на буфер розміром 800x600, то так, набагато простіше переставити мишу в екранний простір і проігнорувати реальну роздільну здатність екрана. Однак великим недоліком цього є те, що підвищення масштабу може виглядати некрасиво, особливо якщо ви робите блокадної "8-бітової" графіки в стилі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.