Привет, Хабр! Меня зовут Артур Валиев. В этой статье я расскажу о некоммерческой игре, которую разрабатывает команда моих друзей, и о задачах, с которыми я помогал им в процессе работы.
В основе проекта лежит необычная концепция: постепенно менять доступное игроку пространство — от полноценной 3D-арены до одномерной линии, где главным измерением становится время. Наша рабочая формула выглядит так:
3D − 2D + 1D + 3D(Time)
Разберём, как эта идея повлияла на физику, камеру, визуальный стиль и геймдизайн проекта на Unity.
Claude + Codex + Фантазия + Команда(RUST_C++_C#) + Музыкант + ХАБР)

На первый взгляд это звучит как шутка, которая родилась глубокой ночью в Unity после десятого сломанного коллайдера.
Но постепенно эта формула стала рабочим языком всего проекта.
Я честно вообще плохо понимал зачем? Мы начали с обычной трёхмерной арены. Затем стали думать не о том, как сделать ещё одну красивую комнату с врагами.
Что есть глубина?
Что останется от игры, если схлопнуть мир до одной линии?
И можно ли считать время ещё одним измерением, если большая часть решений игрока строится не на позиции, а на моменте действия?
Так у нас появилась игра про простые фигуры, скорость, физику, волны противников и постепенно разрушающуюся геометрию мира.
Проект не коммерческий, и на стадии BETA, мы просто делимся опытом. Название тоже BETA.

Почему игра чёрно-белая
Честный ответ: потому что мы не дизайнеры.
Но в какой-то момент это перестало быть оправданием и стало ограничением, вокруг которого сформировался весь стиль.
Когда у тебя нет возможности побеждать дорогими текстурами, сложными материалами, детализированными моделями и десятками оттенков, приходится искать выразительность в другом:
в силуэте;
в контрасте;
в скорости;
в ритме;
в направлении;
в поведении объектов.
Чёрный фон, белая линия, простая фигура, удар, вспышка, след.
Из этого постепенно появился не стиль «дешёвой графики», а язык технического чертежа.
Мир не выглядит нарисованным. Он выглядит так, будто его пытаются доказать.
У каждой формы есть своя роль:
круг = скорость треугольник = направление и атака квадрат = масса и защита линия = закон пространства формула = след мысли

Мы почти полностью отказались от цвета. Благодаря этому любое отклонение становится событием. Если весь мир белый на чёрном фоне, красный импульс сразу воспринимается как опасность. В мире тонких линий внезапное утолщение считывается как угроза. Если пространство обычно непрерывно, разрыв становится не украшением, а механикой. Ограниченная палитра заставляет каждый визуальный элемент что-то означать.
3D как физическое тело
Первая часть игры существует в полноценном трёхмерном пространстве.
Игрок здесь не просто иконка или точка управления. Это физическое тело.
Он:
катится;
прыгает;
сталкивается;
получает импульс;
меняет направление;
накапливает скорость;
передаёт часть движения своим атакам.
Именно скорость стала одной из главных тем проекта.
На раннем этапе мы заметили странную вещь: игрок мог разогнаться настолько сильно, что выпущенный им снаряд визуально казался медленным.
Технически всё работало правильно. У снаряда была постоянная скорость. Но ощущение было неправильным.
Если тело движется вперёд и выпускает снаряд в том же направлении, кажется естественным, что часть скорости должна передаваться выстрелу.
Упрощённо логика выглядит так:
float inheritedSpeed = Vector3.Dot(playerVelocity, shotDirection); float projectileSpeed = baseProjectileSpeed + Mathf.Max(0f, inheritedSpeed) * 1.4f;
Мы берём проекцию скорости игрока на направление выстрела. Если игрок движется вперёд, снаряд получает дополнительную скорость. Если он летит в противоположную сторону, отрицательное значение не используется, чтобы выстрел не становился абсурдно медленным. Эта небольшая механика, но она хорошо показывает наш основной подход. Мы стараемся принимать решения не только на основании баланса, но и на основании внутренней честности мира. Если игрок разогнался, это должно что-то значить. Если он накопил импульс, этот импульс не должен исчезать в момент нажатия кнопки атаки.
Когда физика важнее красивого эффекта

Тот же принцип появился во время работы над одной из фаз босса.
По задумке при потере половины здоровья босс должен был превращать арену в гравитационный колодец.

Первый очевидный вариант — нарисовать вокруг него красивые искривлённые линии, добавить частицы и искусственно тянуть игрока к центру.
Визуально это могло бы выглядеть убедительно.
Но пол при этом оставался бы плоским.
Игрок видел бы воронку, однако продолжал бы стоять на горизонтальной поверхности. Его движение к центру создавалось бы не наклоном, а скрытой силой.
Это противоречило самой идее.
Если арена становится воронкой, она должна действительно стать воронкой.
Поэтому плоский пол пришлось заменить процедурным диском с достаточно плотной сеткой вершин. Во время фазы босса вершины начинают опускаться по высоте в зависимости от расстояния до центра.
Упрощённо профиль можно представить так:
float normalizedDistance = distanceToCenter / arenaRadius; float falloff = deformationCurve.Evaluate(normalizedDistance); vertex.y = baseHeight - currentDepth * falloff;
Глубина постепенно увеличивается, а форма контролируется через AnimationCurve.
После изменения геометрии приходится обновлять:
mesh.vertices = deformedVertices; mesh.RecalculateNormals(); mesh.RecalculateBounds(); meshCollider.sharedMesh = null; meshCollider.sharedMesh = mesh;
Это дороже, чем просто запустить шейдер.
Зато игрок действительно находится на наклонной поверхности и может физически скатываться вниз.
Для нас это важный принцип:
Если механика говорит, что мир изменился, изменение должно происходить в самом мире, а не только в его изображении.
2D как способ мышления
Хотя первая часть игры технически трёхмерная, визуально мы постоянно пытаемся превратить её в чертёж.
Речь не о том, чтобы сделать обычную плоскую игру.
Речь о способе представления информации.
Линиями можно показать:
направление атаки;
траекторию;
заряд;
орбиту;
радиус угрозы;
границу арены;
волну;
разрыв;
связь между объектами.
В какой-то момент LineRenderer перестал быть для нас просто компонентом Unity и превратился почти в карандаш.
lineRenderer.SetPosition(0, origin); lineRenderer.SetPosition( 1, origin + direction * length ); lineRenderer.widthMultiplier = Mathf.Lerp(0.02f, 0.12f, charge);
Одна линия может быть слабым следом.
При увеличении заряда она становится толще.
Затем начинает дрожать.
После выстрела превращается в луч.
Тем же инструментом можно нарисовать орбиту, молнию, границу схлопывания пространства или остаточный след движения.
Вместо того чтобы добавлять отдельный эффект под каждую механику, мы пытаемся развивать единый визуальный язык.
1D как эксперимент над геймплеем
Третья миссия появилась из простого вопроса:
Что останется от игры, если отнять почти всё пространство?
Не уровень без прыжка.
Не обычный тир с видом сбоку.
Не платформер, в котором забыли добавить высоту.
А мир, в котором осталась только одна координата.
x существует y недоступна z недоступна решение находится во времени
В одномерном мире нельзя обойти врага.
Нельзя зайти сбоку.
Нельзя перепрыгнуть препятствие.
Нельзя спрятаться за объектом.
Любая угроза полностью перекрывает всё доступное пространство.
Это сильно меняет смысл обычных действий.
В трёхмерной игре игрок часто решает проблему положением.
В одномерной — моментом.
Основные действия становятся предельно чистыми:
отойти затормозить выстрелить дождаться окна пройти рывком
Именно здесь время окончательно становится частью пространства. Честно, тут стыдно показывать результат, то что мы хотели не получается. Мы решили что единственное что может пользователь это 2 стрелки <- vs -> но, для пользователей это ад. Над этим уровнем пока работают ребята. Все таки от первого впечатления многое зависит.
Почему 1D оказалось сложнее, чем звучало
На бумаге идея кажется простой. Есть линия. Игрок движется влево и вправо. Враги находятся на той же линии. Но очень быстро возникает проблема: если любое препятствие полностью перекрывает путь, как вообще строить полноценный уровень? Нельзя просто расставить платформы. Нельзя сделать обходной маршрут. Нельзя спрятать секрет за углом. Даже один обычный враг фактически становится дверью. Поэтому мы пришли к тому, что одномерная миссия должна строиться не вокруг исследования пространства, а вокруг последовательности боевых и временных ситуаций. Игрок движется вправо. Навстречу летит импульс. Он может уничтожить его выстрелом или пройти насквозь рывком. Дальше появляется барьер. Его нельзя обойти или проскочить. Нужно разрушить. Пока игрок стреляет по барьеру, к нему приближается следующая атака. Он вынужден отступить, дождаться окна, снова приблизиться и продолжить разрушение. Так даже простая стена превращается в маленькую боевую сцену. Основной цикл начинает выглядеть так:
Игрок видит угрозу.
Оценивает расстояние и скорость.
Решает, продолжать движение или отступить.
Выбирает между выстрелом и рывком.
Проходит участок.
Получает короткую передышку.
Следующая ситуация объединяет уже знакомые правила.
В одномерной миссии практически невозможно спрятать плохой дизайн за декорациями.
Если движение неинтересное, игрок заметит это сразу.
Если атака не читается, избежать её невозможно.
Если враг появляется слишком близко, ситуация становится нечестной.
Если рывок не ощущается хорошо, вся миссия разваливается.
Камера в мире, где есть только линия
Отдельной проблемой стала камера.
Если всегда держать игрока строго по центру, он одинаково хорошо видит пространство впереди и позади, но плохо чувствует направление движения. А то слишком сильно сместить его к краю, встречная атака может появиться слишком поздно. После мы остановились на камере, которая смотрит на линию строго сбоку и показывает больше пространства в направлении движения. А когда двигаем камеру, при движении вправо игрок находится примерно в левой трети экрана. Если попробовать развернуть камеру она не перескакивает мгновенно. Она плавно перестраивается, перенося свободную область на другую сторону. Кошмар. А тут еще множь скорость которая тоже влияет на обзор.
В Целом чем быстрее движется игрок, тем дальше камера смотрит вперёд.
Это не просто визуальное удобство. В 1D обзор фактически становится частью баланса.
Игрок не может уклониться вверх или вниз, поэтому должен заранее видеть угрозу и успевать принять решение
Честно я не знал как нормально это объяснить, пока не вынес коллеги все мозги.
Камера в мире, где есть только линия
Тут мы мы уже узнаем подробности об отдельной проблемы камеры.
В обычной 3D-игре камера чаще всего отвечает за комфорт: показать игрока, не залезть в стену, не потерять цель, не мешать управлению. Но в 1D-мире камера внезапно становится частью правил.
Потому что в 1D у игрока нет бокового маневра.
Он не может обойти угрозу сверху. Не может уйти вниз. Не может зайти сбоку. Если что-то летит по линии, оно перекрывает весь мир. Поэтому единственная честная защита игрока — заранее увидеть угрозу и успеть принять решение:
выстрелить отойти затормозить пройти рывком принять удар
И тут выясняется, что позиция игрока на экране — это уже баланс.
Если держать игрока четко в центре (в камере в смысле), он одинаково видит пространство впереди и позади. Формально это справедливо. Но ощущается плохо: направление движения теряет смысл. Игрок бежит вправо, но экран не говорит ему: “вот твое будущее”. Он видит слишком много прошлого.
Если слишком сильно сместить игрока к левому краю, движение вправо начинает ощущаться лучше: впереди много пространства, угрозы читаются заранее. Но появляется другая проблема: если игрок разворачивается, камера не успевает перестроиться, и атака с новой стороны может появиться слишком поздно.
В итоге камера для 1D должна делать нечто среднее:
смотреть строго сбоку держать линию читаемой показывать больше пространства впереди не телепортироваться при развороте учитывать скорость игрока
То есть камера должна не просто следовать за координатой x, а предсказывать намерение игрока.
Упрощенно это можно описать так:
float direction = Mathf.Sign(playerVelocity); float speed01 = Mathf.InverseLerp(0f, maxSpeed, Mathf.Abs(playerVelocity)); float lookAhead = Mathf.Lerp(baseLookAhead, maxLookAhead, speed01); float targetX = playerX + direction * lookAhead; cameraX = Mathf.Lerp(cameraX, targetX, Time.deltaTime * followSpeed);
Идея простая: чем быстрее игрок движется, тем дальше камера смотрит вперед.
Но важна деталь: направление нельзя переключать мгновенно. Иначе при каждом развороте камера будет дергаться, а игрок потеряет ориентацию. Поэтому направление камеры должно иметь инерцию:
lookDirection = Mathf.Lerp( lookDirection, inputDirection, Time.deltaTime * turnSmooth );
Так появляется странный эффект: камера начинает жить почти как физическое тело. Она не просто показывает линию. Она распределяет внимание игрока во времени.
Если игрок медленно движется, камера держится ближе. Если он разогнался, камера отдает ему больше будущего. Если он резко разворачивается, камера не прыгает, а постепенно переносит свободную область на другую сторону.
В обычном уровне это было бы просто удобством. В 1D это становится механикой.
Потому что обзор в 1D — это не вопрос красоты. Это вопрос справедливости.
Если игрок умер от угрозы, которую физически не мог увидеть заранее, это плохая смерть. Если он видел угрозу, но не успел среагировать, это уже игровой выбор. Камера здесь проводит границу между “меня убили нечестно” и “я ошибся”.
Самое смешное, что я до конца не уверен, что мы “решили” этот уровень. Скорее мы попали в странное место, где привычные вещи перестали работать. Камера, линия, скорость, время реакции — все смешалось в один узел.
Честно, пока сыро, линию показать и ее геймплей только используя 2 направления это пока сложно. НО, на бумаге прикольно.
В 3D можно спрятать ошибку камеры за пространством. Игрок все равно может прыгнуть, отвернуть, обойти, импровизировать.
Время как настоящее измерение
Когда мы говорим 3D(Time), мы не имеем в виду буквальное четвёртое пространственное измерение.
Мы имеем в виду игру, в которой объект определяется не только координатой, но и своим состоянием во времени.
Например:
через 15 секунд начнётся новая волна;
рывок восстановится через 2 секунды;
супервыстрел заряжается 3 секунды;
бессмертие действует 10 секунд;
после атаки босс открывается на короткий момент;
арена начинает схлопываться после определённой фазы;
снаряд либо догонит игрока, либо не догонит его в зависимости от скорости;
погибший участник команды вернётся, если кто-то сможет продержаться достаточно долго.
Технически многие из этих механик начинаются с обычных таймеров.
if (isHoldingFire) { chargeTime += Time.deltaTime; } if (chargeTime >= requiredChargeTime) { FireBeam(); }
Но для игрока это не просто увеличение числа. Есть какое-то напряжение. Сделали вокруг персонажа символы (символы-уравнения). Линии становятся толще. Звук нарастает. Игрок ещё не выстрелил, но событие уже происходит. Действие существует не только в момент результата. Оно занимает участок времени.
И на этом участке игрок остаётся уязвимым.
Почему в игре появляются уравнения


Уравнение для нас - это особая форма визуальной магии. Оно выглядит так, будто за происходящим скрывается строгая логика, даже если игрок не знает и не обязан знать всех правил этой системы. Именно поэтому мы не хотели использовать привычный язык фэнтези: синие огоньки, руны, кристаллы и абстрактные заклинания. У нашей игры другая фантазия. Игрок не колдует в привычном смысле — он вмешивается в устройство мира, словно меняет отдельную строку в его формуле.
Поэтому лечение может быть представлено не сердечком, а выражением HP + 25 = const, ускорение — формулой v = v₀ + at, супервыстрел — записью lim power → ∞, а критический удар — чем-то вроде P(hit) < ε. Мы не пытаемся сделать научный симулятор и не утверждаем, что каждая формула физически точна. Для нас это визуальные метафоры, пасхалки и часть общего языка игры.
Благодаря этому интерфейс перестаёт быть наклейкой поверх мира. Число урона, символ, линия заряда и траектория снаряда принадлежат одной системе. Они не просто сообщают информацию игроку, а выглядят как следы процессов, которые происходят внутри самой реальности игры.
Когда формул становится слишком много
У этого подхода быстро обнаружилась и обратная сторона. Если вокруг каждого объекта постоянно летают символы, пределы, интегралы и координаты, изображение превращается в шум. Формула перестаёт восприниматься как событие и становится обычным декоративным мусором.
Поэтому математические обозначения должны появляться дозированно, только в моменты, когда действительно меняется закон мира. Например, во время зарядки сильной атаки, деформации пространства, резкого ускорения, потери измерения или активации необычного бонуса. Формула должна означать, что происходит что-то фундаментальное, а не просто заполнять пустое место на экране.
Если формулы присутствуют везде, они теряют значение. Если всё является формулой, формулой не является ничего.
Зачем игре мультиплеер
Мультиплеер появился в проекте не потому, что кооператив автоматически увеличивает охват. Он нужен потому, что наши механики становятся интереснее, когда рядом находится другой игрок.
Один участник может убегать от волны противников, второй — удерживать босса, третий — пытаться добраться до бонуса. Кто-то погибает, но раунд при этом не заканчивается. Пока жив хотя бы один игрок, остальные сохраняют шанс вернуться. В упрощённом виде правило звучит так: если в команде остался хотя бы один живой участник, погибшие ждут возвращения; если не осталось никого, команда проигрывает.
Это полностью меняет смысл смерти. В одиночной игре смерть часто означает конец попытки. В кооперативе она становится временным состоянием команды. Погибший игрок уже не просто смотрит на экран поражения, а наблюдает, как последний живой участник пытается продержаться ещё несколько секунд.
Из-за этого возникает совсем другая эмоция. Не «я проиграл», а «продержись, я скоро вернусь». Само время возрождения превращается из технического таймера в драматический ресурс. От нескольких секунд зависит, продолжится ли раунд вообще.
Командное выживание как система ролей
При этом мы не хотим жёстко назначать игрокам классы. В игре нет обязательного танка, медика или стрелка. Роли появляются из конкретной ситуации.
Самый быстрый игрок может собирать бонусы. Самый тяжёлый — удерживать опасную область. Тот, у кого заряжена сильная атака, готовит удар по боссу. Игрок с небольшим запасом здоровья отступает и пытается сохранить раунд. Через несколько секунд ситуация меняется, и вместе с ней меняются роли.
Это хорошо сочетается с геометрической основой игры. Форма объекта определяет его склонность к определённому поведению, но не фиксирует функцию игрока навсегда. Круг может быть быстрым, квадрат — тяжёлым, треугольник — агрессивным, но настоящая роль возникает только в конкретный момент.
Что мы на самом деле пытаемся сделать
Мы не пытаемся создать реалистичный шутер, построить красивую фэнтезийную арену или спрятать простые фигуры под дорогими визуальными эффектами. Нам интереснее сделать игру, в которой форма действительно имеет смысл.
Круг быстрый, потому что он катится. Треугольник опасен, потому что у него есть выраженное направление. Квадрат тяжёлый, потому что он сопротивляется движению. Линия страшная, потому что в одном измерении её невозможно обойти. Время важно, потому что почти вся игра строится вокруг простого различия: успел игрок или не успел.
Для нас это проект о честности ощущений. Если игрок разогнался, его выстрел должен унаследовать часть этой скорости. Если босс стал чёрной дырой, арена должна физически изгибаться, а не только изображать это красивым шейдером. Если мир схлопнулся до линии, игрок должен действительно потерять возможность обходить угрозы. Если команда выживает только благодаря одному оставшемуся участнику, эти несколько секунд должны ощущаться важнее всего предыдущего раунда.
Простые фигуры невозможно обмануть декором
Работа с минималистичной графикой оказалась одновременно свободой и наказанием. Мы можем быстро создавать новые объекты, не тратя месяцы на моделирование каждого противника. Но простые фигуры очень плохо скрывают слабые решения.
Круг либо читается, либо нет. Линия либо помогает понять механику, либо мешает. Удар либо ощущается, либо остаётся обычной вспышкой. Враг либо создаёт интересную ситуацию, либо является точкой с полоской здоровья.
В более детализированной игре часть проблем можно временно скрыть за красивым окружением, анимацией или эффектами. В чёрно-белом мире спрятаться почти негде. И это относится не только к игроку, но и к разработчику.
Минимализм заставляет отвечать на самые простые и самые неприятные вопросы. Приятно ли двигаться? Понятно ли направление атаки? Чувствуется ли масса? Честно ли работает опасность? Есть ли у формы смысл? Если ответ отрицательный, декор уже не спасёт.
Вместо заключения
Мы делаем игру из простых фигур, потому что хотим понять, сколько смысла можно получить из формы, движения и времени. Пока наша рабочая формула выглядит так: меньше цвета, меньше визуального шума, больше формы, больше скорости, больше времени и больше смысла.
В более коротком виде это записывается так:
3D − 2D + 1D + 3D(Time)
Эта формула не является математически строгой. Она не объясняет архитектуру проекта и вряд ли поможет исправить очередной сломанный MeshCollider. Но она честно описывает направление, в котором мы движемся.
Мы постепенно отнимаем у игрока пространство, пока не становится видно, что главным измерением игры всегда было время.
Спасибо что дочитали. С Вами был Ar2r, всем спасибо! До скорых встреч!
aywan
Когда читал про вторую фазу босса думал о том что появляется черная дыра, потом должна быть фаза на поверхности черной дыры, потом должна быть фаза спагетизации на линии, ну и потом уже фаза сингулярности - бой в 0d))