Привет, Хабр. Я делаю AR-хоррор для iPhone, называется Home Haunt. Сюжет — «Вий» Гоголя: бурсак Хома Брут три ночи читает над мёртвой панночкой в старой деревянной церкви. Только церковь вырастает не где-то на экране, а у вас в комнате. Ваши стены, ваш пол, ваш диван.
Игра сейчас в закрытом тесте в TestFlight. В статье — что получилось технически, где споткнулись и какие цифры намерили. Всё, что на гифках — реальная запись экрана iPhone, без вставок.
Стек
Unity 6 (6000.5), AR Foundation поверх ARKit, только iPhone.
Сборки из командной строки (
-batchmode -executeMethod), выгрузка в TestFlight скриптом.Кооператив — своё реле: сначала C# в Docker на маке, теперь Cloudflare Workers + Durable Objects.
Озвучка — локальные TTS (Kokoro для английского, Qwen для русского) + Gemini TTS на ключевые реплики, каждый дубль проверяем whisper'ом.
1. Скан комнаты — это часть игры, а не загрузка
Всё дальше опирается на настоящие плоскости: пол и стены. Поэтому сначала скан.

Как считаем готовность:
осмотр — 60 % прогресса (12 секторов по кругу, в каждый смотреть ~0,35 с);
найден пол — 25 %;
две стены (стена/дверь/окно считаются вместе) — 15 %.
Готово, когда прогресс ≥ 0,98 и осмотрены все 12 секторов. Если за 20 секунд ARKit не нашёл пол или две стены, или стена ближе 1,4 м — включается запасной режим, игра не застревает.
Что поняли: голый прогресс-бар убивает настроение. Сейчас Хома во время скана бормочет («сначала посмотри, где дверь, потом молись»), и те же 10 секунд ощущаются как осмотр чужого места, а не как загрузка.
2. Вещи на ваших стенах и переноска пальцем
После скана в комнате вырастает стол (1,0 × 0,6 м, высота 0,75 м) — рядом с вашим настоящим столом или в 1,2 м перед вами. А вещи Хомы появляются на ваших стенах: шапка висит на гвоздике на высоте 1,0–1,6 м, внутри найденной плоскости стены, не ближе 15 см к её краю.

Сначала вещи брались тапом. Это было скучно — AR превращается в меню. Переделали на переноску:
зажать вещь пальцем ≥ 0,25 с, и только если до неё ≤ 2 м (иначе «Подойди ближе»);
вещь висит перед камерой с весом (шапка лёгкая, люлька тяжёлая — качается по разному), телефон вибрирует;
несёшь к столу и отпускаешь в пределах 0,6 м от места — иначе падает на пол.
Так начинаешь ходить по квартире. А когда ходишь, пугаться своего коридора намного проще.
Под шапкой нацарапано «I · V · VII» — это номера свечей на полу. Свечу зажигаешь огарком: надо держать пламя над фитилём почти неподвижно.

Цифры, на которых остановились: пламя не дальше 0,14 м по горизонтали от фитиля, телефон движется медленнее 0,25 м/с, держать 0,3 с. Сначала было 0,25 м — и хватало просто провести огарком низко вдоль ряда, загорались все подряд.
3. Кровь по настоящим обоям
Когда все вещи на столе, комната «уходит»: темнеет, по стенам течёт кровь, из угла лезет нечисть и шепчет «Не смотри на стены, философ».

Для меня это самый сильный момент, хотя моделей тут меньше всего. Свои стены человек знает. Когда меняются они — работает лучше любой модели.
4. Тишина перед страхом
Первая сцена — дом Архивариуса в Киеве — вообще без пугалок. Свеча, письмо сотника, чёрная книга, Хома ворчит, что ехать не хочет. Механику туда я всё-таки добавлю, но тоже без пугалок. Правило такое: сначала история и тишина, потом кровь.
Дальше по сюжету изба старухи, казаки с загадками и рассказами, книга.


5. Страницы, которые смеются
В сцене «Страницы» вы ищете листы по комнате и раскладываете сюжет по порядку. Каждый найденный лист смеётся. Этот смех — мой, записал сам.

Разложил неправильно — шёпот «Не этот… тот, что раньше».
6. Гроб и ночь

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


7. Кооператив: два телефона, две квартиры
Идея: один играет Хому в церкви, другой — Юрко в «избе духов», каждый у себя дома. Ответ на загадку есть только у двоих вместе: у Юрко картина, у Хомы замок.
Сначала реле было на C# в Docker у меня на маке, наружу — через Cloudflare quick tunnel и Worker на relay.artuplabs.com. Работало, медиана доставки игрок → игрок 49–62 мс. Минус очевидный: мак должен быть включён.
Перенёс на Cloudflare Durable Objects (бесплатный план). Протокол и клиент не менял, адрес тот же. Что проверили:
кадры совпадают с C#-реле байт в байт — 122 из 122;
e2e 11 из 11, три раза подряд;
сброс объекта посреди сессии: клиенты вернулись за 3,7–3,8 с, без потерь.
Цена — задержка. Объект живёт в Варшаве, пинг 93–111 мс против 48 через мак. Доставка в smoke-тесте — 145 мс при нашем пороге 150. По LTE, скорее всего, будет выходить за порог, проверим на живых телефонах.
Что поняли на первом плейтесте вдвоём: пошаговый кооп не работает. Один делает — второй ждёт, и ждёт долго. Переделываем так, чтобы оба всегда были заняты, а промах одного мешал другому.
8. Озвучка
Реплик много, поэтому конвейер:
английский — Kokoro, русский — Qwen, локально;
ключевые реплики — Gemini TTS (бесплатная квота ~50 озвучек в день, поэтому порциями);
каждый дубль прогоняем whisper'ом и сверяем с текстом — так ловятся проглоченные слова;
ударения в русском правлю руками на своей странице: кликаешь гласную — она становится ударной («хомА»), правило можно отправить в словарь.
Отдельный трюк для Юрко из «Твин Пикса»: шум задом наперёд под репликой. Слова в итоге оставили обычными, а шум — перевёрнутым.
9. Бонус: почему трейлер дёргался
Монтировал трейлер из записей экрана и получил дёрганье. Оказалось, игра пишет кадры неравномерно — то 60, то 30–36 в секунду, с пропусками. При переводе в 30 fps на одном отрезке выпадало 63 кадра из 297, на другом 102 из 349. Решение — выводить в 60 fps без пропусков, а трясущиеся AR-куски стабилизировать с небольшим зумом.
Что дальше
кооператив без ожиданий;
головоломки вместо простых тапов (гроб, дом Архивариуса);
русская озвучка моим голосом для части героев.
Видео и новости игры: YouTube · TikTok · Instagram (@haunt_link)
Сайт: https://paledoorgames.artuplabs.com
Хотите в тест на iPhone — пишите на hello@artuplabs.com.
Буду рад вопросам, особенно про AR в маленьких комнатах — это была самая неприятная часть.
