authors_main:task_editor:advanced:gps

Коды по GPS-координатам

Движок умеет сам вбивать код, когда игрок дошёл до нужной точки на местности. Сделать это можно двумя способами:

  • Триггер на сервере — зона пишется маркером {! on gps … !} в тексте задания. Координат цели нет в странице игрока нигде: ни в тексте, ни в скрипте, ни в ответах сервера. Это основной способ.
  • Точки в скрипте — массив g_targets в «Скрипте задания». Проверка идёт в браузере, поэтому координаты цели лежат в исходном коде страницы. Способ старый, но нужен, когда браузеру самому надо знать, где цель: показать расстояние, нарисовать точку на карте.
Важно: геолокация включается только с разрешения игрока. Если игрок откажет, ни одна зона не сработает, поэтому оставляйте запасной способ пройти уровень (обычный код, подсказка со сливом) или заранее предупредите команды, что GPS обязателен.
Трек команды. Пока слежение включено, движок записывает путь команды — по устройству, сегментами «стояли» и «шли», плюс срабатывания зон и отброшенные замеры. На странице игры у автора есть ссылка «Треки GPS»: там отчёт с признаками подмены геолокации по командам (вердикт — не приговор, у каждого признака есть честные объяснения), спорные моменты вроде «дошли до зоны, но код не вбился» и карта. Трек хранится 90 дней от последней записи; удалить раньше можно там же.

Оба способа начинаются одинаково — с вызова gps_start() в поле «Скрипт задания»:

gps_start();

Без него браузер не спросит координат, и ни одна зона не сработает. Если в задании есть триггеры, а gps_start() нигде нет, редактор предупредит об этом при сохранении.

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

У задания с GPS рядом с названием стоит значок-спутник. Он показывает игроку, работает ли геолокация, и сам же её включает: на телефоне браузер даёт координаты только после нажатия, поэтому значок стоит с самого начала, ещё до первого замера.

Значок GPS в шапке задания: зелёный — можно идти

Цвет Что значит Что сделает нажатие
серый слежение не включено включит его
красный позиции нет: ищем спутники, игрок запретил доступ или геолокация недоступна начнёт поиск заново или подскажет, как разрешить доступ
жёлтый позиция есть, но точность хуже 30 метров — такой замер движок не засчитывает обновит позицию
зелёный всё работает, можно идти обновит позицию

Если навести на значок, подсказка назовёт состояние словами и текущую точность.

Зона объявляется маркером в тексте задания:

{! on gps 51.5299 -0.1275 radius 30: code ньютон !}
  • 51.5299 -0.1275 — центр зоны: широта и долгота, в том виде, в каком их копируют из Google Maps или OpenStreetMap. Запятая между ними не обязательна.
  • radius 30 — радиус в метрах. Можно не писать — по умолчанию 30.
  • code ньютон — что сделать, когда игрок в зоне: вбить код. Код должен быть кодом или бонусом этого задания.
  • Подпись зоны для себя пишется комментарием внутри маркера: {! on gps /* задний двор */ 51.5299 -0.1275: code ньютон !}. Игрок её не увидит, а тестер увидит на карте.

Общие правила маркера — где он срабатывает, что проверяет сохранение, как пишутся несколько действий — на странице Серверные действия.

Двор, сквер, квартал — зона-многоугольник. После слова polygon идут координаты вершин подряд, парами «широта долгота», от 3 до 30 вершин. Радиуса у многоугольника нет — границы задают вершины.

{! on gps /* сквер */ polygon 51.5305 -0.1280 51.5320 -0.1262 51.5313 -0.1230 51.5291 -0.1238: code сквер !}

Пока игрок идёт, страница отправляет его координаты на сервер — когда он сдвинулся хотя бы на несколько метров, но не чаще, чем раз в пару секунд. Сервер отвечает, в какой зоне игрок стоит, и страница вбивает код этой зоны — молча, обычным ответом. В журнале ответов такой код записан за игроком, который дошёл.

  • Расстояния до цели движок не показывает — по расстояниям из нескольких точек цель легко вычислить. Если игроку нужна подсказка «теплее-холоднее», это старый способ, g_targets.
  • Замеры с точностью хуже 30 метров не считаются вовсе. Позиция по wifi или по адресу приезжает с точностью в сотни метров, и её центр случайно попадает в любую зону — код вбился бы человеку, который никуда не шёл.
  • Прыжки не считаются: замер, по которому игрок переместился быстрее 150 км/ч, отбрасывается.
  • Подделать геолокацию в браузере всё равно можно, и закрыть это нельзя. Смысл в другом: в странице нет цели, и обход зоны требует осознанных действий, а не одного взгляда в исходный код.

Обычный код зона вбивает один раз. Условие о поле в квадратных скобках позволяет вбивать снова — и здесь оно нужнее всего:

{! on gps /* северный двор */ 51.5221 -0.1072 radius 30: code [c1 !=] north !}
{! on gps /* южный двор */    51.5201 -0.1072 radius 30: code [c1 !=] south !}

Две зоны на один сектор — переключатель: в первом секторе стоит код той зоны, в которой команда была последней. Шаблон {% match code_1 %} показывает по нему, где команда сейчас. Зоны переключателя не должны пересекаться: стоя в двух сразу, игрок получит оба кода подряд.

{! on gps /* далеко */  51.5190 -0.1072 radius 30: code [c2 <] 1far !}
{! on gps /* у двери */ 51.5215 -0.1072 radius 30: code [c2 <] 4door !}

Набор зон с < — приоритет: код с бо́льшим числом в начале заменяет меньший, но не наоборот. Так в секторе остаётся лучшая из достигнутых точек.

Подробно про условие о поле — на странице Серверные действия.

Бывает, что по зоне ничего вбивать не нужно — надо только показать игроку что-то, пока он стоит у беседки. Для этого есть действие zone с именем:

{! on gps /* беседка */ 51.5241 -0.1072 radius 30: zone беседка !}

Кода у такой зоны нет. Страница сообщает скрипту задания, что игрок вошёл в зону или вышел из неё, — один раз на смену, а не на каждый замер:

QEng.Gps.onZone = function (name, inside) {
  if (name === 'беседка') $('#gazebo').toggle(inside);
};
  • Имя зоны — всё, что после слова zone; пробелы в нём можно. Приходит оно только тому, кто уже стоит в зоне, — где зона, страница по-прежнему не знает.
  • Когда задание перерисовывается (вбит код, пришла подсказка), движок заново сообщает «вошёл» про зоны, в которых игрок так и стоит: перерисовка стирает то, что скрипт нарисовал в тексте.
  • Зона, которая и вбивает код, и называет себя скрипту, пишется одним маркером: {! on gps 51.5299 -0.1275: code ньютон: zone двор !}.

Маркер работает, пока виден текст, в котором он стоит. Поэтому зона в описании бонуса есть, пока бонус не решён, а зона в подсказке появляется вместе с подсказкой. Так набор зон меняется по ходу уровня без единой строчки скрипта.

Сама зона игру не заканчивает. Это два шага: зона вбивает код, а finish_game стоит в ветке шаблона по этому коду:

{! on gps 51.5299 -0.1275: code финиш !}
{% match code_1 }:{ {%= финиш}:{ {! finish_game !} %} %}

Пример в игре: переключатель, многоугольник, приоритет, зона без кода, зоны в бонусе и в подсказке

У тестера в панели тестирования внизу страницы есть вкладка «GPS» — она появляется на задании, скрипт которого зовёт gps_start().

Вкладка «GPS» панели тестирования: зоны уровня на карте и положение игрока

  • На карте — все зоны уровня, с номерами и подписями из комментариев. Показаны и зоны из веток шаблона, которые сейчас не открыты: так видно, не наехали ли зоны друг на друга. Зоны, которые работают сейчас, отмечены.
  • Своё положение можно подменить: кликнуть по карте, перетащить маркер игрока или вписать широту и долготу и нажать «Применить». Движок сразу проверит зоны по новой точке. Проверка скорости тестеру не мешает — прыгать по карте можно сколько угодно.
  • «Сбросить подмену» возвращает к настоящему датчику, «Показать всё» подгоняет карту под игрока и все зоны.
  • Список зон обновляется вместе с заданием: решили бонус — его зона уходит и с карты.

Что вбили маркеры на этой загрузке страницы, показывает соседняя вкладка «Действия» — см. Серверные действия.

Старый способ, и он работает как работал. Координаты точек лежат в исходном коде страницы, так что для загадки «найдите место» он не годится — игрок откроет исходник и увидит цель. Нужен он, когда браузеру самому надо знать, где цель: показать расстояние, нарисовать точку на своей карте.

Точки складываются в массив g_targets. Каждая точка — объект с координатами, радиусом и действиями:

gps_start();
 
window.target_lat1 = 51.5211367;   // Широта точки
window.target_lon1 = -0.10723867;  // Долгота точки
 
g_targets.push({
  'lat': window.target_lat1,
  'lon': window.target_lon1,
  'r': 30,  // Радиус в МЕТРАХ
  'action_inside': "enter_silent('finish_code_123')",
  'action_outside': "$('#distance_to').text(gps_distance(window.target_lat1, window.target_lon1).toFixed(0))"
});
  • lat, lon — координаты точки.
  • r — радиус срабатывания в метрах.
  • action_inside — строка с javascript, выполняется, когда игрок внутри радиуса.
  • action_outside — строка с javascript, выполняется, когда игрок снаружи радиуса.

Точек может быть сколько угодно — просто добавьте в g_targets несколько объектов.

Действия выполняются при каждом обновлении координат, а не один раз при входе в зону. Пока игрок стоит внутри радиуса, action_inside сработает много раз подряд. Для ввода кода это безопасно: enter() и enter_silent() отправляют код только один раз. А вот показ сообщения или проигрывание звука лучше защитить собственным флагом:
'action_inside': "if (!window.was_inside) { window.was_inside = 1; play_sound('limit'); }"

Функция gps_distance(широта, долгота) возвращает расстояние от текущего положения игрока до указанной точки в метрах. Обычно её показывают игроку, чтобы он понимал, теплее или холоднее:

<p>Расстояние до точки: <span id="distance_to">???</span> м.</p>

Уровень с точкой в скрипте: расстояние до неё считает браузер игрока

Функцию можно вызывать только после того, как получены первые координаты, — до этого положение игрока неизвестно. Проще всего вызывать её из action_outside, как в примере выше: движок сам выполнит её, когда координаты появятся.

Если нужно не просто проверить радиус, а нарисовать карту или свой индикатор, переопределите функцию on_update_position() — движок вызывает её каждый раз, когда пришли новые координаты. Текущее положение лежит в g_position (поля lat, lon, а также accuracy — точность замера в метрах, timestamp, altitude, speed, heading):

gps_start();
 
on_update_position = function () {
  $('#my_coords').text(g_position.lat.toFixed(5) + ', ' + g_position.lon.toFixed(5) + ' (±' + Math.round(g_position.accuracy) + 'м)');
};

Также можно отслеживать статус работы датчика через on_gps_status(status, details):

  • requesting — запрос разрешения у браузера;
  • searching — ожидание захвата спутников GPS;
  • locked — координаты получены и обновляются;
  • timeout — поиск спутников затянулся (движок делает мягкие авто-повторы);
  • denied — игрок запретил доступ к геопозиции;
  • unavailable — геолокация недоступна или отключена.

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

Те же функции есть и под общим именем QEng.Gps: QEng.Gps.start() и stop(), distance(lat, lon), position, targets, addTarget({…}), onUpdatePosition, onStatus. Старые имена работают и будут работать.

Служебная функция init_debug_map(широта, долгота, зум) рисует карту с вашими точками и радиусами и ставит перетаскиваемый маркер игрока. Куда вы перетащите маркер — там движок и будет считать игрока. Функция ждёт на странице контейнер <div id=«debug_map»> и работает на Leaflet + OpenStreetMap, без API-ключей. Для триггеров на сервере она не нужна — у тестера для этого есть вкладка «GPS».

Движок не подключает никаких картографических библиотек — карту автор добавляет сам в текст задания. В примере ниже используется бесплатный Leaflet с тайлами OpenStreetMap: подключаются css и js библиотеки, создаётся контейнер карты, а метка двигается по координатам игрока.

Пример с Leaflet: карта уровня и метка игрока на ней

Учтите, что задание перерисовывается при автообновлении, поэтому состояние карты (центр, зум, объект карты) удобно хранить в window, чтобы она не создавалась заново и не «прыгала» под игроком.

Если по сюжету нужно просто отправить игрока в точку, а не проверять его приход, хватит обычной ссылки. Вставляется в режиме «Источник»:

<a href="geo:?q=59.249,37.465">59.249, 37.465</a>

Схема geo: открывает координаты в навигаторе, установленном на телефоне. Для настольных браузеров, где такой схемы нет, подойдёт ссылка на карты:

<a href="https://www.google.com/maps/search/?api=1&query=49.940333,36.39305" target="_blank">49.940333, 36.39305</a>

Пример триггеров на сервере | Пример точки в скрипте | Пример с картой | Пример ссылок на координаты

  • authors_main/task_editor/advanced/gps.txt
  • Последние изменения: 2026/09/23 08:33
  • (внешнее изменение)