Мышь: viewport и локальные координаты
Преобразовать клик и различить Relative и ScreenRelative.
Сначала определите систему координат. Позиция курсора в окне, точка внутри viewport, мировая позиция 2D и локальная позиция объекта — разные величины. Совпадение чисел в неподвижной сцене не делает их взаимозаменяемыми.
Откуда брать точку
| Контекст | Значение |
|---|---|
| Событие мыши в обработчике Entity | InputEventMouseButton.Position или InputEventMouseMotion.Position — координаты события в viewport. |
| GUI-обработчик Control | Событие может быть уже преобразовано в локальную систему Control. Не применяйте преобразование viewport повторно. |
| Текущее состояние viewport | GetViewport().GetMousePosition() — текущая позиция, а не обязательно позиция прежнего события. |
| Объект 2D | Преобразуйте точку из того же viewport в локальную систему объекта. |
| Выбор объекта 3D | Постройте луч камерой по точке viewport; одной координаты глубины из мыши не получить. |
GlobalPosition события мыши не является мировой позицией вашего Entity2D. Для клика по конкретному событию сохраняйте его Position; повторный опрос позже может вернуть уже другую позицию.
Viewport → локальная точка 2D
Файл ниже учитывает преобразование объекта и canvas. Используйте его для точки того же viewport, где находится цель; цель должна быть подключена к корректному дереву, а преобразование — обратимо.
using Reckfell;
public static class PointerCoordinates
{
// point must be in the same viewport as target.
public static Vector2 ToLocal(CanvasItem target, Vector2 point) =>
target.GetGlobalTransformWithCanvas().AffineInverse() * point;
}В _UnhandledInput() проверьте InputEventMouseButton с Pressed=true и ButtonIndex=MouseButton.Left, затем передайте его Position в PointerCoordinates.ToLocal(this, position) для своей Entity2D. Вы получите точку в её локальных координатах. _UnhandledInput() подходит, если интерфейс должен получить возможность обработать клик раньше игровой логики; маршрут события.
Проверенный пример: canvas имеет масштаб 2 и сдвиг (100, 50), объект — позицию (10, 20) и масштаб 2. Точка viewport (168, 154) преобразуется в локальную (12, 16). Простое вычитание позиции объекта здесь дало бы неправильный результат.
Не используйте матрицу одного viewport для событий другого. Если событие уже преобразовано в локальные координаты Control, повторное применение этой функции тоже даст неверную точку.
Перемещение мыши и поворот камеры
Relative — приращение, которое может масштабироваться при преобразовании события. ScreenRelative сохраняет экранное приращение; ScreenVelocity — экранную скорость. Это не абсолютная позиция.
В проверке двукратное преобразование события изменило Relative с (3, 4) на (6, 8), а ScreenRelative осталось (3, 4). Для чувствительности обзора, не зависящей от масштаба отображения, используйте подходящее экранное приращение. Не умножайте уже полученное смещение за событие на delta второй раз как скорость; для стика, задающего угловую скорость, расчёт другой.
В режиме захвата не рассчитывайте поворот по абсолютной позиции курсора. Обрабатывайте относительное движение и предусмотрите освобождение курсора для меню. Сам захват курсора зависит от оконной системы и не проверяется синтетическим событием.
Для 3D
Используйте Camera3D.ProjectRayOrigin() и ProjectRayNormal(), затем физический запрос с нужной длиной и маской. Камера и точка должны относиться к одному viewport. Готовый пример: выбор объекта лучом.
Выполнены преобразования при масштабе canvas и объекта, различение Position/GlobalPosition, Relative/ScreenRelative. Живой курсор, захват ОС, GUI-маршрутизация, вложенные viewport, Camera2D и экспорт этим тестом не оценивались.