{{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(широта, довгота, зум)'' малює карту з вашими точками та радіусами і ставить перетягуваний маркер гравця. Куди ви перетягнете маркер — там движок і вважатиме гравця. Функція чекає на сторінці контейнер ''
'' і працює на **Leaflet + OpenStreetMap**, без API-ключів. Для тригерів на сервері вона не потрібна — у тестувальника для цього є вкладка «GPS». ===== Карта на рівні ===== Движок не підключає жодних картографічних бібліотек — карту автор додає сам у текст завдання. У прикладі нижче використовується безкоштовний Leaflet із тайлами OpenStreetMap: підключаються css та js бібліотеки, створюється контейнер карти, а мітка рухається за координатами гравця. {{ :authors_main:task_editor:advanced:gps_map_uk.png?800 |Приклад із Leaflet: мапа рівня і мітка гравця на ній}} Врахуйте, що завдання перемальовується при автооновленні, тому стан карти (центр, зум, об'єкт карти) зручно зберігати у ''window'', щоб вона не створювалася заново і не «стрибала» під гравцем. ===== Посилання на координати ===== Якщо за сюжетом потрібно просто відправити гравця в точку, а не перевіряти його прихід, вистачить звичайного посилання. Вставляється в режимі **«Джерело»**: 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|Приклад посилань на координати]]