{{indexmenu_n>33}}
====== Коди за GPS-координатами ======
Движок уміє сам вбивати код, коли гравець дійшов до потрібної точки на місцевості. Зробити це можна двома способами:
* **Тригер на сервері** — зона пишеться маркером ''{! on gps ... !}'' у тексті завдання. Координат цілі немає в сторінці гравця ніде: ні в тексті, ні в скрипті, ні у відповідях сервера. **Це основний спосіб.**
* **Точки в скрипті** — масив ''g_targets'' у «Скрипті завдання». Перевірка йде в браузері, тому координати цілі лежать у вихідному коді сторінки. Спосіб старий, але потрібен, коли браузеру самому треба знати, де ціль: показати відстань, намалювати точку на карті.
> **Важливо:** геолокація вмикається лише з дозволу гравця. Якщо гравець відмовить, жодна зона не спрацює, тому залишайте запасний спосіб пройти рівень (звичайний код, підказка зі зливом) або заздалегідь попередьте команди, що GPS обов'язковий.
> **Трек команди.** Поки стеження увімкнене, рушій записує шлях команди — по пристрою, сегментами «стояли» і «йшли», плюс спрацьовування зон і відкинуті заміри. На сторінці гри в автора є посилання «Треки GPS»: там звіт з ознаками підміни геолокації по командах (вердикт — не вирок, у кожної ознаки є чесні пояснення), спірні моменти на кшталт «дійшли до зони, але код не введено» і карта. Трек зберігається 90 днів від останнього запису; видалити раніше можна там само.
===== Увімкнути стеження: gps_start() =====
Обидва способи починаються однаково — з виклику ''gps_start()'' у полі **«Скрипт завдання»**:
Відстань до точки: ??? м.
gps_start();
Без нього браузер не запитає координат, і жодна зона не спрацює. Якщо в завданні є тригери, а ''gps_start()'' ніде немає, редактор попередить про це під час збереження.
Бібліотеку геолокації движок підключає лише завданням, яким вона потрібна, — окремо вмикати нічого не треба.
===== Значок GPS у шапці завдання =====
У завдання з GPS поруч із назвою стоїть значок-супутник. Він показує гравцеві, чи працює геолокація, і сам же її вмикає: на телефоні браузер дає координати лише після натискання, тому значок стоїть від самого початку, ще до першого заміру.
{{ :authors_main:task_editor:advanced:gps_indicator_uk.png |Значок 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 ньютон !}''. Гравець його не побачить, а тестувальник побачить на карті.
Загальні правила маркера — де він спрацьовує, що перевіряє збереження, як пишуться кілька дій — на сторінці [[uk:authors_main:task_editor:advanced:server_actions|Серверні дії]].
==== Зона довільної форми ====
Двір, сквер, квартал — зона-багатокутник. Після слова ''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 !}
Набір зон із ''<'' — **пріоритет**: код із більшим числом на початку замінює менший, але не навпаки. Так у секторі залишається найкраща з досягнутих точок.
Докладно про умову про поле — на сторінці [[uk:authors_main:task_editor:advanced:server_actions|Серверні дії]].
==== Зона без коду: zone ====
Буває, що за зоною нічого вбивати не треба — треба лише показати гравцеві щось, поки він стоїть біля альтанки. Для цього є дія ''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 !} %} %}
[[https://qeng.org/game.php?jump_to&gid=3493&task_id=83702|Приклад у грі: перемикач, багатокутник, пріоритет, зона без коду, зони в бонусі й у підказці]]
===== Перевірити, не виходячи з дому: вкладка «GPS» =====
У тестувальника в панелі тестування внизу сторінки є вкладка **«GPS»** — вона з'являється на завданні, скрипт якого викликає ''gps_start()''.
{{ :authors_main:task_editor:advanced:gps_tester_tab_uk.png?800 |Вкладка «GPS» панелі тестування: зони рівня на карті й положення гравця}}
* На карті — усі зони рівня, з номерами й підписами з коментарів. Показано й зони з гілок шаблону, які зараз не відкриті: так видно, чи не наїхали зони одна на одну. Зони, що працюють зараз, позначено.
* **Своє положення можна підмінити**: клацнути по карті, перетягнути маркер гравця або вписати широту й довготу й натиснути «Застосувати». Движок одразу перевірить зони за новою точкою. Перевірка швидкості тестувальникові не заважає — стрибати по карті можна скільки завгодно.
* **«Скинути підміну»** повертає до справжнього датчика, **«Показати все»** підганяє карту під гравця й усі зони.
* Список зон оновлюється разом із завданням: розв'язали бонус — його зона йде і з карти.
Що вбили маркери на цьому завантаженні сторінки, показує сусідня вкладка «Дії» — див. [[uk:authors_main:task_editor:advanced:server_actions|Серверні дії]].
===== Точки в скрипті: g_targets =====
Старий спосіб, і він працює як працював. **Координати точок лежать у вихідному коді сторінки**, тож для загадки «знайдіть місце» він не годиться — гравець відкриє вихідний код і побачить ціль. Потрібен він, коли браузеру самому треба знати, де ціль: показати відстань, намалювати точку на своїй карті.
Точки складаються в масив ''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(широта, довгота)'' повертає відстань від поточного положення гравця до вказаної точки **в метрах**. Зазвичай її показують гравцеві, щоб він розумів, тепліше чи холодніше:
{{ :authors_main:task_editor:advanced:gps_distance_uk.png?800 |Рівень із точкою в скрипті: відстань до неї рахує браузер гравця}}
Функцію можна викликати лише після того, як отримано перші координати, — до цього положення гравця невідоме. Найпростіше викликати її з ''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(широта, довгота, зум)'' малює карту з вашими точками та радіусами і ставить перетягуваний маркер гравця. Куди ви перетягнете маркер — там движок і вважатиме гравця. Функція чекає на сторінці контейнер ''
59.249, 37.465
Схема ''geo:'' відкриває координати в навігаторі, встановленому на телефоні. Для настільних браузерів, де такої схеми немає, підійде посилання на карти:
49.940333, 36.39305
[[https://qeng.org/game.php?jump_to&gid=3493&task_id=83702|Приклад тригерів на сервері]] | [[https://qeng.org/game.php?jump_to&gid=3493&task_id=76365|Приклад точки в скрипті]] | [[https://qeng.org/game.php?jump_to&gid=3493&task_id=80987|Приклад з картою]] | [[https://qeng.org/game.php?jump_to&gid=3493&task_id=54060|Приклад посилань на координати]]