Кодовый парусный корабль #2
Всем привет, мой предыдущий по этой теме не был оценен сообществом, потому что он в нем даже не появился. Чтобы понять о чем будет идти речь в этом посте рекомендую сначала ознакомиться с первым: http://pikabu.ru/story/kodovyiy_parusnyiy_korabl_5082490
Сразу ссылка в гит с самыми актуальными файлами для тех, кому не интересны пояснения: https://gitlab.com/open_sourse/pirate (надеюсь, ссылка будет работать)
Итак, в этой части мы объявим несколько простых и понятных методов и научим наш корабль передвигаться и стрелять. Итак, начнем с рассмотрения дополненного класса пушки, который был создан в предыдущем посте:
Первое: мы добавили булевую переменную charged, что в переводе означает "заряжено". Тут все просто, если пушка заряжена, то мы можем стрелять, а если нет - нет. Также можно увидеть пример конструкторов, которые я забыл добавить в предыдущем посте. Помимо этого были добавлены некоторые новые переменные в класс Ship, но их можно найти в гит и посмотреть.
Рассмотрим метод Fire, который возвращает класс Bullet (пуля, ядро - рассмотрим ниже). Опишем его простыми словами: если пушка не заряжена, то не возвращать ничего, а если пушка заряжена, то зарядить её и отдать экземпляр класса Bullet, передав в него урон пушки.
Вот класс нашего ядра. Тут есть поле урона и скорости (скорость полета пока статична). Ну и конструктор. Ничего интересного, в целом.
Итак, перейдем к нашему основному классу, на который мы потратим большинство содержания поста. К слову, класс называется Ship, однако, как вы уже могли заметить ранее, такой класс уже есть, но это совершенно разные классы, ибо путь к ним выглядит так:
1) App.Items.Ship.Ship
2) UniApp.Items.Ship.Ship
Ниже мы рассмотрим второй вариант (тут сразу добавлен вид скрипта в инспекторе):
Первое что мы видим, что данный класс имеет в себе поле App.Items.Ship.Ship, т.е. данный корабль должен принимать в себя общий класс корабля.
Дальше мы видим два списка с векторами. По названию можно понять что это позиции пушек. Они нужны для того, чтобы наши ядра вылетали с относительно реальных точек. Если бы корабль был в 3д, то вместо этих позиций находились реальные модели, хотя мы и сейчас могли добавить реальные модели, но в виде рисунков, ноооо...их нет. Почему? Поймете позже, когда увидите наш облавок.
Дальше идут листы с пулями - для чего они нужны увидите позже. Также есть экземпляр пули из которого будут скопированы остальные пули (в инспекторе просто добавлено в это поле префаб пули). И булевая переменная moving, т.е. - движение. Также увидите позже для чего он нужен.
(ВНИМАНИЕ, это абзац, как и все сущности в нем созданы для отладки и потому будут удалены) Итак, первое что мы делаем при старте игры собираем наш корабль из его составных частей. В предыдущем посте, наверное, была написана суть этого действия, но это не точно.
Также мы добавляем 3 пушки на правую сторону корабля и 5 пушек на левую (для показательности).
Итак, перейдем к основному, что мы должны рассмотреть в данном после - методы.
Первый метод - Fire. Делает понятную функцию, открывает огонь. К слову, сейчас я назначил клавиши для отладки какие-нибудь, однако позже это будет переработано так, чтобы совершенно не касаться этих методов, если мы захотим изменить управление.
Итак, метод разделен на две части: одна срабатывает если нажата кнопка Е, вторая - если Q. Почему так? Один вариант стреляет с левой...мачты...или как это вообще называется? С левой стороны, короче. А второй с правой, потому что пушки у корабля с разных сторон.
Если нажали E, то для каждого элемента листа с правыми пушками, объявленному выше, мы включаем функцию FirePreprocess. FirePreprocess делает следующее: для пушки запускаем метод Fire (App.Items.Ship.Cannon) и если метод возвращает null (а как мы помним, он возвращает null только когда пушка не заряжена), то ничего не делаем, а если возвращает что-то другое (App.Items.Ship.Bullet), то добавляем его в лист ядер, которые мы принимаем из аргументов функции.
Если переменная в методе начинается с _ (нижнего подчеркивание), то это значит, что данная переменная получается из аргументов функции.
Ну что же идем дальше, надеюсь, уже не забыли о чем мы говорили выше, ибо мне пришлось перечитывать, чтобы продолжить рассказ. Итак, когда мы для каждой пушки запустили метод FIrePreprocess, то дальше запускается метод FireProcess, которые берет нужный список ядер и просто создает новое ядро из префаба, объявленного в полях класса и рассматриваемого выше. Рассмотрим метод по строкам, ибо некоторые действия могут быть непонятны:
57. Создаем из префаба ядро, задав в качестве стартовой позиции позицию пушки, которую мы берем из листа позиций пушек.
58. Задаем новому ядру родительский объект, который является нашим кораблем. (это делается для того, чтобы ядро перешло в локальную систему координат нашего корабля).
59. Получаем компонент Bullet у нового объекта (к слову, это не тот Bullet, который был описан выше. Код этого Bullet будет ниже) и устанавливаем ему направление, которое мы принимаем из аргумента функции (там вправо или влево, в зависимости от того из каких пушек мы стреляем).
60. передаем класс App.Items.Ship.Bullet в UniApp.Items.Ship.Bullet.
61. И теперь самое интересное: мы убираем родителя у нового объекта. Логичный вопрос: а на кой черт мы его до этого назначали, а теперь убираем при чем в том же методе? Суть в том, чтобы сначала перевести объект в локальные координаты родителя и назначить ему вектор движения в зависимости от текущего местоположения корабля, а потом убрать привязку к координатам, чтобы движение корабля не влияло на местоположение ядра и, в целом, стало независимо ни от кого.
64. Очищаем лист, ибо мы уже создали все нужные объекты из него.
Фух, жуть, казалось такие простые методы, но объяснить их не так-то и просто, как кажется.
Ах да, обещанный скрин класса UniApp.Items.Ship.Bullet:
Тут есть два поля, которые мы обсудили выше и в функции Update(которая вызывается каждый кадр). Если пуля не назначена в классе, то мы ничего не делаем, а если назначена, то двигаем объект. Зачем нужна проверка на null Bullet-а? Все дело в том, что если класс наследуется от MonoBehaviur, то мы не можем создавать его экземпляры с помощью ключевого слова null, поэтому мы не можем создавать конструктор класса, чтобы гарантировать то, что данное поле будет назначено точно при создании.
Итак, к самому методу. Как вы можете видеть, мы берем направление (только X координату, ибо нам нужно перемещать объект только по X. Там в целом, есть еще свои причины почему сделано так, но не буду распыляться, ибо пост довольно большой выходит. Если интересно отвечу в комментах) умножаем его на скорость и дельта переменную времени, чтобы все было плавно.
Позже тут будет добавлен разгон ядра и его уничтожение, когда он пройдет свой максимальный путь.
Итак, вот как выглядит наша стрельба (этот черный кусок обгорелой деревяшки и есть наш корабль, извините, но рисовать не умею и знакомых, которым было бы интересно что-то рисовать для меня, нет):
К слову, я похоже неправильные пушки назначил, потому что снаряды вылетают с неправильной стороны, но это легко поправить.
Рассмотрим методы движения:
Первый метод Move двигает наш корабль ровно прямо на максимальной скорости (позже будет добавлен разгон) и изменяем переменную moving на true. Это сделано для того, чтобы в дальнейшем можно было поворачивать корабль только когда мы двигаемся...ну, мне кажется именно так ходят корабля, т.е. не могут вращаться на месте(если корабль не весловой))
Второй метод Rotation: суть первой строки только что описали, дальше если нажата A, то вращаем корабль по своей оси в одну сторону, а если D, то в другую. И в конце переключаем moving в false. Таким образом у нас в любом случае moving не останется true, ибо данные методы в Update стоят так:
Тут Fire где угодно можно поставить, а вот Move и Rotation только в таком порядке, по описанным выше причинам.
Вот, к слову, как выглядит наше передвижение:
Ну на этом наши полномочия все. В следующем посте (если этот не соберет гору минусов), мы добавим:
1) Ограничение количества пушек на одной стороне корабля
2) Вылет снарядов с разной скоростью
3) Разгон снарядов при полете
4) Разгон корабля при движении
Ну и может что-то еще
Лига Разработчиков Видеоигр
6.6K пост22.1K подписчиков
Правила сообщества
ОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
- Никакой политики
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"