В этой статье мы создадим самонаводящуюся ракету, рассмотрим алгоритм поведения ракеты от старта до попадания в цель, сделаем простой выхлоп частицами, дым и взрыв.
Для начала нам надо решить какие состояния есть у ракеты и какие эффекты нам предстоит сделать:
Первое: Ожидание. Ракета ждет команды на старт, с ней ничего не происходит.
Второе: Старт. Ракета стартует, она получает цель, и ее специальным направленным взрывом или срабатыванием первой ступени выбрасывает из шахты на определенную высоту.
Эффекты: Направленный взрыв и движение ракеты из стартовой позиции.
Третье: Включается маршевый двигатель и ракета начинает поворачиваться в сторону цели.
Эффекты: Выхлоп маршевого двигателя,дым.
Четвертое: Попадание в цель.
Эффекты: Взрыв.
Итак, у ракеты есть четыре состояния. Мы сделаем их через оператор switch и перечисление enum. Но для начала надо подготовить сцену.
Создадим Terrain.
GameObject>3d Object>Terrain
Добавим текстуру. Я использовал привычную мне текстуру бумаги миллиметровки. В инспекторе нажмем кнопку Paint Terrain, далее в открывшемся окне Edit Terrain Layers и в появившемся списке Create Layer выберем подходящую.
Отлично. Земля готова. Теперь надо создать саму ракету. Так как у нас всего лишь прототип, то достаточно создать 3D Object Cylinder, это и будет ракета.
GameObject>3d Object>Cylinder
Переименуем его в Missile и добавим к нему новый скрипт под названием MissileController.
Теперь нам нужна цель. Опять создадим Gameobject, только теперь это будет Cube.
Переименуем его в Target и добавим ему тег Target для того, чтобы ракета нашла его по тегу и добавила как цель.
Основные объекты мы создали теперь пришло время оживить нашу ракету.
Откроем скрипт MissileController в редакторе. Для этого в окне Assets кликнем правой кнопкой на нашем скрипте и в появившемся списке нажимаем Open C# Project.
В открывшемся редакторе (по умолчанию в Unity Microsoft Visual Studio) в скрипте MissileController объявим первую переменную. Это будет ссылка на компонент Transform цели ракеты.
В Start мы добавим строку находящую цель ракеты по тегу. Как мы знаем, функция Start вызывается до обновления первого кадра. То есть, либо при старте сцены, либо при появлении в сцене объекта.
Сохраняем скрипт и переходим в Unity. Во вкладке Hierarchy выбираем Missile и смотрим на наш скрипт в Inspector.
Пока что переменная Target пуста. Но стоит нам нажать Play как скрипт находит цель и записывает ее в переменную.
Отлично. Теперь перейдем к перечислению состояний ракеты. Воспользуемся ключевым словом enum, которое используется для объявления перечисления – списка именованных констант и объявим переменную для доступа к списку.
Перейдем к оператору switch. Для чего он нужен и что это за зверь такой? Это оператор выбора, который выбирает для выполнения один раздел из нашего списка перечисления enum, сравнивая их с выражением соответствия.
Итак, у нас получается четыре раздела switch. Пора заполнить их действиями.
Первый раздел это idle. В него мы поместим таймер, отсчитывающий время до старта ракеты. Требуется это для того, чтобы ракета не взлетела сразу после запуска приложения. В дальнейшем можно отказатся от таймера и запускать ракету по какому либо событию. Например, нажатию кнопки.
По истечении времени таймер переключит переменную missile_state_t из idle в start.
Стоит обратить внимание на Time.deltatime. Это время в секундах, которое потребовалось для отрисовки последнего кадра.
То есть, значение Time.deltatime не равно секунде. Стоит учитывать это, например, при написании часов.
Далее у нас идет раздел start. В нем мы передвинем ракету на пару метров вперед и переключимся в раздел fly.
Объявим публичную переменную типа float speed_move – скорость движения и приватную Vector3 start_pos – точку, куда будет двигаться ракета в разделе start.
В методе Start мы зададим значение переменной start_pos местоположением ракеты.
Это требуется для того, чтобы знать расстояние, которое ракета преодолела с момента старта для включения в нужный момент раздела fly.
Для этого мы воспользуемся функцией Vector3.Distance, которая возвращает значение расстояния между двух точек. В нашем случае это точка старта ракеты и текущая позиция ракеты.
Получив расстояние и проверив, с помощью оператора If, прошла ли ракета стартовую дистанцию, переходим в следующий раздел и начинаем двигать ракету.
Делаем мы это с помощью transform.Translate. То есть, двигаем ракету по одной из осей с заданной скоростью.
В разделе fly мы продолжаем двигать ракету с помощью Translate. Только теперь определяем вектор направления на цель и плавно поворачиваем ракету, чтобы вектор указывающий на цель совпал с вектором движения ракеты.
Чтобы получить вектор, направленный на цель, мы должны вычесть вектор цели из вектора ракеты. Одновременно с этим получаем длину нового вектора. Она нам понадобится для отслеживания попадания в цель. И с помощью Slerp сферической интерполяции разворачиваем ракету. А когда расстояние до цели окажется меньше 1 переходим в последний раздел end.
В последнем разделе мы уничтожаем цель и ракету с помощью Destroy.
На этом логику самонаводящейся ракеты можно считать оконченной и пора переходить к эффектам.
Первый – это эффект старта ракеты. Сделаем что-то вроде направленного взрыва. Заодно его можно будет использовать как эффект попадания ракеты в цель.
Создадим пустой Gameobject и добавим ему компонент Particle System. Переименуем его в Explosive, ведь это будет эффект взрыва.
В инспекторе настроим его как на скриншоте и рассмотрим пункты настроек, которые мы изменили. Идем сверху вниз.
Looping – этот пункт отвечает за то, будет ли повторятся взрыв или нет. Отключаем, ведь у нас взрыв одиночный.
Start Lifetime – время жизни частиц. Чем больше время, тем дольше частицы взрыва существуют.
Start Speed – начальная скорость частиц. Совместно со Start Lifetime регулируется дальность разлета частиц. Чем выше скорость и время жизни, тем дальше распространяется взрыв.
Start Size – стартовый размер частиц. Можно экспериментировать, чтобы получить наиболее красивый результат.
Далее во вкладке Shape требуется переключить поле shape в sphere (сферу) для того, чтобы частицы распространялись во все стороны.
В последней вкладке Render требуется назначить Material – это материал наших частиц. Я назначил по умолчанию Material Default-Particle.
Взрыв готов. Теперь его надо сохранить в Prefabs и применить в сцене. Для чего надо немного дописать скрипт ракеты.
Объявим переменную Gameobject и в инспекторе назначим на нее префаб нашего взрыва.
Далее нам потребуется чтобы наш взрыв в нужный момент и в нужном месте появился в нашей сцене. Моментов этих два, start и end разделы. Добавим в них Instantiate – эта функция клонирует оригинальный объект и возвращает этот клон. Проще говоря, устанавливает в сцену клон заданного объекта в установленные координаты и угол поворота.
Теперь при старте и при попадании ракеты в цель в сцену устанавливаются эффекты взрыва. Нам осталось добавить выхлоп ракетного двигателя и дым. По примеру нашего взрыва создадим еще один GameObject и назовем его Exhaust (выхлоп).
Настроим Particle Systems как на скриншоте.
Сделаем наш эффект выхлопа дочерним ракете для того, чтобы он следовал за ней.
Сделаем дым. Для этого нам надо во вкладке Color Over Lifetime поле Color задать как на скриншоте.
На этом эффект ракетного выхлопа можно считать готовым, осталось только сделать управление для него.
Опять возвращаемся к скрипту ракеты и добавляем новую переменную GameObject exhaust. В редакторе в inspector назначим в нее эффект выхлопа.
И опять вернемся к скрипту. Нам требуется в нужное время выключить и включить эффект. Для этого используем SetActive.
В методе Start мы выключаем эффект, ведь двигатель у ракеты включается только в третьем разделе.
Но мы включим эффект не в третьем разделе, а в конце второго для того, чтобы вызов функции включения отработал только один раз.
Теперь все готово для запуска ракеты. Переходим в редактор, проверяем все ли правильно назначено и нажимаем Play.