Unity 3D: о чем давно было пора рассказать нашим подросткам
Прочитал как-то на Хабре заметку «Что делать с детьми летом, если ты айтишник?» Ответил на нее двумя своими заметками (раз и два). А теперь предлагаю взглянуть на поднятую тему немного с другой стороны: с точки зрения «борьбы с цифровым неравенством»? С тем самым, которое проявляется в ограниченном доступе к информации. По мнению некоторых отечественных экспертов, для ее преодоления, якобы, достаточно всего лишь »100% обеспечение образовательных учреждений, учреждений здравоохранения, органов государственной власти и местного самоуправления современными услугами цифровой связи»… Про борьбу с «цифровым неравенством» учреждений здравоохранения и органов госвласти промолчу… А вот про образование процитирую:»Хошь, как хошь, а маловато будет! » А что, по‑вашему, уважаемые эксперты, получат «преодолев цифровое неравенство» наши «рядовые» школьники? Складывается стойкое убеждение, что единственное, что они получат — это игромания. Где те ресурсы, которые помогут расширить их знания, разовьют полезные навыки? Где (адаптированные на соответствующий возраст) методические материалы, которые сделают доступными нашим детям хотя бы уже имеющиеся в сети Интернет полезные ресурсы, созданные в других странах?
Как минимум 32 развитые страны с 2012–14 годов обучают программированию с первых классов обычных общеобразовательных школ. Почему правительство дарит (выдавая абсолютно безвозмездно) каждому первоклашке специальный «школьный контроллер» не в России, где появился мем »Программирование — вторая грамотность», а в Англии? Почему их, а не наши российские дети, исследуя в школе и дома подаренный им контроллер, уже к 6-му классу успешно осваивают два «взрослых» языка программирования? Ау, академики российской академии образования? Я не про ваш ФГОС, не про парадигму образования и не про куррикулум… Объясните, пожалуйста, толком: может все эти 32 страны идут не тем путем?
Мало того, что мы полностью «пролетели» с «импортозамещением» детского и подросткового контента в сети Интернет, но и достойных книг IT‑тематики на возраст 7–14 лет в России также практически нет. Купил было внуку толстый (600+ страниц) двухтомник по программированию игр, который (российский) автор рекламирует на возраст 9+. Купил и радовался! Но 11-летний внук, который «напилил» к этому времени более 120 программ в Scratch и несколько неплохих игр в Roblox, книжку не оценил.
Решил глянуть сам: в чем же дело? Оказалось — в обыкновенном обмане! Книжка, как минимум, на 14+! Оцените, как и о чем пишет этот автор (см. стр. 106):»Наверняка в школе ты уже проходил возведение в степень, квадратный корень, модуль числа, тригонометрические функции, число Пи и так далее». Или вот еще (см. стр. 112):»А знаешь, что такое матрица? Представь таблицу Пифагора. По сути она является матрицей или двухмерным массивом». А что, такие объяснения — это точно на возраст 9+? Может быть, автору было бы лучше погодить с матрицами? Но вместо этого автор не стал объяснять читателям, что такое «сервер» и что такое «клиент»… Поэтому стрелковое и лазерное оружие, которое можно сделать по его «рецептам», стреляет только на мониторе того, кто этот «рецепт» применил, другие игроки об этом и не подозревают… И перечень «плюх» этого двухтомника можно продолжить…
Что называется, достало! Согласитесь, не странно ли, когда мировое сообщество (восторженно и настороженно) исследует возможности Chat GPT, подавляющее большинство наших подростков (в отличие от подростков тех самых 32-х стран) живут, как жили люди в 19 веке, абсолютно не представляя того, как работает пульт управления ТВ, лифт или светофор, не говоря о компьютерах, смартфонах, спецэффектах в кино и роботах? И если они случайно не попадут в ССУЗ или ВУЗ на научно‑технический факультет, они так и останутся «засланцами» из 19 века, научившимися говорить: «Ну‑ка, зеркальце, скажи, да всю правду доложи: я‑ль на свете всех милей, всех прекрасней и белей?» Чем еще, кроме конспирологии, можно объяснить этот вопиющий факт? О какой личной успешности таких людей в 21-м веке можно говорить? А с их неуспехом придет и неуспех страны. Не так ли?
В России «Спасение утопающих — дело самих утопающих». И когда на глаза попался совет Михаила Жванецкого:»Писать нужно, когда уже невтерпеж! », — подумал: «Наверное, пора!» Написали вместе с 13-летним внуком две книжки о программировании на возраст 10+. Ведь ни в программировании на MicroPython «школьного» контроллера, ни в создании игр в среде Roblox нет ничего такого, что было бы недоступно пониманию «рядового» школьника даже меньшего возраста. Лейбл »10+» поставили только из‑за того, что понимаем, что помощников у детей нет и разбираться с тем, что написано в наших книжках, им придется абсолютно самостоятельно. Обе книжки по большей части отражают личный опыт, полученный внуком в ходе упорного «грызения» туториалов Micro: bit и Roblox, просмотра «подсказок» в YouTube и разработки‑отладки программ. Дополнительным мотивом была реализация принципа: «Хочешь понять — объясни другому!» Книжки тонкие — по 120/160 страниц (не любят современные дети «лонгридов»). Потому что без «воды» и утомительных «тренировочных задач» — только все самое нужное, что можно сразу же по прочтении встроить в свою игру! Но зато уж точно написаны понятным языком! С самыми подробными комментариями к скриптам, с простым объяснением функций Sin и Cos и др. Должен же читатель понимать, почему скрипт устроен именно так, а не только применять Ctrl+C — Ctrl+V, как частенько советуют в YouTube.
Сделав маленький вклад в расширение доступа к IT‑знаниям, почувствовал, что остановиться трудно, потому как «поле» это в России практически непаханое! Захотелось продолжить, хотя бы для того, чтобы помочь внуку расширить кругозор и приучить его учиться «нон‑стоп». А тему, о чем еще будет полезно написать, выбрал, ориентируясь и на интересы внука, и «в пику» крутым «заманухам» — рекламе Интернет‑курсов по Unity! Обещанные в такой рекламе трудоустройство, быстрая карьера и финансовый успех в геймдеве в совокупности с невысоким качеством сопровождающих «замануху» материалов и с отсутствием четких учебных планов вполне внятно описали еще один вид «IT‑лохотрона»…
Наличие интереса к разработке игр в Unity подтвердило и большое число просмотров статей по этой теме на habr.com, и множество положительных комментариев к таким статьям. Посмотрел переводные книги — они явно не на подростков! Возможно, что качественная книжка по основам Unity будет полезна. Но чтобы точно убедиться в этом, исследовал англоязычный туториал по Unity и провел экспресс‑анализ самых разных публикаций о разработке игр на этом движке. Это привело к выводу, что основы Unity 3D можно описать гораздо более полно и более системно, чем у «конкурентов». Естественно, придется ограничить круг рассматриваемых тем, но объем доступного для самостоятельного изучения материала вполне позволит школьникам, уже прошедшим «курс молодого бойца» в Roblox, начать создавать качественные игры и в Unity. Выбор был сделан! Более того, пару недель назад книжка уже вышла из типографии.
А чтобы можно было как‑то оценить доступность новой книжки пониманию подростков, давайте сравним то, как одна и та же тема раскрыта в разных печатных изданиях. Например, рассмотрим так называемые «коллайдеры»:
Вот, например, что написано о них в книге Джона Мэннинга и Пэриса Батфилд‑Эддисона »Unity для разработчика. Мобильные мультиплатформенные игры», СПб, 2018
(см. стр. 53): «Добавьте коллайдеры в части тела. Коллайдеры определяют физическую форму объектов. Так как разные части тела имеют разную форму, в них нужно добавить коллайдеры разной формы:
Выберите спрайты рук и ног и добавьте в них компоненты Box Collider 2D.
Выберите спрайт головы и добавьте компонент Circle Collider 2D. Оставьте значение радиуса, установленное по умолчанию.
Выберите спрайт тела и добавьте коллайдер Circle Collider 2D. После этого перейдите в панель инспектора и уменьшите радиус коллайдера примерно до половины размера тела. Теперь части тела гномика готовы к соединению друг с другом. Соединение частей тела будет осуществляться с помощью сочленения HingeJoint2D…»
Согласитесь: очевидно, что имеет место погрешность перевода. Ну и краткость в данном случае — вовсе не сестра таланта… Это была страница 53. Следующее упо минание термина «коллайдер» мы находим на стр. 88 в комментариях скрипта:
// Если да, удалить все сочленения.…
foreach (Joint2D joint in GetComponentsInChildren ())
{ Destroy (joint); }
// …и коллайдеры.
foreach (Collider2D collider in GetComponentsInChildren ())
{ Destroy (collider);}
Далее термин «коллайдер» встречается в комментариях скриптов, приведенных на стр. 92, 100, 105 и только на стр. 112 встречаем (опять‑таки не совсем четкое) пояснение:
«Основой класса SignalOnTouch является метод SendSignal, который вызывается методами OnCollisionEnter2D и OnTriggerEnter2D. Последние два метода вызывает движок Unity, когда объект касается коллайдера или когда объект входит в область действия триггера. Метод SendSignal проверяет тег объекта и, если он хранит строку «Player», генерирует событие Unity. Теперь, имея класс SignalOnTouch, можно добавить первую ловушку…»
Согласитесь: начав со стр. 53, не завершить (хотя бы в общем) тему до стр. 112 — это странно. То есть это однозначно не на подростков!
Теперь посмотрим, что рассказано о коллайдерах в книге Ферроне Харрисона »Изучаем C# через разработку игр на Unity». 5-е издание, 2022:
(стр. 231) «Компоненты Collider определяют, как и когда объекты GameObject входят и выходят из физического пространства друг друга, сталкиваются или отскакивают. К одному GameObject должен быть прикреплен только один компонент Rigidbody, а вот компонентов Collider может быть несколько. Такой вариант называется составным коллайдером (рис. 7.7).»
Рис. 7.7
(стр. 237) «Компоненты Collider не только позволяют физической системе Unity распознавать объекты, но и дают возможность реализовать взаимодействия и столкновения. Коллайдеры — своего рода невидимые силовые поля, окружающие объекты GameObject. Они могут позволить проходить сквозь них, а могут быть твердыми, в зависимости от их настроек. У коллайдеров есть множество методов, которые срабатывают во время различных взаимодействий… Взгляните на приведенный ниже скриншот объекта Capsule. Зеленая оболочка вокруг объекта — это компонент Capsule Collider. Его можно перемещать и масштабировать с помощью свойств Center, Radius и Height. Когда мы работаем с примитивом, его Collider по умолчанию соответствует форме примитива.
Рис. 7.8 Скриншот объекта Capsule
Поскольку мы создали примитив Capsule, у него в комплекте сразу есть Capsule Collider.
У коллайдеров есть различные формы: Box, Sphere и Mesh. Их можно вручную добавлять в меню Component Physics или с помощью кнопки Add Component на панели Inspector. Когда компонент Collider входит в контакт с другими компонентами, он отправляет так называемое сообщение или рассылку. Любой сценарий, в котором есть один или несколько соответствующих методов, получит уведомление, когда Collider отправит сообщение.
Например, когда два GameObject с коллайдерами контактируют друг с другом, они оба отправляют сообщение OnCollisionEnter со ссылкой на объект, с которым произошло столкновение.
Полный список уведомлений, которые выдают компоненты Collider, можно найти в разделе Messages на странице docs.unity3d.com/ScriptReference/Collider.html. События столкновения и триггера отправляются только в случае, когда сталкивающиеся объекты относятся к определенному набору компонентов Collider, Trigger и RigidBody, а также в случае кинематического или некинематического движения. Подробнее можно почитать в разделе Collision action matrix по адресу docs.unity3d.com/Manual/CollidersOverview.html.» (конец цитирования темы коллайдеров в книге Ферроне Харрисона).
Получается, что и эта книга — не на подростка, да и не на всех, кто постарше, ведь нужно знать английский, либо «пилить» тексты переводчиком. А теперь сравните, что написано про коллайдеры в новой книжке (ниже приведены три главы текста):
Игровые объекты Unity
Игровые объекты GameObject могут быть как физическими объектами (персонажами, травой, деревьями, транспортом, зданиями, оружием, пулями, взрывами) так и виртуальными (интерфейсами и др.). У каждого физического игрового объекта есть физические координаты. Допускается вложение игровых объектов в «родительские» игровые объекты. Координаты вложенного физического объекта привязаны к координатам «родительского» объекта. Если же объект расположен непосредственно в сцене, он позиционируется относительно «мировых координат». Например, в родительский объект автомобиля могут входить колеса, корпус, окна и т.д. Каждый игровой объект состоит из компонентов, реализующих свой набор функций и свойств, необходимых, чтобы объект мог быть именно таким, каким он задуман. Это позволяет собирать объекты из отдельных простых блоков.
В состав GameObject могут входить следующие компоненты:
— Renderer, который рисует объект в игре; — Transform, задающий положение и ориентацию объекта в игровом мире; — Collider, задающий границы столкновений с другими объектами; — Animator, оживляющий объект; — Script, управляющий его движением, здоровьем, уроном и т.п.; — Audio Listener, выводящий аудио через динамики компьютера; — и др.
Рассмотрим примеры того, как свойства и поведение объектов отображаются в окне Inspector. Для этого кликнем ранее вставленный нами куб в окне Hierarchy, чтобы он отобразился в окне Inspector.
* Статус активности объекта. По умолчанию GameObjects активны, но они могут быть деактивированы. Деактивация отключает все компоненты, входящие в GameObject, делает объект невидимым и не реагирующим ни на какие события. Активный статус GameObject соответствует наличию флажка слева от имени объекта.
Рис. 4.1 Статус активности объекта Cube
Статусом активности, как и многим другим, можно управлять при помощи скриптов, а не только «вручную»:
— для деактивации статуса GameObject достаточно указать GameObject.SetActive (false);
— для активации статуса GameObject достаточно указать GameObject.SetActive (true);
* Компонент Трансформация (Transform). Используется для размещения всех игровых объектов в пространстве игры, определяя их положение (Position), ориентацию (Rotation) и масштаб (Scale). К каждому GameObject всегда прикреплен Transform.
Рис. 4.2. Компонент Transform объекта
* Компонент Mesh Filter содержит ссылку на сетку (mesh) — совокупность вершин, рёбер и граней, которые определяют форму многогранного объекта в трехмерной компьютерной графике.
Рис. 4.3 Компоненты Mesh Filter и Mesh Renderer объекта
* Компонент рендеринга (Mesh Renderer) визуализирует в игре mesh трехмерного игрового объекта и включает следующие параметры:
— Materials — материалы объекта с указанием их количества; — Lighting — свойства освещения включая:
* Cast Shadows (отбрасывает ли этот объект тени); * Receive Shadows (отображаются ли тени на самом объекте); — Additional settings — возможность отслеживания движения объекта.
* Компонент границ столкновения объекта (Box Collider)
Рис. 4.4 Box Collider
Этот компонент определяет геометрическую фигуру (параллелепипед, сферу, капсулу) или несколько таких фигур, охватывающих части объекта, и может служить в игре для определения моментов столкновений этого объекта с другими объектами. Выбрать тип объекта можно, нажав в главном меню Unity кнопку Component — Physics, и далее выбрать Box, Capsule, Sphere, Mesh.
Чтобы изменить границы столкновений выбранной фигуры, кликните по кнопке Edit Collider внутри компонента — в границах объекта появится параллелепипед или другая фигура с мелкими желтыми квадратиками, расположенными на каждой его грани. Курсором мыши, нажав ЛКМ, можно удобно и быстро менять размеры фигуры, устанавливая наиболее оптимальные. Наведите курсор на мелкий квадрат и: — нажмите клавишу Shift, чтобы изменять размер объекта по всем трем осям X, Y, Z сразу; — нажмите клавишу Alt, если хотите менять размер только по одной из осей координат;
— двигайте мышкой в нужную сторону; — для выхода из режима редактирования границ столкновения объекта нажмите кнопку Edit Collider еще раз.
Коллайдеры имеют ряд других параметров:
— впараметре Is Trigger (Триггер) «галочка» ставится, когда нужно, чтобы при столкновениях объект столкновения не становился преградой, пропуская сквозь себя движущийся объект, но при этом запускались какие-нибудь события;
— параметр Material используется для придания объектам свойств трения и упругости (способности подпрыгивать). Из-за наличия исходно установленных свойств трения и упругости игровые объекты и персонажи могут самым неожиданным образом «прилипать» друг к другу. Чтобы этого не случалось, объектам, в состав которых не входит компонент Rigidbody, нужно «обнулять» исходно заданные значения коэффициентов трения. Для этого кликните ПКМ в окне Project, выберите Create — Physic Material и в окне свойств материала обнулите указанные коэффициенты:
Рис. 4.5 «Обнуление» коэффициентов трения физических материалов
После этого созданный «физический материал» нужно применить в коллайдере, выбрав его в меню, которое откроется после нажатия на маленький кружок в окошке None (Physic Material).
— параметр Center используют для задания положения центра фигуры, определяющей границы столкновений;
— параметр Size задает размеры фигуры коллайдера.
В нашем конкретном случае параллелепипед Box Collider оптимально подходит для определения границ столкновений объекта типа «куб». Но очевидно, что существует большое количество объектов, для которых параллелепипед уже не будет оптимален. Как быть? В редакторе Unity все предусмотрено: в главном (самом верхнем) меню в разделе Component вы можете выбрать и добавить объекту, с которым работаете, и другие компоненты настройки границ столкновений. Допустимо применять к одному объекту одновременно несколько разных фигур определения границ столкновений («коллайдеров»). Каждый из этих коллайдеров должен охватывать какую-то из частей игрового объекта. |
Рис. 4.6 Путь выбора и перечень вариантов компонентов, используемых для определения границ столкновения объектов
6. Определяем факт столкновения объектов
Столкновение происходит, когда Unity обнаруживает, что коллайдеры двух игровых объектов вступают в контакт или перекрываются, если по крайней мере один из объектов имеет компонент Rigidbody и находится в движении. |
Вы можете добавлять коллайдеры в GameObject без компонента Rigidbody для создания полов, стен и других неподвижных элементов. Они называются статическими коллайдерами. Коллайдеры GameObject, которые имеют жесткое тело, называются динамическими коллайдерами. Статические коллайдеры могут взаимодействовать с динамическими коллайдерами, но, поскольку у них нет твердого тела, они не будут двигаться при столкновении с динамическими.
Давайте добавим нашему кубу (Cube) скрипт, определяющий его поведение:
— в окне Project выберите папку Assets и, нажав ПКМ, выберите в выпадающем меню Create — Folder. В директории Assets появится папка New Folder. Переименуйте папку в Scripts;
— выберите созданную папку, нажмите ПКМ и выберите в меню Create — C# Script. В папке появится файл с именем NewBehevior, представляющий собой заготовку нового скрипта. Переименуйте ее в Cube;
— откройте объект Cube окна Hierarchy в окне Inspector;
— наведите курсор на скрипт Cube в окне Project, нажмите ЛКМ и перетащите скрипт в окно Inspector — на кнопку Add Component в самом низу окна Inspector. Скрипт Cube станет одним из компонентов объекта Cube; — дважды кликните по скрипту — скрипт откроется в окне Inspector, и Windows предложит вам открыть скрипт в одном из доступных редакторов. Если же вы установили Microsoft Visual Studio, скрипт автоматически откроется в нем;
Рис. 6.1 Созданная Unity «заготовка» скрипта игрового объекта Cube
Дадим пояснение по составу заготовки скрипта:
* строки 1… 3 подключают стандартные библиотеки, используемые для работы скрипта;
* в строке 5 Unity объявил класс с именем Cube. Классы должнысодержать методы (функции), описывающие свойства и поведение каждого из игровых объектов. Название класса, как правило, должно совпадать с названием скрипта, чтобы Unity мог связать объект с нужным скриптом. MonoBehaviour показывает, что созданный класс относится к базовому классу компонентов, создаваемых пользователем. Модификатор public означает возможность доступа и использования данного класса в других классах (другими классами);
* классы обычно содержат несколько методов (функций), которые определяют поведение объекта. В заготовке скрипта Unity всегда создает два метода: Start и Update (см. строки 8 и 14). Эти методы вызываются:
— Start в первый момент, сразу после того, как скрипт загружается;
— Update впоследствии при каждом новом кадре игры.
Использовать методы Start и Update необязательно. Заменим оба метода своим скриптом (см. рис. 6.2), а именно:
* вставим в строке 8 функцию OnCollisionEnter (), позволяющую определять столкновения объекта с другими объектами,
* в строке 10 в состав этой функции включим функцию Debug.Log, которая (при обнаружении столкновений) будет выводить в консоль Unity сообщение, указанное в скобках этой функции.
Рис. 6.2 Скрипт, определяющий столкновения объекта Cube
Продолжим работу с объектом Cube:
— сохраните скрипт, выбрав File — Save Cube.cs или кликнув по «кнопке-дискете» в меню Microsoft Visual Studio и (после обработки кода редактором Unity) нажмите кнопку Play — вы увидите, что при падении куба внизу на «линии статуса» редактора Unity появится сообщение »Hit Something» («Столкнулся с чем-то»), которое мы вставили в Debug.Log;
— нажмите кнопку Console и откройте ее окно — в нем вы увидите два (или больше) таких же сообщения, что и на «линии статуса». Эти сообщения соответствуют столкновениям куба как с наклонной пластиной, так и с игровой платформой Plane.
Рис. 6.3 Сообщения о столкновениях (с какими-то объектами)
— чтобы понять, с каким именно объектом столкнулся наш куб, поменяем скрипт: введем (в строках 10 и 14) операторы if и else if. Они будут проверять имена объектов, с которыми произошло столкновение куба, и выводить в консоль редактора Unity соответствующие этому сообщения.
Рис. 6.4 Определение имен объектов, с которыми сталкивается куб
— запускаем программу и убеждаемся, что программа точно определила, что куб сначала столкнулся с объектом Cube_1, а затем с объектом Plane.
Рис. 6.5 Сообщения об объектах, с которыми столкнулся куб
11. Определение столкновений объектов с использованием триггера
Если вы хотите определить факт столкновения, но не хотите, чтобы объект, с которым столкнулся движущийся объект, стал преградой, нужно в компоненте, определяющем границы столкновения объекта-преграды, включить режим Is Trigger (поставив «галочку»). Это отключит физические свойства границ игрового объекта, но оставит возможность контроля «пересечения» заданных границ. |
Реализуем и испытаем этот режим:
— поставьте башне Tower_1 «галочку» Is Trigger;
— наведите курсор на «шапку» окна Rigidbody объекта Tower_1, нажмите ПКМ и в появившемся меню выберите Remove Component, чтобы удалить компонент Rigidbody (иначе игровой объект, лишенный физических границ, обладающий массой и находящийся под действием силы гравитации, просто провалится через игровую площадку;
— измените скрипт Tower_1:
Рис. 11.1 Модифицированный скрипт объекта Tower_1
Рис. 11.2 Сообщения в консоли, отражающие события столкновений объектов Box, Tower_1 и Tank
Нажав кнопку Play, мы видим, что, падая, кубик (Box) легко проваливается сквозь башню. Первое сообщение в консоли редактора Unity сообщает об этом же. Второе сообщение в консоли оповещает, что кубик столкнулся с игровой площадкой Plane. Затем, когда мы протаранили башню танком, появилось третье сообщение в консоли о том, что продолжая движение, танк столкнулся с объектом Box. А через 3 сек с момента тарана башни танком мы видим, что башня бесследно пропадает.
Кроме перечисленного выше, коллайдеры упомянуты в главе 32, описывающей создание анимаций персонажей, процитирую и этот небольшой блок текста:
»— кликните по Add Component —Physics — Box Collider и настройте коллайдер под размеры персонажа.
Будьте внимательны к коллайдерам, иначе ваши персонажи могут начать проваливаться «в пропасть», например: если у персонажа или Terrain, по которому он ходит, будут отсутствовать компоненты, определяющие границы столкновений или если вы случайно опустите нижнюю границу коллайдера персонажа чуть ниже верхней границы Terrain. |
Рис. 32.9 Настройка границ столкновения персонажа
На этом цитирование глав новой книжки завершаю. По-моему, я не упустил ничего важного для понимания темы «коллайдеров» и не заставил никого из читателей-подростков перенапрягать когнитивные способности. Практика также показывает, что приведенные примеры скриптов, снабженные построчными комментариями, легко воспринимаются теми, кто до сих пор не имел опыта программирования на С#, но имеет хоть маленькую практику написания скриптов в Roblox.
Если вы, уважаемый читатель, дошли до текущей строки моей заметки и вам интересно, что это за книжка, сообщу, что «гуглить» нужно «Азбуку программирования игр в Unity 3D». А еще мне очень хочется надеяться, что однажды и вы напишете свою «детскую» книжку или создадите иной IT‑контент, который будет полезен российским подросткам. Ведь не случайно же вы остались, отказавшись от релокации? И потом: «Если не мы, то кто?»