Войти на сайт

Авторизация, ждите ...
×

ТЕМА: Godot Engine. Проект "Космический шутер"

Godot Engine. Проект "Космический шутер" 1 год 1 мес. назад #128992

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
Рабочая программа «Разработка компьютерных игр на движке Godot Engine»
Аркады. Проект «Space Shooter»

Оглавление разделов
1. Концепция
2. Космический корабль
3. Управление корабля. GDScript
4. Плазменный снаряд
5. Космическое пространство
6. Метеорит
7. Взаимодействие объектов
8. Интерфейс и предметы


ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ]


Приветствую вас форумчане, меня зовут Доктор Баг. В реальной жизни я работаю педагогом, провожу дополнительное образования (иными словами провожу кружки). Мое желание открыть кружок по разработки компьютерных игры, к сожалению, RPG Maker программы не подходят для этих целей, так как подобная серия программ заточена под определенный шаблон жанра. Конечно скриптами, плагинами можно изменять сам мейкер, но моя цель научить детей использовать свои знания не нагромождая их лишней информацией.
Мой выбор пал на игровой движок Godot Engine, по нескольким причинам
• Он бесплатный
• У него имеется язык программирования схожий на язык python (нынче я открываю кружок по основам python)
• И у Годота нету проверки лицензии (как у меня было постоянно с Game Maker Studio 2 особенно с моим крутым в кавычках интернета)
• Он кроссплатформенный
• На нем можно создавать и 3D игры

Мои планы таковы, я желаю развивать GameDev в образовательной сфере. Нынче у меня проходит кружок «Основы программирования Python», а на следующий год планирую открыть еще два кружка «Pixel Art» и «Разработка компьютерных игр на движке Godot». Пока что как таковой рабочей программы по «Разработка компьютерных игр на движке Godot» у меня нету. Но я собираюсь использовать Форумы для заметок, для создание методички, мол напоминания для себя, алгоритм действий что показывать детям. Но чтобы создать подобную программу, придется самому как следует разобраться в годоте. Я нашел способ: я буду изучать различные гайды, от начало до велико, различных авторов. Потому здесь я буду разбирать эти уроки, и открывать новые элементы самого годота. Скажем честно документация Годота оставляет желать лучшего.
Рабочая программа будет разбита на жанры игр. Первый Цикл уроков посвящен жанру Аркады: Другие разделы: казуальные игры, головоломки, платформеры, и рпг подобные игры (так как разнообразие рпг жанра слишком большое, будет браться только часть элемента), будут разбираться, когда я дойду до них, возможно буду создавать отдельные странички.
Подобную страничку можно использовать и для тех, кто хочет освоить сам годот, только вам еще нужно знать хотя бы основы программирования не обязательно языка годота. Ребята если буду долго не выкладывать информацию, подпинывайте меня, я бывает ленюсь 8)
1. Концепция

На этот раз наш проект будет «Космический шутер». Простенькая игра в котором игрок управляет небольшим корабликом, сражаясь с встречными метеоритами и вражескими кораблями. Игрок сможет управлять своим космическим судном в четырёх направлениях, не будет ограничений по сторонам. Игрок сможет стрелять плазмаметом из корабля, уничтожая противников. И у самого игрока будет жизни, если в игрока попали, уничтожается корабль, забирается сердечка, она же жизнь корабля. Игрок сможет защищаться время от времени, подбирая предмет «Щит». В игре будет три разновидности предметов. Щит – дает временную неуязвимость игроку, Сердечко – дает дополнительную жизнь и Усиление – повышает огневую мощь корабля.
Враги: метеориты и вражеские корабли. Метеориты будут плавно двигаться вниз по игровому миру, а вражеские корабли случайным образом будут двигаться из стороны в сторону, временами атакуя игрока.
Соответственно если закончатся попытки игрока, игрок проигрывает.
Зарисуем как будет выглядеть игровая механика, без графических излишеств.
Концепция «Космический шутер»
Игровое поле будет ограниченно, подобные размеры игрового поля можно будет подогнать под мобильные устройства при особой сноровки. Возможно когда-нибудь сделаю урок по импортированию игры на смартфоны.
Игровые очки будут начисляться после уничтожения противника, и в зависимости от уровня опасности противника будут повышаться игровые очки.
А на заднем фоне будут красоваться звезды, созданные из узла частиц.
Так же будут анимация взрывов, и особая техника для распада врага на части.
Для графики возьмем созданный набор Доктора Бага коcмической тематики(assets). Графика обновлена
здесь.
В наборе имеется все необходимое для создание базовой механики игры.
Теперь важная информация! Мы переходим на игровой движок годота версии 4.0

Вы так же можете обновить игровой движок, все что вам нужно просто скачать с официального сайта текущую версию, на данный момент свежая версия 4.0
Официальный сайт движка: https://godotengine.org/
Перейдите в раздел «Download» и скачайте игровой движок под вашу операционную систему. Спуститесь ниже страницы чтобы найти нужную операционную систему.

В моем случаи это Windows 10.
Вы скачаете архив, выберете нужное место в компьютере и распакуйте движок.
Запустите движок.

Создайте новый проект нажав на кнопку «Новый проект».
Так как мы будем создавать игру «Космический шутер», то назовём наш проект на английском языке, под названием «Space Shooter». Первое что вам нужно выбрать место где будет располагаться ваша игровая папка совсем содержимым. Кликните на кнопку обзор и выберите место. В моем случаи все будущие проекты находятся по пути «Documents/Projects». Я создал папку «Projects» (Проекты) для будущих проектов.

Следующие пропишите название проекта и кликните на кнопку «Создать папку», годот создаст папку и добавит в общий путь. Теперь эта папка является корневой папкой вашей игры, в котором будет находится все игровые элементы, не выходя за пределы этой папки.

Теперь разберем отрисовщики, здесь их аж 3 штуки.
Отрисовщик «Forward+» - предназначен для создание 3х-мерных игр, оптимизированный под 3D-направление. В этом режиме можно создавать игры только для настольных ПК: стационарных компьютеров и ноутбуков.

Отрисовщик «Мобильные устройства», имеет в своей расположение менее продвинутую 3D-графику. Заточен для настольных и мобильных устройств.

Отрисовщик «Совместимость» заточен под 2Д игры, этот режим позволяет создавать игры не только для мобильных и настольных устройств, но и игры, предназначенные для веб-платформ.

Последнее метаданные контроля версии Git. Про эту систему вы можете узнать через интернет, система, позволяющая следить за вашим версиями проекта и в случаи чего откатываться к нужной версии проекта. Сейчас нам не принципиально эта система, оставьте как есть.
Выберете отрисовщик «Совместимость», подобный режим нам идеально подходит, так как мы не будем выходить за рамки 2Д-игр.
Кликните на кнопку «Создать и редактировать»

Узрите новую версию Годота.

Что же настроем сам Годот под 2д игры, уберем лишнее узлы предназначенные для 3д-направление. Перейдите в «Управление возможностями редактора…» через меню «Редактор».

С прошлого проекта у нас должен был остаться профиль «2D-Game», если его нет, создайте такой профиль.
Далее уберите галочку с 3Д Редактор. Чуть ниже мы спустимся и уберем еще пару галочек связанное с 3д узлами.


После выбранных ресурсов кликните на экспорт профиля.

Мы сохраним профиль в проекте «Космический шутер», но в дальнейших разработках других игр, мы можем брать с этого проекта заточенный на 2д игры профиль Годота. Либо вы можете сохранить в своем специальном месте.

Далее кликните по кнопке «Сделать текущим», чтобы настройки вступили в силу. Далее нажмите «ОК»

Затачивать игру будем под мобильные устройства (не отрисовщик). Мы выберем размеры экрана под стать мобильному устройству. Конечно размеры мобильных устройств могут отличаться, на они имеют приблизительные размеры, начиная от маленького экрана и заканчивая большим.
Так как наша игра сделана под стиль пиксель арт, что подразумевает маленькие размеры спрайта, то мы возьмем самый маленькие размеры экрана: 480х800 пикселей, чтобы спрайты не терялись в огромных пространствах экрана.
Давайте сразу это настроем. Перейдите в проект – настройка проекта.

Меню некоторых опции были переведены на русский язык. Во вкладке основные, мы ищем вкладку Дисплей, далее нам нужно Окно.

Изменим размер вьюпорта по ширине и высоте, тем самым мы настроем границы внутригрового экрана: Viewport Width: 480, Viewport Height: 800.

Закройте настройки.
Далее разобьём проект на простые этапы. Разобьем создание игры на этапы.
1. Создание боевого корабля игрока
2. Создание пуль
3. Создание фона
4. Создание метеорита
5. Взаимодействие пуль и противника
6. Создание щита корабля
7. Создание интерфейса жизни, щита и игровых очков
8. Создание вражеских кораблей
9. Создание анимации уничтожение объектов
10. Эффект тряски и подбираемые бонусы
11. Звуки
Этапы входе разработки могут изменяться, дополняться или даже удаляться, но придерживаться основного плана.
Скачанные ресурса Доктора Бага, распакуйте, переместите в корневой проект игры «Space Shooter» и переименуйте папку в название «Assets».

Убедитесь, что в файловой системе игры в Годоте отображается набор.

Мы будем использовать практически все элементы набора.

В папке «Suboleya» храниться шрифт на кириллице. Так же имеется музыка, сгенерированная нейросетью, вы можете использовать свою, так как её могут преследовать авторские права.
В следующей главе мы будем создавать корабль игрока, прописывать его движение по игровому миру.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Последнее редактирование: 10 мес. 1 нед. назад от Doctor_Bug.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Kerotan, Демий, Jas6666, Фред Канниг, VarVarKa, Turan_Super

Godot Engine. Проект "Космический шутер" 1 год 4 нед. назад #129030

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
2. Космический корабль

Обновим Годот до новой версии (на текущий момент). Стараемся обновлять да свежею версию, дабы подхватить улучшение и исправление ошибок (багов хех)

Мы начнем с создание космического корабля, которым будет управлять наш игрок. Нам нужно определять, когда корабль будет сталкиваться с противником, либо с вражескими снарядами. Для основы корабля подойдет физический узел «KinematicBody…», стоп, его нет!?
В новой версии физический узел «KinematicBody2D» переименовали в «CharacterBody2D» (Character – Персонаж, Body – тело. Дословно «Тело персонажа»), так что вы теперь не найдете этот узел даже если попытаетесь.

Кликните по другому узлу, и найдите узел «CharacterBody2D».

Переименуем узел игрока в «Player».

Далее привяжем наш анимационный спрайт, с помощью узла «AnimatedSprite2D», помним этот узел позволяет работать со спрайтом и его анимацией. Привяжите к основному узлу «Player» узел «AnimatedSprite2D». Хоть этот узел остался на месте, ох эти обновы.

Над физическим и анимационном узлом висят желтые восклицательные знаки. Каждому требуется дополнить что-то свое. Начнем с анимации.
Кликните по узлу «AnimatedSprite2D» и обратите внимание на инспектор справа.

Годот заботлива свернул все вкладки в инспекторе, напомним у нас версия годота 4.0.1, ранее мы использовали версию 3.4.3.
Раскройте вкладку «Animation» (Анимация). Узел анимации требует спрайтовые кадры «Sprite Frames». Создайте такой ресурс кликнув по <Пусто> и выбрав «Новый SpriteFrames».

В этом ресурсе мы и будем загружать и нарезать анимацию. Снова кликните, но уже по созданному «SpriteFrames», дабы перейти в режим «Спрайт кадры»

Теперь загрузим анимацию спрайта в систему. Наш кораблик, точнее картинка, спрайт имеет название «ship.png». Кликните на иконку «сетки» чтобы перейти к корневой папки, будем искать и загружать спрайт.

Все наши спрайты находятся в папке «Assets», перейдите туда, найдите картинку «ship.png» и нажмите «Открыть», дабы загрузить картинку в систему.

У вас откроется окошко с еще одним редакторам, увеличьте размер картинки (фактически сам спрайт не изменяется, лишь визуально) через иконку плюсика.

Не обращайте внимание на расплывчатость картинки, мы это исправим (возможно), но чуть позже. Сейчас нам надо грамотно поделить наши кадры по горизонтали и вертикали. Так как в ряду у нас по три спрайта, и в столбик также по три ряда, разделим по горизонтали и вертикали на 3 единицы.

Присмотритесь на спрайт, ровно по середине, у нас расположилась анимация летящего корабля прямо, а в левом столбике - анимирует наклон корабля в левую сторону, ну и правый столбик, наклон в правую сторону. Сейчас нам нужен столбик по середине. В ручную выберете нужные кадры, и нажмите на кнопку добавить 3 кадра.

Далее вы увидите такую картину. Размазанные спрайт, который мы с вами будем исправлять.

Чтобы предать спрайтам пиксель арт, придется поступить иначе, нежели в других проектах. Изменение версии 4.0 ведут нас прямиком к настройкам проекта (проект – настройки проекта). Там мы ищем рендеринг – Текстуры.

В Годоте реализовали фильтры, которые влияют на отображение спрайтов, параметр «Nearest» убирает размытие в спрайтах, правда пока что он не работает в отображение SpriteFrames, но в игре он будет отображаться в стиле пиксель арт. Задайте параметру «Default Texture Filter» значение «Nearest».

Сначала вам следует перейти к файловой системе в Годоте, раскрыть папку «Assets» найти нужный спрайт корабля «ship.png», выберете его. После нужно будет перейти во вкладку «Импорт». Будем переимпортировать спрайт в пиксель арт.
Корабль обрел свой пиксель арт стиль, правда только в рабочем пространстве.

Давайте сразу сохраним сцену, чтобы в дальнейшем не утерять данные. Кликните по меню «Сцена» далее «Сохранить сцену как».

Нашу сцену с корабликом мы назовем «Player» и по традиции создадим папку с таким же названием и сохраним туда сцену.

Вернемся к нашему узлу анимации, и переименуем анимацию на название «Straight» (Переводится как «Прямо»), да вот в этой области располагаются все созданные анимации.

Теперь добавим еще одну анимацию, здесь в «Спрайт кадры». Кликните на иконку «Добавить анимацию».

Переименуйте вновь созданную анимацию на слово «Left» (Влево). И кликните по решетки снова, чтобы добавить новую анимацию. Выбираем тот же спрайт корабля «ship.png».

Настройте спрайт, поделите его на 9 частей, как мы выше делали, и выберите левые части кадры и подгрузите их.

Как вы могли заметить у нас теперь имеется две анимации, которые мы можем переключать кликая на список анимации.

Теперь ваша задача сделать анимация «Right» (Вправо), чтобы кораблик был наклонен в правую сторону.
Вот что у вас должно получиться:

Если мы запустим нашу сцену, то кораблик не будет анимироваться, нам надо запустить автовоспроизведение. Кликните по иконки с буковкой «А» в треугольники над анимацией «Straight» (Прямо). Игра будет запускать анимацию кораблика с этой анимации «Straight».

Теперь займемся программированием управлением корабля, но прежде мы создадим ключи действий. Кнопки, которые будут активировать ключи действий и двигать наш кораблик туда куда мы того пожелаем. Ключам будут привязаны клавиши клавиатуры или действий джойстиков.
Прежде перейдем к Проект – Настройка проекта… - Список действий.

Версии 4.0 (.1) Годота интерфейс отличается. Кнопки по умолчании, которые были в предыдущих версиях, скрыли под переключателем «Показать встроенные действия»

Заметьте добавился новый параметр «Мёртвая зона», в значение 0.5, этот параметр рассчитан на силу прикасания стиков или курков джойстика. Чем ниже показания, тем слабее действует показателя стика или курка.

Left Stick и Right Stick - стики

Left trigger и Right trigger – это курки.
Пока что выключите опцию «Показать встроенные действия». Создадим свои собственные уникальные ключи для корабля.
Создадим пять действий, пять ключей.
• move_up (Двигаться вверх)
• move_down (Двигаться вниз)
• move_right (Двигаться вправо)
• move_left (Двигаться влево)
• shoot (Стрелять)
Начнем с ключа движение вверх. Введите ключ «move_up» в поле «Добавить новое действие»

А затем кликните на кнопку добавить.

Теперь привяжем клавиши и кнопки джойстика, мы будем двигать наш кораблик вверх по клавише «W» и стрелочки вверх. Кликните по плюсику действия.

Здесь тоже коснулись изменение версии 4.0. Теперь все кнопки, клавиши поместили в отдельные окно. Теперь чтобы определить нужную клавишу вам необходимо кликнуть в поле «Ожидание ввода…» и просто нажать на нужную кнопку клавиатуры или джойстика, или мыши.

Для ключа «move_up» мы привяжем клавиши «W» и стрелку вверх, кликните по полю и нажмите соответствующие кнопки.

Ниже есть даже привязка через дополнительные клавиши, такие как «alt», «shift» и т.д.
Нажмите «ОК». Как мы можем заметить, у нас была привязка к ключу.

Снова прожмите плюсик и на этот раз прожмите стрелку вверх.

Вы можете и вручную найти кнопки, но это будет долго. Хотя для джойстика, если у вас его нет, это единственный способ найти нужную кнопку, стик.

Можете включить джойстик (если есть) и привязать еще клавиши вверх. Все прожатые клавиши, будут срабатывать, когда будет активироваться ключи, что удобно для разработки.
Теперь добавьте еще четыре ключа и привяжите соответствующие клавиши.
• move_down (Двигаться вниз: стрелка вниз, S)
• move_right (Двигаться вправо: стрелка вправо, D)
• move_left (Двигаться влево: стрелка влево, A)
• shoot (Стрелять: пробел он же space, Enter)

Вот что у вас должно получиться. Добавляете следующий ключ, привязываете клавиши.

В следующий главе запрограммируем управление нашего корабля.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Jas6666, VarVarKa

Godot Engine. Проект "Космический шутер" 1 год 4 нед. назад #129039

  • Turan_Super
  • Turan_Super аватар
  • Вне сайта
  • Познающий
  • Сообщений: 12
  • Спасибо получено: 7
Спасибо. Жду следующую часть.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Doctor_Bug

Godot Engine. Проект "Космический шутер" 1 год 3 нед. назад #129062

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
3. Управление корабля. GDScript

Теперь, когда все кнопки назначены, давайте напишем скрипт который позволит управлять кораблем. Закройте окно список действий и обратите внимания на панельку сцен. Выберете узел «Player» и прикрепите новый скрипт к узлу. И отключите шаблон который предлагает сам Годот, он не понадобиться, по крайне мере сейчас.

Наш скрипт будет сохранятся в папку с игроком «Player», оставим как есть и просто нажмем «Создать».

Мы начнем прописывать плавное движение корабля. Для этих целей нам понадобиться скорость корабля. А так же переменные для ускорение и трение корабля, будет отвечать за трение и сбавлять скорость после отпускание кнопки управления.
• speed – скорость
• acceleration – ускорение
• friction – трение
Установим примерные значения, далее будем корректировать. А еще сделаем их экспортируемые, чтобы мы могли их подгонять под свои нужды. В версии 4.0 ключевое слово «export» требует специальный символ, прежде чем мы им воспользуемся, символ @. Давайте пропишем в коде.

Теперь если вернуться к узлу «Player» в инспектор, то там появиться дополнительные параметры настройки.

Далее нам понадобиться Vector2. Напомним, что такое Vector2:
Vector2 - Двухэлементная структура, которую можно использовать для представления позиций в 2D-пространстве или любой другой паре числовых значений. Иными словами, это массив имеющий координаты X и Y.
Vector2 = [x, y]
Аргумент «ZERO» в векторе указывает нам что направление не задано. Она выставляет значение вектора равная = [0, 0]
Создадим еще одну переменную и укажем вектор направление. На этот раз мы не сможем использовать переменную «velocity», так как она теперь зарезервирована, так что назовем нашу переменную «axis» (ось)

Далее нам понадобиться собственная функция, здесь мы будем отслеживать нажатие игроком клавиши движений. Назовём функцию «get_axis» (Get – получить, «Получить ось»)
В пишем такую конструкцию.

Теперь давайте её разберем. Модуль Input отвечает за ввод пользователя. А метод «is_action_pressed» отвечает за нажатие кнопки. Прописывая метод Годот будет давать подсказки виде ключей, тем самых ключей которые мы настраивали в настройке проекта – списке действий. Метод int конвертирует булево выражение в число, если Истина (true) – возвратит 1, иначе если Ложь (false) то возвратиться 0.
Но что происходит в этой формуле?
Если игрок нажал кнопку вправо «int(Input.is_action_pressed(‘move_right’))», то это выражение становиться истинной, а конвертер int превращает истину в единицу, так же работает и клавишей влево. Далее простая формула определяет куда двигаться объекту.
Вправо – Влево = результат направления

Так же работает и с клавишами вверх и вниз.
Далее полученное направление отправляем в Х и Y переменной вектора axis.
Следующая строчка возвращает вектор направление и нормализует скорость. Допустим за 1 кадр, ваш объект двигается по одной клетки. Если вы двигались вверх, то вектор направление был бы (-1, 0), на одну клетку корабль переместился вверх, а если вправо, то (0, 1), корабль сместился вправо. Но если вы будете двигаться вверх и вправо, ваша скорость увеличиться и корабль займет позицию (-1, 1), корабль переместится вверх вправо, словно корабль стал двигаться по диагонали, игнорируя положенную скорость, так как в этом направление вы не сделали шаг ровна в одну клетку, а прошли путь больше одной клетки. Метод normalized нормализует вектор направления, учитывая стороны в которые двигается ваш персонаж, чтобы перемещаясь под разным углом, вы гарантировано проходили точно такой же путь, как и в другие стороны.

В итоге функция «get_axis()» будет отслеживать нажатие клавиш перемещений и возвращать вектор направления, в которой будут храниться Х и Y.
Добавим еще одну функцию «player_movement()» которая будет отвечать за движение игрока, здесь же мы будем применять ускорение и торможение корабля. Разберемся в некоторых аспектах и изменениях обновленного годота 4.0.
Запишем такое выражение.

Вспомним что такое «delta».
Delta – это количество времени прошедшая с момента вызова последнего кадра игры. Если игра привязана к частоте кадров, то она работает быстрее на быстром ПК и медленней на медленном. Дельта же уравновешивает это, так как она берёт время, пройденное с последнего кадра, в итоге на любом ПК игра будет работать идентично не важно лагает она или нет.

На 15 строчки мы будем переприсваивать вектор2 помещая туда результат функции «get_axis()», даже если игрок не нажмет клавиши, туда будет помещаться пустой вектор2, а точнее Х и Y направления будут равняться 0, если же движение было, вектор вернет значения.
Теперь проверим вектор.

Для начала проверим вектор2 на отсутствие нажатие на клавиши движений, а после проверим на наличие движение, если игрок нажимает клавиши движения.
Слово «pass» ничего не выполняет, это простая заглушка для блоков чтобы система не вызывала ошибку. Впишем такую конструкцию в условие отсутствии нажатий клавиш.

По началу код может показаться сложным, но не переживайте разберем каждое слово. Как вы могли заметить здесь появилось ключевое слово «velocity», да теперь это зарезервированное слово узла «CharacterBody2D» и предназначено оно для хранения скорость объекта.
velocity - Текущий вектор скорости в пикселях в секунду, используемый и изменяемый во время вызовов move_and_slide. Об функции «move_and_slide», поговорим чуточку позже.
Сама встроенная переменная velocity хранит в себе вектор направления.
velocity = Vector2(0, 0)
Так что мы можем туда помещать наши вектора. Следующее, метод length() возвращает длину вектора. Из школьных времен, вспоминаем, как вычислять длину вектора с помощью квадратного корня из сумму квадратов его координат. Ну или изучаем заново ссылка: Один из случайных сайтов на тематику длины вектора.
В итоге выражение «velocity.length()» вычисляет длину вектора скорости и сравнивает со значением трения «friction», с учетом дельты, особенности обновлений кадров устройства. Так что значения «friction» может быть либо больше, либо меньше, зависит от мощности ПК или иного устройства.
В случае если длина вектора скорости больше значения трения, спускаемся на строчку ниже.

Здесь же код уже более понятней, мы просто сбавляем скорость объекта, учитывая нормализация направления вектора, умножаем на значения трения и вычитаем самой вектора скорости каждый игровой кадр, тем самым сбавляем скорость корабля. Так как дельта сильно урезает переменную трение (в моем ПК, дельта равняется 0,016…), то торможение идет постепенно, каждый кадр.
(трение) 100 * 0,016 = 1,6
1,6 пикселей срезается со скорости с учетом нормализации.
Допишем еще одно условие.

Если же длина вектора скорости объекта окажется меньше значения трения, то полностью останавливаем объект. Устанавливаем вектору скорости объекты X и Y нули.
В итоге эта конструкция притормаживает объект, если объект набрал скорость, и после вовсе останавливает его.

Теперь пропишем функцию если игрок нажал клавиши движения. Впишете следующую конструкцию.

Что же происходит здесь?
Здесь мы устанавливаем вектор скорости объекта, прибавляем каждый шаг. Мы используем вектор направленности «axis» (значения могут колеблется от -1 до 1, в зависимости от направленности объекта) и мы умножаем направление на ускорение «acceleration», так же корректируем движение с помощью дельты «delta». Дельта сильно влияет на ускорение, так как значения дельты могут быть приблизительно «0.016» (зависит от устройства), то умноженное на ускорение может сильно снизить значение.
К примеру:
Двигаемся влево, направление отрицательное
(-1, 0) * 700 * 0.016 = (-11,2, 0)
Метод «limit_length» - возвращает вектор максимальной длины, ограничивая его длину до указанного значения. Иными словами, вложенная скорость «speed» внутрь метода «limit_length», не позволит набрать скорость выше 500 единиц (так как speed равен 500), даже если скорость, направление будет отрицательным. Но если значение вектора ниже, то Godot позволит им воспользоваться.

Иными словами, этот механизм позволяет ускорять корабль, но не позволяет выйти за рамки максимальной скорости.

Кстати, давайте исправим название переменной скорости на более подходящее название. Вместо «speed» мы укажем «max_speed» (max – максимально, максимальная скорость).

В этой функции осталось указать функцию «move_and_slide». Теперь мы туда не вкладываем скорость velocity, но и без неё игрок не будет двигаться.

Move_and_slide() - перемещает тело в зависимости от скорости. Если тело сталкивается с другим телом, оно будет скользить вдоль другого тела (по умолчанию только по полу), а не сразу останавливаться. Если другое тело является CharacterBody2D или RigidBody2D, на него также будет влиять движение другого тела.
Теперь нам нужно обновления, помним, что у нас имеются две встроенных функций «_process» и «_physics_process». Мы разбирали их в предыдущих проектах. Функция «_physics_process» рекомендуют использовать при расчете движений, а функция «_process» использовать для менее требовательных ко времени процессам.

Теперь Годот указывает стрелкой на то что эта функция является встроенной.
Поместим туда функцию player_movement(delta).

И… мы это сделали, наконец-таки мы прописали движение нашего корабля. Запустите текущую сцену через клавишу F6 или на соответствующую иконку и проверьте работоспособность корабля.

Если вы все сделали правильно, ваш корабль начнет летать с шустрой скоростью, пускай сейчас им немного сложнее управлять, но мы это подправим. Да и размер корабля не мешало бы увеличить.

Давайте настроем параметры корабля, для лучшего управления. Здесь вы можете лично настроить под свою игру. Попробуйте поиграться с максимальной скоростью, ускорением и трением. Эти параметры настроены лично в моем проекте. Но их в будущем могу изменить.

Мы настроили скрипт корабля, но над узлом «Player» все еще висит восклицательный знак, это оповещение что для физического узла требуется форма столкновения. В других проектах мы использовали узел «CollisionShape2D» в котором мы брали готовые формы (Овал, окружность, квадраты и т.п.), но в этот раз мы сами нарисуем форму столкновения. Прикрепите узел под названием «CollisionPolygon2D», этот узел позволяет нам рисовать собственные формы столкновения. Узел отвечает за границы формы столкновения объекта.

Прикрепив узел, мы можем начать рисовать точки. Наш корабль треугольной формой, значит и форма столкновения будет треугольной формой. Выберете узел «CollisionPolygon2D» и кликайте на левый нижний край корабля. Кликая мышкой в рабочем пространстве вы размещаете точки, соединёнными отрезками. Вам понадобиться три точки, когда поставите третью точку кликните на начальную, первую точку, тем самым Годот окрасит захваченную область, это область и будет нашей формой столкновении.


Если вы случайно поставили точку не туда, или разместили лишнею точку, кликните правой кнопкой мыши по форме, и она удалиться.
Если вы не хотите удалять поставленную точку, а лишь поправить, зажмите её левой кнопкой мыши и перемещайте в нужную позицию.
Не беспокойтесь если форма будет меньше видимой области корабля, это даже хорошо. Мы будем создавать игровой трюк, когда игрок будет маневрировать между пуль и противниками, и чуть ли не задевая их, игрок будет ощущать себя словно Асом в маневрировании. Хотя форма столкновения просто меньше видимого корабля.
Сохраните сцену с кораблем. Создайте новую сцену.

Мы создадим игровой мир, сцену назовем «Space» (Космос), корневым узлом этой сцены будет простой узел «Node». Выберете «Другой узел» - «Node».

Переименуйте узел в «Space», а после сохраните сцену в папку «Space» под тем названием который предложит сам Годот.


Прикрепите дочернею сцену с игроком, нажмите на иконку с цепью, и найдите сцену «Player». Либо просто перетащите сцену из файловой системы прямо на узел «Space».

Если мы запустим игру наш кораблик окажется слишком маленьким.

Для нашей пиксельной игры минимальный размер экрана 480х800 оказался слишком большим. Давайте уменьшим экран в два раза. И экран будет составлять 240х400. Снова зайдите в настройки проекта – Дисплей – Окно, выставите значения экрана 240х400.

Размер то мы изменили, но если мы сейчас запустим игру, игровое окно будет маленьким.

Поэтому мы дополним некоторые параметры, а именно переопределим ширину и высоту игрового окна. Видно, что перевод Годота подтянули еще не везде, так как «Переопределение ширины окна» перевод есть, а параметр «Window Height Override» не перевели хотя это по сути «Переопределение высоты окна». В версии Годота 3.5.1 и ниже эти параметр назывались «Test width» и «Test height». Введите размер окна 480х800. Напомним версия Godot используемая сейчас 4.0.1. В будущем перевод может уже появиться.

Но и этого еще не достаточно, размер игрового окна мы переписали, но размер игрового поля не сохраняет те заданные пропорции. Нужно указать еще один параметр. Поднимите список параметров вверх и найдите параметры «растяжения – режим». Здесь для пиксельной игр, рекомендуют использовать режим «Viewport».
Выберете этот режим.

Переместите корабль игрока в центр и запустите игру. На этот раз сделайте сцену космоса главной сценой, через клавишу F5 или на соответствующую иконку.

Годот спросит назначить текущую сцену – Главной сценой, выберете «Выбрать текущий»

Что же корабль не сильно большой, но и не сильно маленький, будет место для маневра.

Если вы сделали главной сценой «Player», не беда, мы исправим это. Вам следует перейти всего лишь в «настройки проекта – приложение – запустить», там вы найдете параметр «Главная сцена», сбросьте этот параметр. Это в случаи если главной сценой сделали не сцену с космосом «Space».
Совсем забыл про анимацию. Перейдем скрипту и добавим узел через ключевое слово «@onready», да теперь добавляется @ в подобные ключевые слова.

Создадим функцию «animation_ship» в конце скрипта и от вектора движения по Х будем отслеживать какую анимацию запускать корабля. Если Х отрицательное, корабль двигается влево, если положительное, то вправо. А если Х равен 0, то он не двигается в сторону. Метод «play» запускает выбранную анимацию, узла «AnimatedSprite2D».

Далее нам нужно разместить функцию в физическое обновление экрана «_physics_process»

И ваш кораблик будет бодро поворачиваться в то направление куда двигается.

На этом глава оканчивается. В следующей главе займемся плазменными снарядами, квантовой бомбой и аннигиляторной пушкой… Хотя остановимся на плазменных снарядах =)
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Демий, Jas6666, VarVarKa

Godot Engine. Проект "Космический шутер" 1 год 3 нед. назад #129063

  • Turan_Super
  • Turan_Super аватар
  • Вне сайта
  • Познающий
  • Сообщений: 12
  • Спасибо получено: 7
Ура, спасибо.
Я таки дождался. :whistle:
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Doctor_Bug

Godot Engine. Проект "Космический шутер" 1 год 2 нед. назад #129102

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
4. Плазменный снаряд

Наш боевой корабль будет встречать противников встречным огнем, метая плазменными снарядами. Для создание снарядов нам понадобиться отдельная новая сцена. Создайте новую сцену. В качестве основы корневого узла будет выступать узел «Area2D». Выберете другой узел и найдите узел «Area2D».

Узел «Area2D» позволяет обнаруживать перекрытие объектов «CollisionObject2D» (физических объектов), когда один объект перекрывается другим объектом. Узел не имеет физической оболочки, следовательно, по этому узлу может проходить любой другой физический объект.
Теперь переименуйте в «plasma_projectile» (плазменный снаряд)

Теперь наделим этот снаряд картинкой, воспользуемся узлом «Sprite2D», который позволяет загрузить простую картинку.

Раскройте в файловой системе папку с картинками (Assets). Найдите картинку под названием «projectile.png», схватите картинку и поместите в параметр «Texture» в инспекторе узла Sprite2D.

Наш снаряд довольно маленький, сразу его и не увидишь, но он точно появился на экране. Увеличьте рабочее пространство в центре, и вы его увидите.

Узел «Area2D» (Узел «plasma_projectile») так же является физическим узлом и для взаимодействии с другими объектами ему так же следует принимать форму столкновения, здесь мы воспользуемся узлом «CollisionShape2D». Примените этот узел.

Узел «CollisionShape2D» похож на узел «CollisionPolygon2D», только отличие в рисование формы. Узел «CollisionShape2D» предлагает готовые варианты простых форм. Теперь нам надо выбрать форму в параметре «Shape» в инспекторе узла. Выберете форму капсулы «Новый CapsuleShape2D».

И настройте форму под форму снаряда, используя красные точки.

Да, наш снаряд будет вылетать с носа корабля, поэтому желательно сместить центр снаряда вниз, приподняв сам снаряд вверх. Для правильного высчитывания размещения снаряда.


Сохраните сцену в папку «Projectile», название сцены Годот возьмет из название корневого узла сцены.

Теперь займемся скриптингом. Выберете корневой узел сцены и прикрепите новый скрипт. Скрипт плазменного снаряда мы сохраним в папку со снарядами «Projectile», там же будем хранить и вражеские снаряды. Название можете оставить, то что предлагает сам Годот.

Наш снаряд будет двигаться, значит мы воспользуемся встроенной функцией «_physics_process()», которая обновляет наш игровой цикл.

И для снаряда так же требуется вектор движения, но наш снаряд имеет корневой узел «Area2D», которые не имеет встроенной функции «Move_and_slide()», функции которая позволяет скользить вдоль других тел, оно и не к чему. Но нам придется проделать движение самим, для этих целей понадобиться глобальные позиции объекта, мы присвоим позиции вектор движения. Вектор движения «movement_vector» Будет хранить обыкновенный вектор 2Д.

Чем отличаются глобальные позиции, от простой позиции объекта?
«position» - позиция относительно родителя узла. Локальное пространство.
«global_position» - глобальные позиция. Глобальное пространство.
Представьте космический корабль и в нем находится космонавт. Корабль находится в космосе и совершает путешествие к далекой звезде. Корабль будет родительским узлом, космонавт дочерним узлом. Космос же, в котором находиться корабль, будет родительским узлом корабля. У каждого объекта есть как локальное пространство, так и глобальное. Допустим корабль имеет глобальную позицию по Х и Y равное (0, 0), а космонавт, находящийся внутри корабля, имеет глобальную позицию равную (20, 0), так как он находится немного дальше от основания корабля, в середине судна. В этот момент локальные и глобальные позиции объектов будут совпадать. Через N количество времени, корабль совершил путешествие вперед изменив свои координаты. Теперь глобальные позиции корабля составляют (200, 0), локальные координаты корабля будут таким же, так как его локальное пространство это весь космос. Но вот координаты космонавта будут немного иными: глобальные координаты космонавта будут равняться (200 + 20, 0), 200 пройденный путь корабля с космонавтом, и само смещения космонавта относительно корабля на 20. А вот локальные координаты космонавта не изменятся, они будут так же равняться (20, 0), так как относительно корабля космонавт был не подвижен, если конечно он не пойдет в какую-либо сторону.

Вернемся к разработке. Мы привязываем снаряды относительно глобальных позиций, без привязки к кораблю. Хотя можно по экспериментировать и создать интересный эффект с привязкой снарядов относительно корабля, но это вы можете по экспериментировать самостоятельно.
Далее надо направить наш снаряд вверх. Зададим вектор движения по Y на отрицательное значения.

Помним, что за вертикальную ось отвечает ось Y, и расчет идет сверху вниз, по возрастанию, соответственно нам надо вычитать единицу чтобы подыматься вверх.
Одной единицы будет не достаточно, так как эта единица лишь отвечает за направление. Нам понадобиться переменная регулирующая скорость. Создадим экспортируемую переменную скорости «speed», чтобы далее мы могли её корректировать в будущем. Далее умножим скорость на вектор движения и на корректировку обновление частоты кадра, на дельту.

Помним, что дельта может иметь очень маленькие значения и приумножение так же может сильно снижать параметры, вывод: делаем больше скорости.
По идеи, наш снаряд уже может двигаться при запуски сцены. Предлагаю испытать сцену, перейдите в сцену с «Космосом» и прикрепите сцену снаряда, переместите снаряд вниз экрана. Это будет временная роль, после мы удалим этот снаряд, так как мы будем загружать программно сцену.

Запустите сцену.

Снаряд летит очень быстро. Вы все сделали правильно. Удалите сцену снаряда со сцены «Space».
Теперь нам понадобиться удалять снаряд, когда он покидает приделы видимости, приделы экрана, иначе снаряд никуда не денется и будет забивать память вашего устройства. Это не критично если снарядов 10, 100, 1000, но чем больше их будет становиться количество (улетевшие далеко в космос), тем сильнее будет грузить ваш процессор. Представьте, что вы выпустили более 10000 снарядов за игру, и каждый снаряд просчитывается: координаты, узлы столкновения, и кучу параметры которые они тащат за собой. Для избавление нам неугодных снарядов поможет узел «VisibleOnScreenNotifier2D», который заботливо уничтожает вылетевшие объекты за экран. Добавьте этот узел к узлу «plasma_projectile».

Обратите внимание на появившиеся область пурпурного цвета, эта область и будет высчитывать пределы видимости снаряда. Подкорректируйте область под форму снаряда.


Теперь обратимся к сигналу это узла. Найдите сигналы во вкладке «Узел» в возле инспектора. Нам нужен узел «screen_exited()».

Кликните по нему, далее убедитесь, что узел подключается к корневому узлу «plasma_projectile», и нажмите присоединить.

Сигнал — это функция, которая будет запускаться в определенные условии. Условие для каждого узла свои.
Функция «_on_visible_on_screen_notifier_2d_screen_exited()» будет срабатывать когда выходит пурпурной прямоугольник за предел экрана. Все что нам нужно будет прописать функцию которая будет уничтожить снаряд. За уничтожение объекта отвечает функция «queue_free()», пропишите внутри функции «_on_visible_on_screen_notifier_2d_screen_exited()» вместо заглушки pass.

В принципе со снарядом все, следующий этап, привязка плазменных снарядов к кораблю.
Откройте сцену с кораблем «Player», перейдите к скрипту.
Идея снаряда такова: игрок нажимает пробел, создается копия снаряда, снаряд летит вверх, удаляется по разным причинам (попадает во противника, либо удаляется за пределом экрана). Мы будем создавать экземпляры (копии) сцены снаряда, но для начала мы должны знать откуда брать сам снаряд, точнее откуда брать сцену. В этом деле поможет предварительная загрузка сцены снаряда функция «preload()», которая будет загружать сцену снаряда быстрее чем будет прогружаться сама сцена корабля. Мы создадим переменную «pl_projectile» в ней и будем хранить сцену, нужно только точно указать путь к сцене «plasma_projectile.tscn»

Сцену можно перенести прямо в скрипт, Годот укажет сам путь.
Следующее мы создадим функцию, в котором будем отслеживать момент нажатие клавиши пробела и будем создавать экземпляр сцены снаряда. Назовем функцию «shoot». Поместите условие отслеживающая нажатие пробела.

Когда кнопка будет нажата, условие будет срабатывать.
Теперь создадим экземпляр (копию) сцены снаряда используя функцию «instantiate». Поместим экземпляр в переменную «projectily».

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

«get_tree» - Возвращает SceneTree (дерево сцен), содержащий этот узел.
SceneTree - Управляет игровым циклом через иерархию узлов. Как один из наиболее важных классов, SceneTree управляет иерархией узлов в сцене, а также самими сценами. Узлы можно добавлять, извлекать и удалять. Все дерево сцен (и, следовательно, текущая сцена) может быть приостановлено. Сцены можно загружать, переключать и перезагружать.
SceneTree — это реализация MainLoop (Главного цикла игры) по умолчанию, используемая сценами, и поэтому она отвечает за игровой цикл.
Иными словами, метод «get_tree» дает нам доступ к дерево сцен из которой мы далее вынимаем текущую сцену с помощью метода «current_scene», точнее мы получим корневой узел сцены «Space». Метод «add_child()» - добавляет дочерний узел, который помещен как аргумент. В нашем случаи мы помещаем экземпляр снаряда в узел «Space».
Наши снаряды загружаются, но их надо разместить правильно на сцене. Начальные координаты лучше указать с позицией корабля, временное решение для проверки работоспособности снарядов.

Мы берем экземпляр сцены снаряда, его позицию и указываем позицию самого игрока. Да если указать просто слово «position» в скрипте, то будут браться координаты того узла кому привязан скрипт.
Ну и следующее надо указать в функции обновления нашу собственную функцию стрельбы «shoot». Разместим её последним.

А теперь запустите игру и зажмите пробел.

Снаряды вылетают слишком быстро, одной линией. Так как нету ограничений, то каждый кадр создается снаряд, если зажать кнопку пробел. Нам нужна задержка между выстрелами. Подобную задержку мы будем реализовывать с помощью узла таймера. Присоедините узел «Timer», в сцене с кораблем.

Переименуем узел таймер в «FireDelayTimer» (Fire – огонь, Delay – задержка и Timer – таймер. Таймер задержки огня). После перейдите к инспектору, включим параметр «One Shot», что не позволит зациклится таймеру и включаться каждую секунду «Wait Time». Таймер будет включаться один раз после выключаться, но мы будем программно его включать.

Перейдем к скрипту.
Нам понадобиться переменная, которая будет указывать как долго нам ждать между выстрелами. Переменная должна быть экспортируемая чтобы мы могли подрегулировать перед игрой. Назовем переменную «fireDelay» и поставим 0.1 секунды. Значения будет дробным.
А еще нам понадобиться переменная хранящая сам узел таймера, назовем переменную так же, как и узел, только с маленькой буквы «fireDelayTimer».

Грузим узел таймера через знак «$».
Теперь, когда мы нажимаем кнопку огня (пробела), мы будем запускать таймер с заданной задержкой. И пока таймер будет работать, мы не позволим создание новых снарядов. Таймер запускается от метода «start()», помещенное внутрь аргумент, это задержка «fireDelay», время через которое таймер отключиться.

Добавим дополнительное условие к нажатию кнопки огня.

Метод таймера «is_stopped()» возвращает истину в случаи если таймер остановлен. Оператор «and» соединяет первое и второе условие, читается это так:
Если нажата клавиша «Огня» И Таймер.остановлен Тогда условие выполнено, выполняем код ниже.
Если первое и второе условия выполняется, то снаряд будет создан, в противному случаи ничего не произойдёт.
А теперь перейдите к игре, и зажмите кнопку огня. Вы увидите, что снаряды вылетают не сразу, а с задержкой 0.1 секунд.

Задержку между выстрелами вы можете регулировать в инспекторе корабля.

Можно сделать прокачку корабля которая уменьшает задержку между выстрелами.
Сделаем еще одну очень крутую вещь, заодно правильно разместим выстрелы на корабле. Сейчас снаряды вылетают середины корабля, мы же сделаем дополнительные узлы с которого мы будем привязывать позицию снарядов. Да корабль будет стрелять с середины, с носа корабля, но мы сделаем возможность стрелять еще и с левого фланга и правого фланга корабля, подобную вещь можно включать подбирая к примеру временное улучшение боевой мощи корабля.
Как это будет работать?
Мы создадим простой узел «Node2D» и туда разместим еще пару таких же узлов. После перебирая дочерние элементы мы будем назначать снаряды каждой позиции корабля.
Давайте начнем, добавьте кораблю узел «Node2D» и переименуйте в «FiringPositions» (Переводиться как «Огневые позиции»).

Теперь в этот узел добавьте еще три узла «Node2D», назовем их:
• LeftFlank – левый фланг
• Head – Нос корабля
• RightFlank – правый фланг
(head – имеет несколько значений перевода: Голова, шапка, и т.д. В нашем случаи «нос корабля»)
Учтите эти три узла должны быть на одном уровне, а узел «FiringPositions» должен быть их родителем.

Теперь их надо правильно разместить. Левый фланг по левую сторону корабля, правый фланг по правую сторону, ну и нос корабля на макушки корабля. Чтобы случайно не сместить анимацию и форму столкновения, заблокируйте их с помощью замочка, не переживайте на игру это никак не повлияет, а вот вам не позволит сдвинуть их случайно. Узлы перетаскиваются вручную, либо можете настроить через инспектор и позицию каждого узла.

Переходим в скрипт корабля. Создадим переменную «firingPositions», которая будет хранить огневую позицию

Теперь в момент, когда снаряды будут запускаться мы будем перебирать все дочерние элементы узла «firingPositions» и каждому назначать по снаряду.

Ключевое слово «for» создает цикл, локальная переменная (которую можно назвать по разному, в нашем случаи ребенок – «child») помещаются дочерние элементы благодаря методу «get_children()», этот метод принадлежит самому базовому узлу «Node». Узел «Node2D» наследуется от узла «Node».
Иными словами цикл проходит по всем позициям: LeftFlank, Head, RightFlank и помещает их в локальную переменную «child», все это вынимается из узла «firingPositions» благодаря методу «get_children()». А слово in идет в связки с циклом for:
for переменная in узел.дети:
Так же обратите внимание на строчку 51, 52, 53, они смещены вправо с помощью кнопки «Tab» их иногда стрелками рисуют ))
Мы будем это переделывать.

Все что нам понадобиться далее позиция каждого ребенка.

Запустите игру и нажмите кнопку стрельбы. И тут вас будет ждать не приятный сюрприз, корабль не стреляет.
На самом деле корабль стреляет, только мы использовали локальные позиции, а помним где находятся? В углу сцены, даже за пределами видимости, локальные координаты этих позиций не изменились, даже не смотря что корабль перемещался, помним относительности пространства. Нам всего то и надо сделать позиции глобальными «global_position».


Запустите игру и насладитесь огневой мощью вашего боевого корабля

Все работает, но подобную мощь мы будем включать только после взятие особых значков усиление, сейчас мы пока будем использовать одну линию стрельбы. Чтобы не удалять узлы, мы будем отслеживать видимость позиции и если он не скрыть создавать снаряд для него. Перепишем чутка код. Снова придется сместить несколько строчек вправо.

Условие проверяет дочерние узлы на видимость «visible», если они видны, мы создаем снаряд, иначе не создаем, все просто.
Теперь если вы выключите видимость позиций они не будут отрабатываться.

Выключите левый и правый фланг, запустите игру и стреляйте.

Таким образом мы можем прописать улучшение которое включает видимость и заодно включает дополнительные снаряды. Так же есть идея создание молчанки, когда временно блокируются снаряды корабля, выключить видимость позиции носа корабля.
На этом глава «плазменные снаряды» окончены, следующей теме разберем создание фона и работа с частицами. Желаю отличного дня.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Последнее редактирование: 1 год 1 нед. назад от Doctor_Bug.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Демий, Jas6666

Godot Engine. Проект "Космический шутер" 1 год 6 дн. назад #129132

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
5. Космическое пространство

Космическое пространство, столь глубокое и бесконечное. Займемся оформлением пространства вокруг корабля. Поставим перед собой задачи:
• Настроить цвет космоса
• Создать имитацию параллакса за счет пролетающих звезд
• Ограничить пространство невидимыми стенами
Задач немного, начнем, пожалуй, с фонового цвета. В прошлых проектах мы использовали пользовательские узлы «ColorRect» и «TextureRect», в которых мы указывали сплошной цвет или использовали готовую фоновую картинку. В этот раз мы изменим цвет фонового пространства рабочего окружения.

Чуть не забыл, на текущий момент я использую Годот версии 4.0.2

Скачайте и обновите, либо используйте тот, который есть.
Первое нам нужно зайти в настройки проекта. Во вкладке «Основное» найти раздел «Рендеринг», далее найти параметры «Окружения» (на анг. Environment)

Далее найти параметр «Defaults» (перевод «По умолчанию») и далее найти опцию «Чистый цвет по умолчанию», кликните справой стороны по серому цвету.

Вам откроется окошко с палитрой цвета. Выберете темный цвета, если хотите можете выбрать черный, я же выберу темный цвет с фиолетовым оттенком «#120124».

Закройте окно настроек и узрите столь темный космос.

Следующая задача, создание звездного поля.
Для этой цели нам понадобиться узел создающая частицы. Этот узел всегда будет генерировать непрерывный поток частиц, и называется он «CPUParticles2D», в прочем давайте его добавим к нашей сцене с космосом.

На самом деле в Годоте существует два вида частиц:
• CPUParticles2D - излучатель 2D-частиц на базе процессора
• GPUParticles2D - излучатель двумерных частиц

Мы будем использовать «CPUParticles2D», так как он может поддерживаться более старые устройства, а еще и мобильные устройства. Переименуйте узел в «StarField» (Star – звезда, field – поле. Перевод «Звездное поле»).

Наша сцена довольно большая, сейчас, когда мы добавили частицы, они уже работают. Увеличьте экран и присмотритесь в левый угол.

Да вот здесь

Нескончаемый потом частиц, вытекают под действием гравитации вниз.
Если взглянуть на инспектор, то можно обнаружить массу различных настроек.

Мы частично разберем основные из них.
Как вы видите наши частицы падают одной струей вниз, нам бы хотелось, чтобы они падали по области, с верху вниз, занимая всю площадь игрового экрана, мы тем самым будем делать имитацию движения корабля.
Что же перейдите к параметрам «Emission Shape» (Emission – излучения, shape – форма. Перев. «Форма излучения»). Здесь мы будем указывать форму излучения, по умолчанию стоит «Point» - точка, мы же выберем форму «Rectangle» (Прямоугольник). Так же здесь имеется такие формы как: Сфера (Sphere), Поверхность сферы (Sphere Surface), Точки (Points) и Направленные точки (Directed Points)

Форма задает область распространение частиц. Так выглядит сфера

Так прямоугольник

Так точки, здесь показаны 4 точки, но их можно делать бесконечное количество. Выставляйте их куда хотите.

Вернемся к разработке. Вы выбрали форму прямоугольника.

Параметры Х и Y задают ширину и высоту прямоугольника. Зададим ширину прямоугольника равное размерам нашего игрового поля. Ширина игрового экрана равняется 240 пикселей, а вот ширина (и высота) идет от центра частиц. Значит, чтобы правильно настроить ширину нам нужна лишь половина экрана.
240 / 2 = 120 пикселей.
Задайте ширину частиц 120 пикселей по Х. Далее разместите частицы в середину игрового экрана используя параметры позиции «Position».

Мы оставим по высоте в 1 пиксель, ведь мы создаем область в котором будут появляться частицы, будет не совсем естественно если звезды будут появляться в центре игрового экрана. Пускай звезды появляются в самом вверху экрана.
Следующие, в нашем наборе имеются картинки звезд, и мы бы хотели создать именно пролетающие звезды, а не частички по умолчанию.
Раскройте параметр «Drawing» (Рисунок).

Как в узлах спрайта и «TextureRect», так и здесь имеется параметр в который можно поместить картинку, и называется этот параметр «Texture». Перейдите в файловую систему, раскройте «Assets», найдите рисунок «star2.png», почему именно этот рисунок объясню позже. Перетащите рисунок в «Texture».

Как только вы это сделаете то частицы поменяют свой рисунок.

Звезды достаточно маленькие, давайте подправим это. Параметр «Scale» (Масштаб) позволит нам настроить размер частиц.
• Scale Amount Min – устанавливает минимальный размеры частиц
• Scale Amount Max - устанавливает максимальный размеры частиц

Настраивая разные размеры частиц вы так же задаете случайность размерам. У нас пиксельная игра, здесь не сильно можно настроить размеры частиц, но вы должны так же знать про параметр «Scale»
Следующие наши звезды под властью гравитации «Gravity».

Отключим эту зависимость, установим гравитацию по оси Y на 0. Мы хотим, чтобы звезды двигались с постоянной скоростью, а не с ускорением

Обратите внимание звезды теперь появляются в области прямоугольника, но не падают вниз.

Сейчас частицы появляются и исчезают, их постоянная скорость, вектор движения равняется 0. Давайте зададим постоянную скорость частицам, в параметре «Initial Velocity»

Здесь мы так же можем задать случайность скорости движение, какие-то частицы будут двигаться медленнее, какие наоборот быстрее, при условии, если параметры «Velocity Min» (Минимальная скорость) и «Velocity Max» (Максимальная скорость), будут иметь разные значения. Скорость измеряется в пиксель в секунду. Зададим скорость 70 пиксель/секунду.

Теперь наши частицы двигаются с постоянной скоростью, но двигаются вправо.

Это происходит из-за направления, в частицах так же имеется направление. Как мы задаем направление корабля, так и в частицах имеется параметр направления «Direction» (Перев. «Направление»)

Здесь есть направление по оси Х и Y, а также имеется угол разброса, сейчас стоит 45 градусов. Случайным образом частицы разбрасываются, отклоняясь от заданного направление, то вверх, то вниз. Сменим направление, мы будем двигаться частицы вниз, по оси Y, значит должно стоять значение 1, а ось Х на 0. Далее мы уберем разброс, установите параметр «Spread» на 0 (одно из значения перевода Spread это «Размах»).

Звезды теперь двигаются с постоянной скоростью в нужном направлении, но их очень мало.

8 звездочек на экране. За количество частиц на экране отвечает параметр «Amount» (Перев. «Количество»), параметр расположен выше остальных параметров.

Увеличим их количество, примерно до 50.

Теперь на экране расположены 50 частиц звезд. Но как видим далеко они не распространяются, это связанно с тем что у каждой частички есть время жизни. Частица появляется, живет и удаляется. За время её жизни отвечает параметр «Lifetime» (Перев. «Продолжительность жизни») во вкладке «Time».

Lifetime указана одна секунда жизни, именно столько сейчас существует частица перед её полным удалением. Давайте подбирать секунды, чтобы частица успела долететь до границы игрового экрана, прежде чем исчезнуть. Секунд 6 оказалось не достаточно, а вот 7 секунд само то.

Не страшно если звезды будут падать чуть ниже экрана, они все равно будут удаляться.
Но есть одна особенность, когда мы будем запускать игру, звезды будут появляется вверху и спускаться вниз, при этом экран будет пустовать без звезд, пока частицы не заполнят весь экран.

Так что мы заранее пропишем параметр который будет имитировать пройденное время и загружать частицы в нужных позициях. Параметр «Preprocess» отвечает за пройденное время, укажем ему так же 7 секунд. За 7 секунд наши частицы проходят расстояние от верха игрового экрана, до низа.

Запустите игру.

Выглядит замечательно, только звезды немного ломаются от масштабирование, давайте уберем возможность случайно изменять размеры частиц.

Если и менять пиксель арт спрайты, то надо в кратное 2. Но мы оставим на 1.

Мы настроили частицы, теперь будем создавать эффект параллакса.
Немного википедии:
Паралла́кс (греч. παράλλαξις, от παραλλαγή, «смена, чередование») — изменение видимого положения объекта относительно удалённого фона в зависимости от положения наблюдателя.
Чем дальше объект, тем медленнее он будет двигаться. К примеру, представьте велосипедиста который едет по проселочной дороге. Вокруг него имеется поля, деревья, на горизонте видно горы.

Горы будут самыми дальними объектами, они будут почти не подвижны, когда дальнее деревья будут медленно двигаться относительно велосипедиста. И лишь трава вдоль дороги будет быстро сменяться, оставаясь позади. Разная скорость объектов будет создавать глубину статичной картинки.
Так и в нашем случаи мы создадим три звездных полей: самые далекие звезды, звезды по ближе, и самые близкие звезды. Частицы у нас уже есть, их нужно лишь продублировать. Кликните по узлу «StarField» и нажмите комбинацию клавиш ctrl+D, создайте две копии.

Мы сгруппируем их в простой узел «Node2D», и назовем узел «BackgroundStarFields» (Background – фон. Фон звездных полей). Поместите туда наши звездные поля.

Следующие, наши звезды не должны перекрывать корабль игрока, поставьте узел фон звездных полей «BackgroundStarFields» выше игрока «Player», помним, чем выше графический узел, тем дальше от зрителя он будет находиться.

Теперь настроем звездные поля, слегка переименуем их в:
• StartFieldBack – далекие звезда
• StartFieldMiddle – звезда по ближе
• StartFieldFront – близкие звезды.

Настроем каждое звездное поле. Здесь вам придется поработать, я буду задавать параметры, вы ищите их самостоятельно.
Начнем с далеких звезд «StartFieldBack». Первое поменяйте текстуру на «star3.png», это самые маленькие звезды.

Далее скорость далеких звезд должна быть очень медленной, приблизительно к 20 единицам. Не забудьте правильно рассчитать время жизни и предзагрузки. У меня вышло 21 секунда. Количество частиц можете убавить или прибавить, я же убавлю до 30.

Теперь настроем самые близкие звезда «StartFieldFront». Здесь картина иная, подгружаем картинку «star1.png», скорость звезд увеличиваем, но не сильно, примерно до 100. Предлагаю убавить количество звезд до 20, они довольно большие, могут занимать достаточно много места.

Эффект параллакса вам обеспечен. Только… вот звезды слишком яркие. Хм… давайте воспользуемся новой возможностью Годота 4. Мы изменим тип узла «BackgroundStarFields» с «Node2D» на узел типа «CanvasGroup». Кликните на узел правой кнопкой мыши и выберете опцию «Изменить тип»

Ищем «CanvasGroup» и меняем тип узла.

В чем же преимущества узла «CanvasGroup»?
CanvasGroup - объединяет несколько 2D-узлов в одну операцию рисования.
Дочерние узлы CanvasGroup рисуются как один объект. Это позволяет, например, рисовать перекрывающиеся полупрозрачные 2D-узлы без смешивания

Теперь настроем полупрозрачность всех звезд разом.
В инспекторе узла «CanvasGroup», он же «BackgroundStarFields», найдем параметр «Self Modulate» (Перев. «Самомодуляция»), он расположен во вкладке «CanvasItem» - «Visibility»

Кликните по нему и в полосе «A» (Альфа-прозрачность) убавьте прозрачность до половины, вы можете подобрать сами прозрачность или даже изменить цвет звезд в других полосках.

Так то лучше, звезды теперь не столь яркие, и не будут смешиваться с другими объектами.

Теперь перейдем к третей задачи, настроем границы нашего игрового поля. На этот раз, мы будем ограничивать пространства именно игрока, и делать это программно, без статичных узлов столкновения.
Перейдите в скрипт игрока «Player», мы направимся прямо к функции «player_movement». Для отслеживание пределов игрового экрана нам понадобиться функция «get_viewport_rect()».
get_viewport_rect() - возвращает границы области просмотра в виде Rect2 (прямоугольника).


Далее мы будем отслеживать не выходит ли корабль за пределы (viewport - видового) экрана по левую и правую сторону. Начала игрового экрана по Х равняется 0, когда границы справа мы будем получать от ширины (size - размер), он равняется 240 пикселей, но метод ниже будет определять самостоятельно.
И если корабль все же соприкасается с границами, мы убираем вектор движение, скорость его будет равняться 0

Тоже самое провернем и с высотой экрана, на этот раз берем ось Y.

Убрать скорость не достаточно, мы будем выталкивать корабль за пределы границы. Чтобы игрок не смог снова запускать движение и втискиваться в стену. Впишите такую конструкцию.

Функция clamp задается тремя параметрами: входное значения, минимальное значение и максимальное значение. Эта функция не позволяет выходить за диапазон чисел от минимального до максимального значения. Если функция имела такие параметры как clamp(2, 4, 100), то она бы вернула 4, так как минимальное значение равно 4. А если clamp(102, 4, 100), то вернула бы 100, первый параметр как раз и проверяется на диапазон. Далее мы в ручную указываем позицию корабля, и помещаем в границы игрового поля. Запустите игру и попытайтесь выйти за пределы экрана.

Почему мы ограничивали корабль программно, а не статическими узлами типа «StaticBody2D»? Все это связанно с тем, что у нас будут противники: летящий метеориты или вражеский корабль, и они должны свободно уходить за границы экрана, не сталкиваясь со статичными стенами.
На этом глава заканчивается, следующая глава будет посвящена первому противнику: метеориту.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Демий, Jas6666, SirAndriy, Folka

Godot Engine. Проект "Космический шутер" 1 год 43 мин. назад #129151

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
6. Метеорит

Мы добрались до изготовление первых противников. Мы создадим три типа противников: метеорит, астероид и вражеский корабль. Чем же они будут отличаться?
• Метеорит будет двигаться по прямой. Метеорит будет иметь не столь сильную прочность, по уничтожение метеорита, будут анимация распада (сделаем на частицах)
• Астероид будет схож с метеоритом, за исключением что он будет распадаться на метеориты при уничтожение.
• Вражеский корабль будет самым опасным. Он будет двигаться в направление игрока, но с отклонением, а также вражеский корабль будет совершать атаку на корабля игрока.
В этой главе мы создадим метеорит.
Создайте новую сцену. За основу возьмите физический узел «Area2D», переименуйте в «Meteor» (Meteor - метеорит), космический противник в случаи поражения корабля, не должен толкаться, он будет проходить сквозь него, лишь фиксируя момент удара. Далее прикрепите к корневому узлу узел спрайта «Sprite2D». В файловой системе найдите рисунок в папке «Assets», нам нужен рисунок «asteroid_small.png» и прикрепите рисунок в параметр «Texture», в инспекторе спрайта.


Так как вы добрались до третьего проекта «Космический шутер», мы будем ускоряться, и пропускать те моменты которые ранее мы подробно расписывали. Это нужно для закрепление вами материала освоения. Если у вас будут сложности вы можете вернуться назад к главам и посмотреть, как мы делали те или иные вещи.
А теперь прикрепите форму столкновения «CollisionShape2D», форму круга думаю подойдет идеально. В параметре «Shape» в инспекторе формы столкновения вы найдете все необходимое.

Далее сохраним сцену. Нам понадобиться особая папка для всех наших противников, там мы и будем сохранять сцены. Создайте папку под названием «Enemies» (Enemies - враги). Далее сохраните туда сцену с метеоритом, название сцены как всегда даст сам Годот.

Поместите сцену с метеоритом в главную сцену с космосом.

Запустите сцену.

Метеорит стоит не подвижно. Как бы мы не стреляли в него, ничего не происходит. Так давайте же заставим его двигаться! Нам понадобиться скрипт который заставит его двигаться, вернитесь к сцене метеорита и прикрепите скрипт, сам же скрипт сохраните в папке с противниками «Enemies».

Для движения нам понадобиться направление (вектор движения) и скорость, желательно чтобы скорость можно было настраивать. Создадим вектор направления, поместим в переменную, точно так же, как и со снарядом. Только наш снаряд будет двигаться вниз, по оси Y он должен увеличиваться.
Далее создадим переменную скорость. Эта переменная будет хранить текущую скорость метеорита.

Скорость мы будем настраивать иначе, случайно. У нас будет диапазон минимальной и максимальной скорости, а простой рандом будет выбирать случайное значение скорости и задавать его метеориту. Минимальная и максимальная скорость будет настраиваемой.

Далее зададим случайную скорость прежде чем запустим наш метеорит в космос. Придется воспользоваться встроенной функцией.

Функция «randf_range()» возвращает случайное значение с плавающей запятой (дробное значение) из полученного диапазона (от минимального до максимального значения).
Далее скопируем движение со скрипта снарядов, возьмем функцию обновления и все что там заложено.

Запустите главную сцену с космосом.

Метеорит движется.
Давайте еще добавим эффект вращения, чтобы метеорит случайно поворачивался во время полета. Создайте еще одну переменную «rotationRate» (Rotation – вращение, Rate – скорость. Переводиться как скорость вращения), отвечающая за скорость вращения, внезапно.

Теперь так же зададим случайность и вращения. Создадим еще две экспортируемые переменные, вложим туда значения, отвечающие за градусы (обыкновенные числа)

И так же назначим случайный угол поворота используя функцию «randf_range()».

Теперь установим угол вращения нашему метеориту.

Свойство «rotation_degrees» - поворачивает в градусах объект относительно родителя узла. Из геометрии мы знаем про повороты и градусы.
В годоте есть еще один метод отвечающий за вращение объекта, но он использует значения радиан. Радианы мы не будем использовать.
Мы случайным образом выбираем куда вращаться, если число положительное, вращение объекта пойдет по часовой стрелки.
Иначе если отрицательный угол, против часовой стрелки.
Умножение на дельту урезает значительную часть угла, зависит правда от самого устройства.
Теперь решим проблему с выходом объекта за экран, как и снаряды, для избежание переполнения памяти устройства мы будем уничтожать метеорит.
В сцене с метеоритом, прикрепите узел «VisibleOnScreenNotifier2D», узел, уничтожающий объекты, вышедшие за экран… точнее, функция которая реагирует на объект, вышедший за экран. Не забудьте настроить пурпурную форму.

Теперь ваша зада обратиться к сигналу этого узла и подключить сигнал «screen_exited()» к скрипту родительского узла «Meteor». Если у вас будут затруднение в этом деле перейдите к главе «Плазменный снаряд», подобное уже делалось. Вот что у вас должно получиться.

Метод «queue_free()» уничтожит объект, как только сработает сигнал (функция).
Глава получилось не большая, в следующей главе займемся взаимодействий снарядов и метеора.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Последнее редактирование: 1 год 42 мин. назад от Doctor_Bug.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Демий, Jas6666

Godot Engine. Проект "Космический шутер" 11 мес. 3 нед. назад #129169

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
7. Взаимодействие объектов

В этой главе у нас три задачи
• Создать механику взаимодействия снаряда с метеоритом
• Создать механику урона метеорита кораблю игрока
• Проработка неуязвимости корабля игрока
Первым делом нам нужно открыть сцену со снарядом, перейдите в файловую систему и кликните два раза по файлу «plasma_projectile.tscn» в папке «Projectile», это и есть наша сцена со снарядом. На случай если вы закрыли сцену со снарядом.

Корневой узел этой сцены является «Area2D», область которая физически не сталкивается с другими объектами, но реагирует, когда попадает другой объект внутрь. У этого узла имеется достаточно много сигналов.

В проекте «Pong» мы так же использовали узел «Area2D» и использовали сигнал «body_entered»
body_entered( Node2D body )
«body_entered» - испускает сигнал, когда полученное тело входит в эту область. body может быть PhysicsBody2D (физическим телом) или TileMap (тайлмапом, в будущем разберем что это).
В проекте «Pong», у нас был физический объект шарика узел «KinematicBody2D» (В новой версии его переименовали в узел «CharacterBody2D»), так что для это объекта было актуален сигнал «body_entered»
Но наши противники будут иметь физические тела виде областей (Area2D), и потому нам понадобиться другой сигнал.
area_entered ( Area2D area )
Испускает сигнал, когда полученная область(area) входит в эту область (area). Причем мы сможем еще и получать данные о вошедшей области из аргумента «area».
Перейдите к сигналу узла «plasma_projectile» и прикрепите сигнал к скрипту этого узла.

Как только прикрепите, то вот что должно у вас получиться.

Временно пока оставим этот скрипт и перейдем к скрипту самого метеорита.
Наш метеорит, как и другие прочные объекты, будут иметь свой запас прочности. Так получилось, что в играх часто используют параметр «Life» (Life - жизнь), для определение прочности объекта, иногда используют другой параметр под названием «Hit Point» (Очки жизни). Мы возьмем параметр «life», это переменная, которая будет хранить запас прочности метеорита, если эта переменная станет меньше или равна 0, то метеорит уничтожается. И мы так же сделаем переменную экспортной, для будущей корректировки данных. Добавьте переменную в скрипт метеорита, и назначьте 10 единиц здоровья.

Получается нужно попасть в метеорит десять раз, чтобы уничтожить его.
Теперь создадим специальную функцию которая будет отвечать за полученный урон. В ней мы будем уменьшать жизнь, на определенное количество единиц (урон корабля игрока) и еще проверять не уменьшилось ли жизнь метеорита до 0 или и того меньше, в случаи чего уничтожаем метеорит.

В локальную переменную «amount» будет помещаться количество урона, которое будет приходить от снарядов. Далее идет сокращение «life -= amount» здесь идет простая формула, если показать условна она выглядит так:
life = life – amount
Далее идет проверка на количество жизни, если жизнь меньше 0 или равно 0, то мы уничтожаем объект.
Так что созданную функцию «damage()» мы будем вызывать тогда когда захотим нанести повреждения метеориту.
Теперь вернитесь к снаряду, к скрипту, к сигналу «_on_area_entered(area)». Мы обратимся к аргументу «area», словно мы попали в метеорит или иной другой объект, далее обратимся к функции «damage()» и установим урон в 1 единицу.

Здесь правда кроется одна проблема. Функция «_on_area_entered(area)» будет срабатывать каждый раз когда в неё будет входить область, а что если снаряд будет пересекаться с другими объектами, с такими вещами как различные предметы усиления (мы будем их реализовывать в следующих главах), то вызов не существующий функции «damage()» выдаст ошибку и Годот завершит игровую сессию.
Для решение этой цели мы прибегнем к группам, мы будем указывать специальную группу для разрушаемых объектов. А дальше будем отслеживать попал ли объект с нужной группой в область.
Снова перейдите к сцене с метеоритом, задайте группу корневому узлу «Meteor». Для этого перейдите к группам.

Теперь в текстовое поле мы впишем название группы «damageable» (Перев. - повреждаемый), и нажмем кнопку «Добавить», тем самым указав что этот объект относиться к этой группе.


Теперь проверим на условие наличии группы, снова вернитесь к скрипту снаряда, пропишите такую конструкцию.

Метод «is_in_group» проверяет объект на принадлежность к группе. Если объект в находиться в назначенной группе, выдаст истину. А нанесения урона просто сдвигаем вперед используя кнопку «Таб» на клавиатуре, тем самым помещая её в условие.
Запустите игру, атакуйте снаряд до тех пор, пока он не исчезнет. Если вы все сделали правильно, метеорит исчезнет.

Правда пули проходят сквозь объекта, но и это легко поправимо. Просто после нанесение урона метеориту, уничтожайте сам объект снаряда.

Вторая задача: взаимодействия метеорита с кораблем игрока.
Как у метеорита имеются жизни, так и корабля должен быть тот же параметр.
Зайдите в сцену с игроком «Player», найдите скрипт игрока. Добавим переменную «life», параметр экспортируемый.

И этот параметр будет отвечать за проигрыш игрока, в случаи если жизни уменьшится до 0 или и того меньше, игрок проигрывает, корабль уничтожается.
Мы возьмем функцию уничтожения «damage()» из скрипта метеорита и вставим его в скрипт игрока.

Но… прежде чем мы воспользуемся им, давайте кое-что добавим, временно. Это поможет нам понять в будущем некоторые тонкости.

Позже вы поймете для чего они нужны.
Мы прописали функцию повреждения корабля. Как и со снарядами, мы должны привязать корабль с метеоритом, а точнее, метеорит будет посылать сигнал кораблю в случаи если тот его заденет.
Нам нужен сигнал метеорита. Но тут есть одна поправка, нам нужен сигнал «body_entered()» а не сигнал «area_entered()». Помним, сигнал «area_entered()» предназначен для вылавливания областей, а «body_entered()» предназначен для вылавливание физических объектов таких как «CharacterBody2D» (объекты с которым можно столкнуться). Как только коснётся корабль игрока область метеорита, то тот пошлет нужный сигнал.
Что же давайте привяжем сигнал «body_entered()» к скрипту метеорита. Перейдите в сцену с метеоритом, выберете сам метеорит и найдите соответствующей узел и привяжите его к скрипту этой сцены.

Смотрите какой аргумент передает сигнал, если в сигнале областей нам передавался аргумент «Area» (Область), то тут передается «Body» (Тело). Обратимся к телу и вызовем функцию повреждения.

Запустите игру и налетите на пролетающий метеорит, и вы увидите в консоли сообщение о остатке жизней, у вас будет на одну меньше.

Функция сработала. Ваш корабль получил урон, но есть тут одна загвоздка. Получаете урон только когда корабль соприкасается с областью. И каждый раз, когда корабль будет соприкасаться, он будет терять одну жизнь, при условии, если он будет покидать область метеорита. А если не будет покидать область метеорита, то он запросто может сёрфить на камешке не получая последующие урон.

Следовательно, мы изменим подход к повреждению корабля. Правда нам понадобиться переменная и дополнительный сигнал. В переменную мы будем помещать сам корабль, все это мы разместим в скрипте метеорита. Создайте переменную и назовите её «ShipInArea», разместим внутри «null»
Null – это нулевой экземпляр, по сути он указывает отсутствие каких либо других объектов.

Далее мы назначим эту переменную «body» (тело) кораблём игрока. Пока что сотрем функцию повреждения.

Логика такая, корабль входит в метеорит, переменная «ShipInArea» (Ship - корабль) назначается самим кораблем, корабль покидает область метеорита, переменная снова назначается нулевой экземпляр. Значит нам понадобиться сигнал выхода физического узла из области. За подобный функционал отвечает сигнал «body_exited()», сигнал который будет каждый раз включаться когда корабль будет покидать область противника.

Подключите сигнал «body_exited()» к узлу метеорита. И впишите нулевой экземпляр переменной «ShipInArea».

Мы прописали сигналы, теперь мы будем запускать условие которое будет наносить повреждение кораблю. Мы пропишем условие во встроенной функции обновления, только здесь нам не понадобиться точный фпс, мы воспользуемся функцией «_process».

Это условие всегда будет проверятся, и если переменная «ShipInArea» будет хранить наш корабль, то мы будем наносить повреждение кораблю. Корабль вошел в область противника, переменная заполняется кораблем, корабль получает урон, корабль вышел из области противника, переменная становиться пустой, корабль не получает урон.
Запустите игру

Как только корабль коснется метеорита, так тут же будет уничтожен, хотя казалось у корабля 3 попытки. Но на самом деле они как раз все разом и прошли, просто система проматывает код столкновения очень быстро, приблизительно 60 кадров в секунду, за три кадра, все попытки улетучились. Не беспокойтесь, так и положено быть.
Но как быть дальше? Здесь и начинается третья задача, будем использовать временную неуязвимость. Мы внедрим таймер, который будет отсчитывать время следующего урона кораблю. Иными словам, получив повреждение корабль, будет наделен неуязвимостью, на определенное время, при условии, что сам корабль будет в зоне поражения противника. Далее будет включаться счетчик, он будет не большой, думаю в приделах 0,5 секунд (в 1 секунде 1000 миллисекунд), когда он будет завершаться, корабль получит дополнительное повреждение и так до тех пор, пока корабль не уничтожиться.
Что же давайте начнем, добавьте узел таймера «Timer» в сцену с кораблем и переименуйте узел в «InvincibilityTimer» (Invincibility – непобедимый, Timer – время. Перевод: «Таймер непобедимости»).

Далее, как и с таймером задержи выстрела огня, мы настроим опцию единожды запуска таймера. Перейдите в инспектора таймера «InvincibilityTimer» и включите опцию «One Shot». Остальное оставьте без изменения.

Перейдем к скрипту корабля и добавьте переменную, которая будет хранить узел таймера непобедимости. Переменную назовем «invincibilityTimer», сразу туда поместим наш таймер простым перетаскиванием.

Еще нам понадобиться переменная хранящая время в секундах. Время, за которое будет наноситься последующий урон кораблю. Назовем переменную «damageInvincibilityTimer» (Damage – урон. Переменная будет переводиться как «Таймер неуязвимости к урону»). Эта переменная должна быть экспортируемая для будущих настроек. По умолчанию зададим переменной 0,5 секунд.

Так что когда игрок получит повреждение у него будет пол секунды неуязвимости.
Теперь модифицируем нашу функцию получение повреждения «Damage()»

Давайте разбирать каждую строчку. Первое мы запускаем условие, если таймер идет мы выходим из функции с помощью слова «return». Метод «is_stopped()», возвращает истину, если таймер завершен или не включен. А восклицательный знак перед переменной с таймером указывает на отрицание выражения. Можно прочесть эту строчку (70 строчка) так: если таймер включен и работает, прекращаем выполнение функции повреждения.
Следующая строчка запускает таймер (метод «start» - запускает таймер), равная в задержку указанная в переменной «damageInvincibilityTimer» в пол секунды.
А теперь запустите игру. Подставьтесь под метеорит, посмотрите, как корабль получает урон.

Таймер идет, но с задержкой. Вы можете подкорректировать время неуязвимости, если хотите. При этом, важно помнить, когда корабль покидает зону поражения и снова влетает в противника, счётчик неуязвимости действует. Особенно заметно если сделать к примеру, на 2-3 секунды неуязвимости.
Нам осталось сделать пару вещей. Первое противнику надо определять, а действительно ли идет повреждение кораблю игрока или другому объекту? Можно было конечно сделать проверку через группу, но мы пойдем другим путем. Мы дадим скрипту названия класса, и он станет глобальным.
Ключевое слово «class_name» - определяет скрипт как глобально доступный класс с указанным именем. А далее мы сможем проверять в других скриптах принадлежит тот или иной объект глобальному скрипту класса.
Начнем, в скрипте игрока «Plaeyr» разместите следующую строчку:

Далее перейдите к скрипту метеорита, нам понадобиться функции где проверяется входит или выходит в сам метеорит объект корабля (ну или другого объекта). Укажем в той и другой функции условие что объект принадлежит классу игрока. За проверка принадлежит тот или иной объект к чему-то или кому-то отвечает оператор «is»

Мы создали условие и поместили туда привязку корабля к переменной «ShipInArea». В условии мы так и спрашиваем, принадлежит ли полученное тело (body), к классу Игрока (Player). Обратите внимания, класс игрока стал глобальный, нам не надо искать путь к самому скрипту игрока чтобы его объявить.
Как таковой механика игры особа не изменилось, но мы предотвратили случайное воздействие на другие объекты.
И последний момент. Мы прикрутим анимацию щита, он будет показывать временную неуязвимость корабля при повреждении. Перейдите к сцене с игроком и прикрепите узел «AnimatedSprite2D» и переименуйте его в «Shield» (Shield - щит).

Теперь вам предстоит проделать тот же путь что и в главе «Космический корабль». Вам нужно будет в анимационный узел «Shield» поместить анимацию «shield_ship.png», которая расположена в папке «Assets».

Анимация (картинка) «shield_ship.png» идентична анимации «ship.png». Даже не смотря что размеры немного с щитом больше чем без с щита, анимация ляжет ровно. Ведь кадры центрируются в игровом мире.

Дерзайте, проработайте и загрузите самостоятельно анимацию! Смотрите только то что связанно с анимацией.
Вот что у вас должно получиться:

Я временно спрятал анимацию корабля и его форму столкновения.
Давайте перетащим анимация ближе к анимации корабля. Форму столкновения можно спрятать, она никуда не исчезнет, хоть и будет невидимой.

Анимация корабля должна быть выше, так как анимация щита корабля имеет полупрозрачные пиксели и через них видно сам корабль. Запустите игру, посмотрите, как переливается анимация щита на корабле.

Далее перейдем к кодингу.
Откройте скрипт корабля, добавьте переменную «shieldAnim», в этой переменной будем хранить узел анимации.

С помощью встроенной функции «_ready()» мы будем выключать видимость щит/неуязвимость.

Метод «visible» отвечает за видимость. Если что видимость расположена здесь в инспекторе каждого узла, ну или почти каждого.

Да кстати, давайте дополним функцию анимации корабля, закинем туда еще пару анимацией.

Даже если анимация будет скрыта, подобная запись не вызовет ошибку, но будет работать, когда будет действовать щит.
Теперь сделаем видимость щита, когда будет получено повреждение, в это же время будет действовать неуязвимость.

Но как теперь выключить щит? По идеи таймер неуязвимости заканчивается и в этот момент мы должны скрывать щит. Благо и на этот случай есть сигнал, сигнал таймера, когда он оканчивается. Обратимся к таймеру неуязвимости «InvincibilityTimer», нам нужен его сигнал под названием «timeout()» (окончание времени). Подключите к основному скрипту.

Теперь скрываем наш щит неуязвимости в этой функции (сигнале). Как только таймер закончиться щит скроется.

Первое прикосновение с запуском щита неуязвимости.
Что же на этом глава заканчивается, глава была продуктивной, мы создали взаимодействие снарядов с метеоритом, метеорита с кораблем игрока и эффект неуязвимости с привязкой щита. В следующей главе займёмся интерфейсом очков жизни, щита. Так же проработаем предметы которые можно будет подбирать и временно улучшать себя.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Kerotan, Демий, Jas6666

Godot Engine. Проект "Космический шутер" 10 мес. 1 нед. назад #129365

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • ВетеранПрограммист Ruby3 место в КодировкеПроект месяца 1 место3 местоПроект месяца 3 место
8. Интерфейс и предметы


В этой главе у нас три задачи:
• Создать интерфейс: жизни, защиты и усиления.
• Создать механику подбора взаимодействующих предметов.
• Привязать предметы с игровой механикой
Давайте разместим жизни корабля с левой стороны, в правую сторону будет прижиматься иконка щита, она будет активироваться, когда корабль будет получать повреждения или подбирать предмет щит дающий временную защиту. Правее от иконки щита расположим иконку усиления, которая будет давать временно дополнительные плазменные пушки (те самые пушки с левого и правого борта корабля).
Приблизительно будет выглядеть так:

Максимальное сердец на экране будет 5, так же я дополнил иконку усиления и обновил графику космической тематикой. Скачать графику сможете в самой первой главе

Начнем! Создайте новую сцену, за основу сцену возьмем узел холста «CanvasLayer», этот узел создает слой холста, который можно регулировать по отношению к зрителю. Мы можем отображать холст выше остальных элементов, здесь будет интерфейс, здесь же будут располагаться сердца, щит и усиление. Переименуйте узел в UI.
UI (от англ. user interface) — пользовательский интерфейс.

Давайте сохраним сцену, создадим для неё папку под названием «UI» и с тем же названием сохраним внутри неё содержимое.

Далее нам понадобится узел для контроли экранного пространства, пользовательский узел «Control». Он будет заполнять весь экран и хранить все элементы интерфейса. Не забудьте выставить «Полный прямоугольник» - для заполнения пространства экрана.

Для отображение статичных (не анимированных) сердец в прошлый раз мы использовали пользовательский узел «TextureRect», но в этот раз мы отойдем от канонов разработки интерфейса Godot. Наши элементы интерфейса анимированные и имеет спрайт шиты (рисунок, содержащий кадры анимации). Мы привяжем спрайт анимации. Но тут есть одна деталь, для дальнейшей обработки сердец нам понадобится дополнительный узел.
Снова добавьте пользовательский узел «Control» и переименуйте в «Hearts» (перев. Сердца). В этот узел будут помещаться анимированные узлы.

Следующие нам понадобиться узел анимации «AnimatedSprite2D», поместите его в узел «Hearts» и назовите его сердцем в единственным числе «Heart».


Теперь вам нужно создать новый «SpriteFrames» (Спрайт кадров) в инспекторе этого узла и загрузить картинку «heart.png». Знайте в картинке 4 кадра. Проделайте эту операцию самостоятельно. Вот что у вас должно получиться.

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

Ну а теперь давайте обсудим как у нас будет работать механизм. Первое максимальное значения сердец будет 5 штук. Мы не будем их добавлять и удалять во время игры узлы анимации сердец, мы будем их скрывать. А это значит, что мы заранее создадим пять копии анимированных сердец. К сожалению, среди пользовательских узлов, те что отвечают за интерфейс, нету узла воспроизводящий анимацию. Хотя это позволило бы более динамично настраивать расположение сердец. Поэтому нам придется в ручную расположить созданные копии на игровой границы игры. Так, для начала будем центрировать с угла сердечко. Раскройте вкладку «Offset» (смещение) и выключите опцию «Centered» (Центрирования). Один хороший друг посоветовал использовать другой метод, но его изучим в следующем проекте.

Далее создайте дубликат сердца через горячие клавиши ctrl + D. Затем на свежо созданной сердечки в параметрах «Offset», что по оси Х, сместите вправо на «Размер сердечко» и плюс промежуток между сердцами в 2 пикселя. Размер иконки сердца составляет 16 пикселей, итого нужно будет смещать на 18 пикселей.

Создайте дубликат второго сердца, так же сместите вправо еще на 18 пикселей. Чтобы в ручную не высчитывать смещение, мы просто будет прибавлять внутри параметры смещения на 18 пикселей, годот довольно умный он сам посчитает.


Затем добавьте еще два сердца и тем же способом расположите их.

Теперь сместим сам узел, хранящий сердца, чтобы сердца не прижимались к стенки. Выберете узел «Hearts» и найдите вкладку «Transform», там расположены позиции узла, сдвиньте по два пикселя от края.

Запустите сцену (не игру), проверьте как бьются ваши сердца. =)

Следующие иконка «Щит», добавьте новый анимированный узел, на этот раз привяжите его непосредственно к узлу «Control» и назовите узел «Shield».

Конечно вы могли с дублировать узел сердца, но не забудьте создать новый спрайт кадров и после загрузить картинку щита «Shield.png». В любом случаи загрузите картинку щита.

Так же отключите опции центрирования и поместите в правый край экрана. И сдвиньте на два пикселя справа и с верху, тут подбирайте свои оптимальные координаты.

Не забудьте включить авто воспроизведение и так же настроить скорость на 9 FPS. Все должно работать в одном ритме.

Не останавливаемся и создаем еще один анимированный узел, это будет усиление. Назовем «Power» (Мощность), и так же привяжем к узлу контроля.

Как с сердцами и щитом вам нужно загрузить анимированный спрайт «power_up.png», убрать центрирования. Далее разместите левее значка щита, примерно на той же высоте. Так же укажите авто воспроизведение анимации и установите скорость кадров на 9.

Сцена у нас готова, сердца есть, щит есть, усиление есть.

Переходим к следующий задачи, создание предметов взаимодействия (жизнь, щит и усиления).
Под предметами взаимодействия я имею виду предметы, которые можно подбирать во время игры и которые дают определенные вещи. Давайте перечислим все предметы которые у нас будут.
• Предмет «Shield» - будет включать щит неуязвимости на определенное время
• Предмет «Life» (Жизнь) – будет давать дополнительную или восстанавливать утерянную сердечку
• Предмет «Power» - позволит воспользоваться дополнительными пушками. Будет включать на время действия пушек
На этот раз мы сделаем базовый предмет, а далее будем унаследовать предметы и создавать на его основе новые. Наследоваться будет не только сцена, но и скрипт. Здесь затрагивает тема ООП (Объектно-ориентированное программирование).

Приступим создание базового предмета. Создайте новую сцену.
За основу сцены будет узел «Area2D», так как мы будем налетать на предмет для его подбора. Переименуйте его в «BaseItem» (Базовый предмет). Сразу же узлу привяжите узел простого спрайта «Sprite2D», туда будем помещать картинку.

Возможно вы уже догадались какую картинку мы будем использовать для предметов. Поместите файл рисунка «items.png» в параметр текстуры. Далее раскройте вкладку «Animation».

Наши предметы имеют фиксированный размер в картинке, каждый кадр имеет размер 16 на 16 пикселей. Нам будет удобно разрезать спрайт шит (спрайт с раскадровкой) на 3 частей. Такие параметры как «Hframes» и «Vframes» нарезают кадры по горизонтали (H - horizontal) и по вертикали (V - vertical). Нам нужно нарезать спрайт по горизонтали на 3 штучки, выставите значения параметра «Hframes» на значения 3. Параметр «Frame» как раз позволит выбирать нужный нам спрайт. Удобно правда?

Выставите любой кадр какой пожелайте для базового предмета, сейчас это не имеет значения. Теперь прикрепите узел коллизии «CollisionShape2D» выберете форму круга и настройте область под спрайт картинки.

Теперь сохраним сцену, создадим специальную папку для предметов, назовем папку «Items» (Предметы) и сохраним базовый предмет туда, как и последующие предметы которые мы будем создавать.

Создадим скрипт и поместим в ту же папку что и базовый предмет.

Первое укажем глобальный класс базовому предмету, чтобы мы могли получить доступ при наследование в другие классы.

Наши предметы будут двигаться с постоянной скоростью, вы можете конечно сделать свой вариант. Движение схожи как со снарядами, так и с метеорами.
Добавьте переменную скорости, и изменяйте её во встроенной функции физических процессов меняя позицию. Дельта в процесс обязательно.
На всякий случай сделайте переменную экспортной для регулировки скорости.

Предмет будет вылетать с верхней границы экрана потому будем прибавлять по оси Y скорость.
Далее наша область предмета должна реагировать на прикосновения игрока. В нашем случаи, как и с метеоритом, наш корабль имеет физическое тело «CharacterBody2D», а значит у него не область (Area), а тело корабля (Body). Зачем это? Все просто, определяем нужный сигнал нашего базового предмета.
Перейти к сигналом узла «BaseItem» и найдите вхождение тела в область «body_entered()». Подключите его к скрипту.

Функционал проверки вхождение игрока (Именно игрока) схож в скрипте метеорита, с дублируем в наш код.

Так как игрок тоже имеет глобальный класс «Player», то мы запросто можем вычислить именно тело корабля.
Когда предмет будет попадать в область корабля, мы будем уничтожать предмет. Но прежде чем мы добавим удаление предмета мы пропишем функцию «applyItem()» (apply – применить. Дословно «применить предмет») которая будет применять подобранный предмет. Укажем в аргументы этой функции полученное тело нашего корабля.

Да сейчас Godot ругается на неизвестную функцию, но мы это подправим. Добавьте эту функцию, но в ней пропишите простой заглушку, в базовом предмете эта функция ничего выполнять не будет.

Мы почти закончили с базовым предметом, но осталось одна мелочь. Когда предмет будет уходить за пределы игрового экрана, нам надо избавляться от значка. Благо мы подобное уже делали. Добавьте узел «VisibleOnScreenNotifier2D» отвечающий за контролирование выхода за экран. И так же настройте область под размер предмета.

Перейдите к сигналам этого узла и подключите сигнал «screen_exited()», к скрипту базового предмета. Как только поступит этот сигнал, значит предмет вышел за пределы экрана, удаляем объект.

Мы закончили с базовым предметом, теперь будем создавать на её основе новые предметы, унаследованные. Перейдите к главному меню, выберете «Сцена» далее «Новая унаследованная сцена…»

Выберете сцену базового предмета «base_item.tscn». Нажмите открыть. Обратите внимание каким цветом подписаны узлы, структура осталась та же самая.

Все узлы были унаследованы от предка, и этот узел имеет точно такой же набор свойств что и предок.
Давайте переименуем главный узел сцены с «BaseItem» в «ItemShield». Наш первый предмет щита. Сохраните новую сцену в ту же папку с предметами «Items». Не беспокойтесь Годот переименовывает в змеиный стиль (змеиный_стиль – тип написание названий с нижним подчеркиванием).

Мы унаследовали сцену, но нам понадобиться так же и унаследованный скрипт. Открепите скрипт сцены.


Создайте новый скрипт, но не торопитесь сохранять его, сначала мы найдем родительский скрипт чтобы унаследовать его свойства. Кликните по иконки «Папка».

Выберете файл «base_item.gd». Откройте и сохраните скрипт. И вы унаследуете скрипт.

Вот что получиться

Хм… давайте заменим строку путь на глобальный класс, точнее мы вызовем этот класс.

В любом случаи, будь то строка (путь) или вызов глобального класса, мы наследуем все его методы из класса родителя (скрипта) «BaseItem», а значит у нас есть доступ к функции скрипта. Скопируйте функцию «applyItem» со всем содержимым внутрь скрипта, мы будем заполнять его после.

Теперь ваша задача создать унаследованные сцены «ItemLife» (Предмет жизни) и «ItemPower» (Предмет усиления), аналогичным способом. Вы так же наследуете сцену из базового предмета «BaseItem». Переименовываете в нужные названия (ItemLife, ItemPower) главные узлы, сохраняете в папку «Items». Меняете на нужный кадр спрайта

И так же наследуется и сам скрипт и сохраняете как предлагает Годот (названия скрипта будет браться от названия главного узла сцены). У вас должно получиться четыре скрипта и четыре сцены. Будет работать предыдущий скрипт родителя, дочерние скрипты будут менять лишь только одну функцию.

Каждый скрипт наследует глобальный класс базового предмета, но содержимое в каждом скрипте должно быть одинаковым, так как функцию «applyItem» мы будем прописывать по отдельности на каждый предмет.
Поместите сцену с интерфейсом на главную сцену «space.tscn». Перейдите в сцену с космосом и прикрепите сцену UI

А теперь поместим сюда одну из сцен предметов, но не базовый предмет.

Запустите игру и попытайтесь налететь на предмет.
Скорость предмета великовата, можете убавить на свое усмотрение, я выставлю значение скорости 30.
Выглядит не плохо.

Переходим к последней задачи, пропишем взаимодействие предметов с игровой механикой.
Первый наш предмет является щит. Механизм щита, он же неуязвимость, у нас уже сделан. Но мы немного модифицируем механизм. Перейдите в скрипт игрока «Player», найдите функцию получение урона «damage». Обратите на строчку 80 и 81 в скриншоте (у вас может отличаться строчки).

Эти две строчки запускают таймер, а аргумент внутри функции «start()» устанавливает таймер в секундах. Мы можем их вынести в отдельную функцию и использовать при получение урона или включение неуязвимости. Давайте так и поступим, функцию назовем «applyShield()» (Применить щит) и поместим туда эти строчки. С аргумента функции будем принимать время установки запуска таймера. А переменную «damageInvincibilityTimer» заменим на полученный аргумент таймера «time».

Теперь мы можем вызвать функцию внутри функции урона, а переменной «damageInvincibilityTimer» указать время таймера.

Запустите игру. Проверьте работает ли неуязвимость корабля во время прикосновения с метеоритом. Если работает, продолжаем.
Теперь перейдем к скрипту «item_shield.gd», к скрипту предмета щита. Первое нам понадобится настраиваемая переменная, время продолжительности таймера неуязвимости. Пускай будет 7 секунд.

В функцию «applyItem()» поступает аргумент игрока, воспользуемся и вызовем функцию корабля «applyShield()» и укажем продолжительность таймера внутри.

Предмет готов. Вам нужно будет выставить нужный предмет на сцену чтобы про тестить его.

Переместите предмет в поле досягаемости корабля. Запустите игру и возьмите предмет щита.

Щит работает.
Корабль имеет глобальный класс «Player» и потому его видно в любой части проекта. Но использовать переменные глобального класса не получиться, ошибка с нестатической функцией, так как это не экземпляр класса. Следовательно, мы воспользуемся глобальным скриптом, не привязанный какому-либо классу.
Перейдите к скриптам, нажмите на «Файл» - «Новый скрипт»

Назовем глобальный скрипт «global_script», сохраним в папке с игроком, так как большинство вещей будут связаны с кораблем.

Далее подключим наш глобальный скрипт к автозагрузки, чтобы скрипт был доступен из любого места в Godot.
Перейдите в «Проект» - «Настройки проекта» - «Автозагрузка»

Затем нам нужно подключить скрипт, кликните на иконку папки и найдите, и подключите глобальный скрипт.

Годот предложит название скрипта «GlobalScript», но подправим на «Global» и нажмем кнопку добавить. Для упрощение, ведь мы будем часто обращаться к глобальному скрипту.

Скрипт подключен, теперь к нему можно обратиться из любой части Годота.

Добавим первую переменную в наш глобальный скрипт «visibleShield» (видимость щита). Эта переменная будет хранить видимость щита корабля, и через глобальную переменную будет передавать информацию интерфейсу.

Далее нам нужно перейти к скрипту игрока «Player», чтобы добавить функцию следящий за видимостью щита. Мы обратимся к глобальному скрипту и к его переменной и поместим значение видимости щита. Функция «checkVisibleShield» (Check - проверить), будет помещена в постоянный цикл, для проверки.

Теперь поместим в цикл функцию физического процесса.

Изменения интерфейса будет происходить от мониторинга состояния корабля. Перейдите к сцене интерфейса «UI» и прикрепите к нему скрипт.

Здесь в скрипте нам понадобиться все значки, но всё будем делать поэтапно. Сначала загрузим узел «Shield» поместим в переменную с таким же названием.
Функция «get_node()» позволяет загрузить нужный узел по указанному пути в сцене.

Далее нам не обязательно проверять каждый точный кадр в физическом процессе, так что воспользуемся простой функцией процесса. Через глобальный скрипт, глобальную переменную узнаем о состояние видимости щита и будем регулировать каждый кадр видимость значка щита.

Запустите игру, значок щита будет отсутствовать, но будет появляться каждый раз, когда будет активироваться щит.

Переходим к сердцам. Сначала добавим глобальную переменную жизнь «life», перейдите к глобальному скрипту и дополните код.

Добавим функцию изменения жизни игрока. Да у нас уже есть подобная функция, называется «damage()» (урон), но в нем заложен механизм срабатывания щита, так что он не подойдет. Функция назовем «changeLife» (Change – изменить. Дословно изменить жизнь). Функция будет простой. Здесь лишь идет прибавление сердец или сердца, а также мы присваиваем в глобальную переменную «life» измененные жизни корабля.

Перейдем к скрипту «item_life.gd», скрипт который срабатывает, как только игрок подбирает предмет сердца. Здесь, как и скриптом щита, мы создаем переменную «life», она будет экспортируемой, мы можем добавлять и больше одного сердца. Здесь так же применяется заранее заготовленная функция игрока «changeLife», а аргументом будут жизни, которые будут увеличивать переменную жизни игрока. По факту вы можете делать особые сердца которые будут восстанавливать больше одной жизни.

Мы привязали предмет сердца и игрока, с интерфейсом будет сложнее прописать механизм. Дело в том, что мы будем играться с узлами анимации сердец.
Давайте разберем некоторую механику с узлами в Godot Engine, каждый дочерний узел имеет индекс, словно как в массиве (ну или в списке, термин python)

И вызвав родителя узла мы можем применить функцию «get_children()» который соберёт все узлы в массив. Перейдем к скрипту интерфейса «ui.gd», сначала мы обратимся к родительскому узлу «Hearts», что бы после вынуть все дочерние сердца и поместить их в массив. Так же приготовим заранее переменную под будущий массив «array_hearts» (Array – массив. Массив сердец <3).

Затем мы единожды вызовем функцию «get_children()» во встроенной функции «_ready()». Мы лишь раз поместим все будущие анимированные узлы в список. Так как нам не потребуется каждый раз помещать или удалять новые элементы.

Вот что высветит массив «array_hearts», если его поместить в «print()». Массив анимированных спрайтов сердец, спрятаны внутри.

С помощью массива мы можем перебирать и скрывать сердца, если у игрока окажется меньше положенного жизней и наоборот показывать если больше. Но на экране у нас будут максимально высвечиваться НЕ больше пяти сердец. Как и минимально НЕ меньше одного, иначе если меньше, корабль удаляется, игра проиграна.
Для регулирование сердец в интерфейсе мы воспользуемся механизмом Setter-функцией. Мы уже использовали подобный механизм в бонусном проекте по созданию главного меню, но из-за разности в версиях Godot, синтаксис механизма поменялся.
В версии Godot 3.5.1 и ниже механизм сета и гета (setget) выглядела так:

В 4 версии Godot синтаксис слегка изменился, в остальном же все так и осталось.

Мы будем использовать Setter-функцию, эта функция будет запускаться каждый раз, когда значение определенной переменной будет меняться.
Создадим переменную привязанную к сеттеру «ship_hearts» (Сердца корабля) и привяжем сеттер функцию.

Но что мы пропишем внутри функции? Замысел простой, мы просто будем перебирать все немногочисленные сердца, будем скрывать их, а на следующем шаге запускать видимость сердец, через цикл по количеству жизни корабля. Сначала будем скрывать все сердца, заодно будем устанавливать анимацию на нулевой кадр, чтобы анимация сердец всегда была синхронной.

В массиве хранятся анимированные узлы, а через цикл for мы просто перебираем каждый узел. Далее мы вызываем у анимированного узла параметр видимости и устанавливаем на первый кадр (он же нулевой).
Мы можем перебирать анимированные узлы по количеству жизни игрока используя метод «range()» можем создать простой перебор, при чем перебор будет идти от нуля и до указанно числа, не задевая само число. И таким не хитрым способом мы можем запускать нужные по индексу сердца.

В «param» будет помещаться переменная «ship_hearts» отвечающая за текущие количество сердец. А метод «range()» берет только необходимое количество итерации.
Но у нас всего пять сердец, возможно у вас их может быть больше или меньше. В любом случаи мы не должны превышать количество перебора общего количество дочерних узлов. Поэтому мы сделаем проверку, а точнее мы будем задавать диапазон дабы не вызвать ошибку не существующих узлов. Сначала мы создадим простую переменную «max_hearts» отвечающею за максимальное количество сердец.

Теперь определим количество дочерних узлов узла «Hearts» с помощью функции «get_child_count()»

А внутри функции сеттера мы используем метод «clamp()» который контролирует вложенную переменную и не дает выйти за пределы. Первый аргумент этой функции принимает переменную, которую будет контролировать, второй и третий аргумент минимальное и максимальное значение. Когда число попытается выйти за предела диапазона функция «clamp()» вернет число входящие в диапазон.

Теперь чтобы испытать весь наш код, предлагаю задать переменной «ship_hearts» значение «3» и посмотреть, как магическим путем на экране будут высвечиваться 3 сердца.

Три сердца

Global.life – будет связующие между игроком и интерфейсом, и будет передавать количество жизней, сердечек. Теперь выставим значение глобальной переменной.

А в процесс интерфейса поместим условие и будем проверять изменения глобальной переменной жизни, если они поменялись, и мы не превышаем максимально допустимое значение жизни, то мы переписываем локальную переменную жизни, привязанную к интерфейсу.

Снова вернемся к скрипту игрока, немного подправим код. В функцию физического процесса мы поместим небольшое условие, которое будет проверять не изменилась переменная «life» игрока, и если да, то перезапишем глобальную переменную жизни.

Вроде бы все готово, но есть один затаившейся баг. Когда мы будем получать повреждение, сердца будут скрываться, но когда останется последнее сердце, корабль будет уничтожен, а сердце его останется.
Нам нужно указать при уничтожение корабля, что глобальная переменная жизнь равна 0. Заодно подправим видимость щита, так же укажем глобальной переменной видимости щита на ложь, чтобы иконка не зависала на экране после гибели корабля.

Теперь перейдем к последнему предмету, но сперва мы подготовим для него все нужные зависимости. Последний предмет усиление будет запускать дополнительные пушки корабля, на время. Огневая мощь боевого корабля увеличиться в три раза, три пушки, три раза мощнее.
Сначала нам понадобиться таймер. Загрузите узел таймера сцене с кораблем (с игроком) и назовите таймер «FirepowerTimer» (Firepower – огневая мощь).

Добавим в скрипт экспортируемую переменную «firepowerTime» (время огневой мощи) которая будет хранить продолжительность усиления. Выставим 10 секунд.
Так же загрузим узел таймера в скрипт

Загружаете вы вручную узел или используете метод «get_node», это одно и то же.
Далее создадим функцию активации боевой мощи «applyFirepower()», здесь мы будем запускать таймер. Эта функция схожа с функцией «applyShield()», аргументом функции будет время, продолжительность усиление.

Теперь нам понадобятся узлы, отвечающие за правый борт и левый борт корабля. Создадим под них переменные и загрузим их.

Следующий шаг мы будем раскрывать дополнительные пушки при активация таймера, помним мы прописали механизмы активации пушек. Дополним функцию «applyFirepower()»

А когда таймер закончиться мы будем выключать видимость, и у нас пропадут дополнительные пушки. Обратимся сигналу таймера «FirepowerTimer», сигнал «timeout()» и подключим к скрипту «Player».

Впишите левый и правый борт и скройте их, время прошло, пушки перестали работать.

Для эксперимента можете применить функцию applyFirepower(10) в функции «_ready()» временно. Запустите игру, на 10 секунд у корабля появиться дополнительные пушки. Не забудьте потом удалить эту строчку.
Мы создали механику запуска пушек. Займемся последним предметом. Вам понадобиться скрипт «item_power.gd», как и в других скриптах предмета вам следует обратиться к объекту «player» и вызвать функцию «applyFirepower()», заранее мы еще пропишем время продолжительности усиления в экспортируемой переменной «firepowerTime».

Перейдите в сцену космоса и импортируйте предмет усиления

Запустите игру и протестируйте значок.

Свяжем значок с активацией усиления. Добавим в глобальный скрипт переменную, которая будет отслеживать запуск усиления «firepower».

Вернитесь к скрипту игрока, и назначьте глобальной переменной истину при включение таймера усиление, а при завершении таймера ложь.

Обязательно пропишем выключение переменной при уничтожение корабля. Эта переменная будет передавать информацию интерфейсу.

Следующее, перейдем к скрипту «ui.gd», нашего интерфейса. Загрузим последний анимированный узел значок «Power» в скрипт.

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

Запустите игру, протестируйте, все должно работать.

На этом наша большая глава окончена. Мы проделали с вами большую работу: создали интерфейс, подбираемые предметы и связали все эти вещи воедино. В следующей главе мы займемся «Спавном» (Spawn - порождать) противников и предметов. До скорых встреч и хороших разработок.
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Последнее редактирование: 10 мес. 1 нед. назад от Doctor_Bug.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Jas6666
Время создания страницы: 0.524 секунд