Как ограничить движение click'n'drag области?

11

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

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

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

Маркерная диаграмма пути

Итак, я застрял с двумя проблемами:

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

И во-вторых, после того как я определил область, где маркер может быть размещен, как я могу обеспечить, чтобы маркер оставался только в этой области? Например, если игрок щелкает и перетаскивает маркер, он может свободно перемещаться в пределах рабочей области, но не должен выходить за границы области. Так, например, если игрок начинает перетаскивать маркер вверх, он будет двигаться вверх до тех пор, пока не достигнет конца рабочей области (первая диаграмма ниже), но если после этого игрок начнет перетаскивать вбок, маркер должен следовать за перетаскиванием, оставаясь неподвижным. в пределах области (вторая диаграмма ниже).

Первая диаграмма: движение вверх Вторая диаграмма: после перетаскивания

Я надеюсь, что это не слишком запутанно. Спасибо, парни.

Изменить: В случае, если это имеет значение, я использую C ++ с Marmalade SDK.

Vexille
источник
Я думаю, что вы ищете термин «путевая точка», и вы можете математически определить серую область? Решение вашей проблемы, вероятно, будет намного проще, если это так.
Джон Макдональд
Arent путевые точки, связанные с поиском пути и тому подобное? Кроме того, математическое определение серой области - это одна из моих проблем: PI думаю, что я мог бы вычислить что-то вроде прямоугольника или даже круга, но такой формы, с двумя дугами по сторонам и двумя другими сторонами по углу ... немного над моей головой.
Vexille
Вам также необходимо указать, какой язык и / или инструментарий вы используете, поскольку решения в основном зависят от платформы.
Raceimaztion
В самом деле? Я подумал, что может помочь алгоритмическое решение или даже псевдокод. Я все равно отредактирую вопрос, на всякий случай.
Vexille
Определите форму в полярных координатах (максимальный угол от угла символов и минимальное / максимальное расстояние от символа). Затем обновляйте только путевую точку, где находится мышь, если мышь находится в этих границах. Если оно превышает какое-либо из этих экстремумов, присвойте ему максимально возможное значение. Начните обновление, если щелкнуть внутри области, и остановите, когда отпустите кнопку мыши
ClassicThunder

Ответы:

8

Вы можете определить рабочую область, такую ​​как в вашем вопросе, с тремя значениями:

float innerRadius;
float outerRadius;
float maxAngle;

Эти значения будут основаны на центральной точке, которая может быть или не быть позицией игрока. Форма обрабатываемой области зависит от того, где вы разместите эту точку.

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

В приведенном выше примере центральная позиция находится на некотором расстоянии (скажем, 50 единиц) позади игрока. Это можно легко рассчитать как:

float offset = -50;
Vector2 centerPosition = playerPosition + offset * playerForward;

Чтобы ограничить положение маркера этой обрабатываемой областью, сначала переместите маркер, как обычно. Затем проверьте расстояние между центральной точкой и маркером:

Vector2 direction = markerPosition - centerPosition;
float distance = direction.Length();
direction.Normalize();
markerPosition = centerPosition + direction * clamp(distance, innerRadius, outerRadius);

Наконец, проверьте угол наклона маркера в указанном диапазоне. Я буду использовать псевдокод для этого:

- Find angle between vector C->M and vector playerForward
- If abs(angle) <= maxAngle Then do nothing
- Else If angle > 0 Then rotate M around C by maxAngle-angle
- Else If angle < 0 Then rotate M around C by -maxAngle-angle

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

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

Редактировать: подумав, может показаться более естественным, если сначала проверить угол, а затем расстояние, поэтому попробуйте обе альтернативы!

Дэвид Гувея
источник
Отличное решение! Я сталкивался с чем-то, что включало два круга и треугольник, но ваше решение элегантно упрощает это. Что касается проверки угла, я подумал о том, чтобы иметь нормализованный вектор, который стоял в maxAngle (и другой в -maxAngle) от playerForward, который можно было бы умножить на длину C-> M, если бы он имел границы под углом. Я предполагаю, что ваше решение вращения М вокруг С будет менее затратным, верно?
Vexille
@Vexille Ну, вращение включает в себя cosи sinоперацию, так что я не уверен. Но для вычисления этих двух векторов вам также нужно повернуть их, хотя вам нужно делать это только при изменении вектора вперед. В любом случае это не должно иметь большого значения, выберите тот, который вы предпочитаете реализовать.
Дэвид Гувея
10

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

1. Возьмите свою область:

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

2. И преобразовать его в монохроматическое растровое изображение:

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

3. Клонируйте растровое изображение и уменьшите его до 50%:

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

4. И так далее, пока не появится растровое изображение менее 4 пикселей в ширину / высоту:

введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь шкала: 2, 3, 4, 5, 6

5. Теперь у нас есть монохромные растровые изображения разного разрешения: введите описание изображения здесь

6. Возьмите последнее изображение (здесь «scale_6») и переберите все его пиксели.

  • переводить координаты каждого пикселя в экранные координаты: x = Math.pow ( 2, scale_level );где scale_level - это число, которое мы добавили после «scale_». Мы также можем назвать его уровнем дерева квадрантов, хотя на самом деле мы не работаем с деревом квадрантов. Сделайте то же самое с y.
  • проверьте, является ли пиксель в переведенном x & y черным. Если нет, то это не часть фигуры, и вы должны просто continueперейти к следующему шагу цикла
  • проверьте, находится ли пиксель ближе к курсору мыши, чем ранее проверенный пиксель - если да, сохраните координаты пикселя - используйте координаты перед переводом, то есть координаты внутри растрового изображения.
  • в конце цикла умножьте эти координаты на 2: x *= 2; y*=2;чтобы перевести их в координаты на следующем изображении (предыдущий масштаб)

7. Возьмите предыдущее изображение (здесь «scale_5»), но не просматривайте все пиксели; начинаются с x = сохраненные_x и заканчиваются с x = сохраненные_x + 2, то же самое с y. То есть, поскольку теперь вы будете проходить только 4 пикселя для каждого уровня! Остальное как на с. 6.

8. Возьмите первое изображение (самое большое = изображение с наибольшим разрешением), снова пройдитесь по 4 пикселям, и у вас наконец получится пиксель, ближайший к курсору мыши:

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

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

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

9. Однако я рассматриваю «М» как точку здесь. Если вы хотите, чтобы это был полностью подходящий круг, вам нужно circle.radiusсначала сжать (сжать) форму на пиксели.

Я подумал, что добавлю, что этот алгоритм будет работать, только если вы будете использовать не монохроматические, а полутоновые изображения и рассматривать пиксель как «полный», если он не белый, и как «пустой», если он точно белый ... ИЛИ, если вы изменяете размер алгоритм меняет каждую группу из 4 пикселей на 1 черный пиксель каждый раз, когда хотя бы один из этих 4 пикселей не был белым.

Маркус фон Броади
источник
2
+1 за ответ для форм, которые трудно (если не невозможно) выразить математически.
Cypher
Вау, очень интересно. +1 тоже: D
Vexille
Я реализовал это в реальном проекте, и должен сказать, что возникли некоторые проблемы. По сути, вам нужно составить список ячеек сетки, в котором вы берете ближайшую названную ячейку сетки closestи проверяете расстояние до самой дальней точки в closest- давайте назовем расстояние furthest_dist. Теперь вам нужно убрать из списка все ячейки, у которых есть ближайшая точка дальше, чем furthest_distи перейти на уровень глубже. Итак, вместо чего-то вроде этого: i.imgur.com/4UuFo.png Это что-то вроде этого: i.imgur.com/dyTT3.png
Маркус фон Броади