Сказка о котиках, славянские мифы и зеленый свет
Привет, пикабу! Все знают, что Гринлайт закрывает свои двери, а прошмыгнуть в них стремятся все и вся - никто не хочет остаться 1 на 1 с неизвестностью, которую представляет собой Steam Direct. В моем случае, закрытие гринлайта сыграло мне на руку и послужило стимулом создать нечто, готовое к получению зеленого света в ОЧЕНЬ короткие сроки.
Я выпустил первую игру в Steam 24 февраля. И поставил перед собой цель - до 20 марта подготовить к гринлайту второй проект. Удалось ли мне это и как именно? Смотрите сами:
Выпустив Reflection of Mine я остался в окружении каких-то неясных идей и кучи рисунков, которые делал для ныне мертвых проектов. Я всегда думал, что смогу склепать из этого целостную игру, но вот только бросив на них свежий взгляд понял, насколько же черт возьми сильно все это не сочетается друг с другом:
У всех объектов были разные соотношения, детализация и перспектива. Мои надежды ничего не рисовать для Гринлайта рухнули, и целиком я взять смог только задний план для логотипа, который делал когда-то для другой игры. Не думаю, что это сэкономило мне время, потому что сделал я его тогда за полдня. За такой же срок я сделал и задний план для верхнего правого скриншота, а героиню для предыдущей игры один фиг перекрасил и переделал лицо на всех 180-ти спрайтах.
Но такой провал дал мне и другое преимущество: теперь я был волен творить с сюжетом что угодно, ведь я не ограничен уже отрисованным миром. Мне сто лет назад хотелось сделать игру с котиками (потому я так дофига их нарисовал в свое время), и я начал штудировать про них литературу. Для создания Reflection of Mine я прочитал 4 книги о синдроме множественной личности и кучу статей о том же. Почему второй проект должен быть исключением?
Сначала глаз пал на ведьм, деятельность которых была прочно связана с котами. Получив от жены в подарок книгу "средневековые процессы о ведьмах" (на дореволюционном языке), наигравшись для вдохновения в Sega и изрядно закинувшись элем с привкусом Англии, я сел за книгу и сценарий к игре. Я собирался выстроить сюжет вокруг ведьм и колдовства, но малость офигел уже на первых страницах.
Ребят, вы знали что ведьмы ну просто супер-мерзкие? Я привык видеть ведьм на косплей-фестивалях, но этот сексуальный образ разрушился в миг. Например, свой колдовской порошок они делают из толченных костей младенца и слюней жаб, а метлу заставляют летать, смазав ее кровью ребенка.
Надежды создать что-то милое рухнули. Я получил кучу идей для хорроров, но для милой пиксельной игры эти знания не годились.
Протрезвев, я начал дальше искать информацию о котах и мифологии. И вычитал вот что. Кошка у славян считалась проводником в потусторонний мир и имела девять жизней. Зацепившись за эту информацию, я начал штудировать литературу по славянской мифологии, и нашел мощнейший источник вдохновения.
Например, у славян существовало поверье, что если обмазаться тирлич-травой, то можно превратиться в оборотня. Чем не игровая механика, вынуждающая игрока искать эту траву ради того, чтобы обернуться зверем? А легенды о разрыв-траве вообще смогут поспособствовать появлению нового оружия в игре! А самое главное - ведьмы и колдуны не считались у славян какими-то гадкими тварями, заслуживающими костров инквизиции. Были не ведьмы. Были волхвы.
Чего стоит, кстати, один только славянский бестиарий. Т.к. у меня был отрисован задний план для локации "болото", я искал, какие существа по верованию славян там обитают. Одним из таких тварей оказался Болотник, который поджидал путников на специальном месте, называемым Чаруса. Чарусой назывался участок на болоте, усеянный цветами и светом, но ведущий в трясину.
Придумывать монстров исходя из этих мифов стало истинным удовольствием. Так, например, я прочитал про Боровика, который якобы управлял всеми грибами. Что если по сюжету его разозлить? Как он будет сражаться с обидчиками? Конечно с помощью все тех же грибов - они же, по сути, одно целое!
Если вспоминать старые сказки, то на ум сразу придет Леший. Это существо считалось враждебным для человека. Кое-где представляло собой старика, а на некоторых изображениях какую-то лохматую нечисть и умело летать. Я был близок к тому, чтобы запутаться в поверьях, и изобразил Лешего таким, каким представлял его себе в детстве:
В общем, нарисовав еще домовых, летающих анчуток, я остановился, когда понял, что все созданное мной помогает мне делать скриншоты, а вот создать трейлер - едва ли. Располагая информацией о том, что у котов девять жизней, я решил выстроить сюжет вокруг этого. Мотивом множества игр является спасение близких, и я не стану исключением. Только вместо принцессы или принца я возьму того, кто действительно дорог и незаменим - мать главной героини. Не знаю, что может быть печальнее такой утраты, и что может сильнее мотивировать пуститься во все тяжкие ради ее спасения. В мире, где у котов девять жизней, должен существовать кот, который отдаст хотя бы одну жизнь матери главной героини. Она достучится и до Велеса, и до выдуманного мной кошачьего бога, лишь бы вернуть родную мать. Эта завязка нравилась мне еще и тем, что Expecte Amour, который пишет мне музыку, восхитительно справляется с драматичными партиями и мы вместе явно сделаем трейлер мощнее.
Намекнув на сказочность происходящего тем, что с неба обрушился мистичный клубок, я столкнулся с проблемой: дом, который я хотел показывать и который я нарисовал три года назад, не похож на то, что стоит пихать в трейлер.
В ночь, когда я взялся за этот дом, я лег в 6 утра, но в итоге выдал две версии полуразрушенной развалины с просевшей крышей, которая далась мне с потом и кровью:
Основной трудностью придуманного мной сценария (показать 18 лет жизни с матерью) являлась невозможность сменить сцену - я планировал 2 минуты показывать один и тот же дом. Лучшим способом и течение времени показать, и пользователя не усыпить без смены декораций, было проанимировать всё и вся. Я сделал динамичный свет, смену дня и ночи, смену сезонов, проанимировал то, как распускаются и опадают листья.
Это странно, но особую боль мне доставил этот чертов куст рядом со входом - я постоянно забывал прикручивать ему новые анимации, менять им скорость, и он вечно зеленел, если шел снег, и светлел, когда наступала ночь. Я бы мог в 2 раза быстрее сделать этот ролик в Adobe After Effects, но предпочел не тратить время на то, что все равно придется переделывать, и сразу накидал все на движке. В Construct2 благодаря прикольной функции Wait удалось написать целый сценарий.
Самое прикольное, что когда как на ютубе этот ролик весит больше трех сотен метров, сам проект С2 весил не больше трех мегабайт и такое долгое вступление, по факту, вообще невесомо.
Для динамичного света от молний я использовал маску нормалей, которую сделал с помощью бесплатного фильтра от Nvidia. Честно сказать, буду рад, если кто подскажет более красивый и эффективный способ создания нормалей:
Было 19ое марта, я боялся, что Гринлайт вот-вот закроют и в два ночи сидел судорожно монтировал трейлер, используя вместо музыки и звуков какие-то заглушки из интернета. Какого же было мое разочарование, когда я понял, что мне катострофично не хватает кадров геймплея. Дело в том, что я не собирался склеивать заскриптованные сцены, я действительно потратил множество времени, чтобы проработать поведение монстров, воткнутых в ролик буквально на пару секунд. Для быстрого создания чего-то нового всегда прибегаешь к старым знаниям, и благо я когда-то уже кодил полет на метле. Создания этой сцены из перерисованного неба и старых облаков заняло где-то полтора часа. Основную часть времени заняла проработка поведения вот этих анчуток, которые все время стремились слиться в один спрайт и вести себя одинаково. Если кто работает на Сonstruct2, то меня спасло то, что я вручил им поведение Platform, выкрутил гравитацию на ноль, адекватно настроил маску коллизий и повесил поведение Solid. Добавив пару событий, чтобы эти черти смешно толкались, я получил результат, который меня полностью устроил. Придурки перестали сливаться.
Но все равно в трейлере оставалось около трех незанятых секунд. Я уже воткнул все что можно, а вставлять старые текстуры, которые ну совсем не подходят под стиль не хотелось. За какой промежуток времени уставший и отчаянный разраб может нарисовать пиксельный город, если внушит себе, что гринлайт завтра закроют? За два часа. NPC я воткнул из старого проекта, так как уже рисковал разбить голову о клавиатуру, уснув за компом. В итоге у меня получился тайлсет упоротого размера 45х45, работать с которым в дальнейшем будет сущим адом, но я справлюсь.
В общем, вот так из всего, что попалось под руку, был подготовлен душевный проект для выхода в Steam. Механика, над которой я сейчас работаю - перемещение по карте-лабиринту в духе метройдвании, с кучей побочных квестов, связанных со славянской мифологией и котиками. Я буду рад услышать мнения об игре здесь:
Активность увеличивает шансы Catmaze попасть в закрывающиеся зеленые двери!
Лига Разработчиков Видеоигр4.8K постов 19.9K подписчиков
Правила сообществаОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Только гифки/арты/скриншоты из игры. Такие материалы могут сопровождать рассказ об игре или обучающий туториал, но не должны являться основой поста
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"
Сообщество дало этой игре зеленый свет!Поздравляю! с:
Я прошу прощения, но земля на предпоследнем скриншоте выглядит очень знакомо
Я могу ошибаться, но кажется, что в платформеры играют многочисленные создатели платформеров.
Хочу отметить, что этот пост увидел только сейчас, когда вручную просматривал посты сообщества. Это притом, что подписан как на сообщество, так и на пару из использованных тегов. :)
Недавно такое было еще с одним постом. Кому бы пожаловаться. :\
Попробуйте Quixel NDO для фотошопа. Или попроще crazybump. Совет от 3Дшника)
Приятная на вид игра! Проголосовал за =)
трейлер отличный, музычка тоже)
проголосовал за, надеюсь отблагодарите ключём на выходе
Давайте поделюсь анимациями для моей игры
Прошлую порцию своих анимаций для игры я постила даже больше, чем месяц назад. С тех пор, конечно же, создание игры продвинулось, и я готова показать вам ещё несколько анимаций.
Город под дождём:
Сестрёнки пьют чай с яблоками:
Бабка читает книгу в ночи, под свет свечи:
В конце сегодняшней записи хочу сказать спасибо всем, кто следит за моими разработками и пишет полезные и поддерживающие комментарии. Это очень воодушевляет двигаться дальше и доделать-таки эту игру!
Бункер 21. Исповедь разработчика #0
Всем привет! Меня зовут Пётр! Как-то я писал всякое про игры, когда их разрабатывал. Одной из таких является "Бункер 21". Знаковый для меня проект.
Недавно я опубликовал пост и поделился своей небольшой радостью - игра вышла в ТОП лучших игр в App Store.
Хочу выразить всем огромную благодарность за то, что разделили со мной моменты радости! Я просто в таком шоке пребывал, что люди могут писать слова поддержки просто так, по-доброму, без стёба и насмешек. Спасибо. Я физически не успевал даже банально следить за тем, с какой скоростью летят комментарии! Это что-то невероятное. Такого мощного заряда я давно не ощущал!
И, в связи с множеством вопросов и просьб рассказать про свой путь, я решил описать всё, как это было на самом деле. Очень надеюсь, что я смогу придать мотивации всем, кто только в начале разработки своих игр.
Сегодня игра отметилась следующими достижениями [которые многие лично застали]:
- Google Play - 1 место в категории "Приключения"
- Google Play - 18 место в категории "Лучшие игры" вне категорий
- Google Play - 2 000 000+ установок
- Google Play - 30 000 отзывов, с общей оценкой 4.8 (где-то 4.7, где-то 4.9)
- App Store - 1 место в категориях "Приключения", "Экшн"
- App Store - 1 место в категории "Лучшие платные игры" вне категорий
Предисловие
Начну с того, что это не история "успеха" в её привычном понимании.
Я не проснулся знаменитым, не стал миллионером, ничего внезапно на меня не сваливалось.
Всё было наоборот: каждый день я работал, иногда даже по ночам, практиковался, изучал новое, изгонял старое. Вся моя жизнь перестроилась.
Начну с того, чего я ожидал, когда начинал разработку.
Так как это всё же хобби, то мои ожидания от разработки были не такие завышенные. Будучи владельцем ютуб-канала по созданию игр, хотелось иметь за плечами какой-то кейс, глядя на который можно было бы сказать: "Да, я хочу учиться у этого парня!".
Для этого нужен был какой-то мощный и показательный проект, при этом созданный лично мной полностью (это важно!).
Таковым и стал Бункер. Названия у него на разных этапах было разное:
- In Head (DEMO игры)
- Бункер 3D (попытка сыграть на ностальгии бывших владельцев телефонов с J2ME)
- Подземный Бункер 3D (примерно тоже самое)
- Бункер 3D (вернулся к "истокам")
- Бункер 3D Сюжетный Квест (добавил ключевых слов)
- Бункер 2021 - Игра с Сюжетом (продолжил эксперимент)
- Бункер 2K21 (гугл запретил использовать даты в названиях)
- Бункер 21 - Сюжет Offline (нужно больше поисковых фраз)
- Бункер 21 - Выживание с Сюжетом (текущее и последнее)
Как можно видеть, я пытался сделать название понятным как людям, так и поисковому алгоритму Google Play.
Дало ли это результат? Сложно сказать. Я не аналитик и разбираюсь в этом очень слабо. Но, в какой-то момент игру просто попёрло в топы на какой-то невероятной ракетной тяге. Но это случится ещё не скоро.
Помимо манипуляций со страницей, я также четыре (ЧЕТЫРЕ, КАРЛ. ) раза переписывал игру с нуля. Прям с чистой страницы, ибо постоянно натыкался на неудобства из-за своей же недальновидности.
В итоге я выработал формулу, которая подходила бы мне для работы и позволяла расширять игру. Но и до неё мне было далеко. Опыта создания полноценных игр на этот момент я всё ещё не имел.
За всё время разработки я постоянно прокачивался в навыках. Я даже не предполагал, что делать игру, это блин не только "накидал на сцену объектов и В.У.А.Л.Я".
Создание игры - это целый список мероприятий и ритуалов, нарушать которые не то, что не рекомендуется, а просто нельзя!
Работая над игрой, я несколько раз переделывал всё с нуля. С каждым разом, когда я начинал заново переписывать ядро игры, я так же изменял сюжет, ибо каждое повторное прочтение давало мне повод усомниться в своих навыках
Продумывать приходится каждую мелочь. Вплоть до разных деталей типа "в начале игрок говорит, что читал в газете такие-то данные, в середине игры он должен подметить это, проходя мимо. [какая-то несущественная фигня]".
Игра выходила по главам. Каждая новая глава примерно раз в два месяца. Всего глав в игре пять, и последняя глава вышла совсем недавно. Суммарно я затратил на разработку игры ровно один год. С мая 2021 по май 2022.
При этом я не потратил ни рубля на раскрутку игры. Не привлекал маркетологов, аналитиков, астрологов. никого.
Всю работу по игре я сделал сам. Каждый видимый пиксель в игре был создан мной с нуля.
Пока я работал над игрой, я освоил следующие инструменты:
- Godot Engine (игровой движок)
- Blender 3D (моделирование)
- GIMP (работа с текстурами)
- Paint.NET (работа с текстурами)
- Wings 3D (моделирование)
- Audacity (обработка звука)
- LMMS (создание музыки и звуков)
Также я значительно прокачал знания по языкам GDScript и HLSL (модифицированная версия GLSL для шейдеров), освоил работу с Kotlin при написании модулей для Android, чтобы работала реклама от AdMob и РСЯ (реклама от Яндекс). Попутно освоил работу с XCode на Mac, чтобы создавать сборки для iOS (под айфоны).
Ну и по доходам. Заработал я за этот год примерно [скрыто] миллионов рублей. Цифру я раскрою в последней части этой истории. Но доход действительно исчисляется миллионами рублей.
Месяц первый.
Начну этот рассказ я с самого начала. Блокнот мой прямо сейчас в руках, тут всякие зарисовки, проработка персонажа, я даже штрихами пытался изображать его эмоции, как это было в игре "Doom" первых версий.
Несмотря на сказанное мною ранее об одном годе разработки, с мая 2021 по май 2022, история эта началась году так в 2019. Месяц, к сожалению, точно не назову, но это и не имеет большого значения.
Сидел я одним вечерком, листал ютуб, искал вдохновения, как моё внимание привлекло видео о том, что какой-то блогер создал свою игру. Заголовок красивый, картинка на превью сочная. Заинтриговал, зараза! Кликаю!
Прошло 40 минут, ролик закончился. Моему разочарованию не было предела. Мало того, что видео пОлно воды, так ещё и автор откровенно привирал, говоря сначала о том, что он создал клон [некоторой] игры, а по факту просто создал что-то совсем не похожее, ни визуально, ни в плане механик или геймплея.
Было досадно, но я всё же досмотрел видео до конца. Хоть я и расстроился потраченному времени, меня посетила мысль.
"Я ведь тоже блогер! У меня есть аудитория, ей такое будет интересно!".
Начал продумывать идеи. Разработка должна быть интересной. Именно сам процесс. Интересный и мне, и зрителю - это будет самый кайф!
Прошла неделя.
Первый набросок был готов. Я тщательно описывал каждый шаг разработки, опуская всякую техническую часть, оставляя всё самое интересное. Однако, на этапе монтирования итогового ролика я понимал, что не хватает чего-то очень нужного. Посмотрев финальный монтаж, я расстроился: всё вообще не то. Моё изначально интересное видео о разработке игры превратилось в "а щас мы двигаем это сюда, чтобы вот тут игрок. ". Скучно. Скучная нудятина.
Всю последующую неделю я обдумывал, что и как делать, пока просто не пришёл к выводу: "нафиг видео!".
Нужно переосмыслить концепт мероприятия.
Чего я хочу? Сделать крутой проект, который не стыдно будет показать. Проект должен показывать, что один человек может создать игру с полного нуля, с минимальной подготовкой. Я для этого специально взял максимально доступный и полностью бесплатный набор софта. Нужно ли при этом каждый шаг записывать на видео?
Изначально я думал, что ДА! Но потом понял, что нет. Потенциальному ученику "крутого ютубера" будет достаточно обзора итогового продукта. Обзор разработки не даст знаний, лишь введет в заблуждение. Да и мне тяжело. Многие вещи на этапе разработки приходилось переделывать заново по несколько раз, а это влечет за собой и перезапись видео. С "бубнежом" на камеру. Такое затянется на годы.
Я немного посчитал, что всё, что я могу сделать за час без скринкаста, с записью видео может длиться до трех часов. Жуть! А если работы по плану - месяц? Вот-вот.
К концу первого месяца я понял, что бросаться в работу с головой без предварительного плана - плохая идея. Причем план нужен не просто на какое-то ближайшее время, а прямо на весь период предполагаемой разработки.
Я попытался поставить себе сроки - сделать демку за неделю. Вроде неделя - это семь дней, в один день я могу тратить в среднем по 4 часа свободного времени. Итого получаем 24 часа чистого времени. В целом - должно хватить.
Начался второй месяц разработки игры.
Продолжение следует!
PS: Друзья, отпишитесь пожалуйста, интересно ли вам будет это чтиво. Не хочется тратить время впустую, ни своё ни ваше.
PPS: Простите, если кому-то не угодил. Я обычный человек, как и все.
GameMaker Studio 2. Урок 2. События отрисовки. Коллизия. Как работают скрипты. Как подключить русский шрифт. Переходы между комнатами
Сегодня у нас много матчасти и не очень много кода.
Конкретизация разработки.
Как было сказано в прошлом посте, игра, которая будет сделана в ходе данных гайдов, будет относиться к жанру стратегий, а в качестве источников для "вдохновения" у нас - "Oxygen Not Included" и "Rimworld". Теперь же конкретно поговорим с вами о том, что в игре должно быть реализовано, по пунктам.
--) Персонаж - игровой "объект", способный перемещаться по игровому миру и взаимодействовать с ним. Делятся на подконтрольных и неподконтрольных игроку. Игрок НЕ имеет прямого управления над своими подопечными, кроме специального "боевого" режима.
--) Каждый персонаж должен иметь или уметь следующее:
---) Взаимодействовать с предметами, расположенными на карте: подбирать их в свой инвентарь или выкладывать; взаимодействовать с рабочими станциями; сражаться (наносить, получать урон).
---) Каждый персонаж смертен. Каждый персонаж должен иметь ряд характеристик, которые он будет пытаться "поддерживать" на должном уровне, чтобы не умереть и продолжать быть эффективным. Эти характеристики:
---) Каждый персонаж уникален и должен отличаться от других. Черты, навыки с различными уровнями прокачки. Этот пункт может игнорироваться в случае, если это не подходит под конкретного персонажа.
--) Объекты, с которыми можно взаимодействовать через интерфейс игры и с которыми могут взаимодействовать персонажи или иные игровые сущности с какой-то целью.
---) Пример: верстак для создания вещей; исследовательский стол для открытия новых вещей и пр.
-) Интерфейс.--) Простой, понятный. Желательно, на русском языке. --) Хотя бы минимально настраиваемый.
Скажу сразу, что персонажей мы делать будем позже. Начнём мы с интерактивных объектов и размещения их в мире. Но, это будет потом.
Сейчас - продолжаем изучать матчасть.
События отрисовки.
Событие отрисовки добавляется на объект как и любое другое, через кнопку "Добавить событие (Add Event)".
Имеется два события:
--) Событие, которое отрисовывает объект в комнате.
--) Событие, которое отрисовывает объект на экране.
Иными словами, событие отрисовки GUI позволяет нам закрепить на экране наш интерфейс и то, что мы хотим игроку показывать всегда. Draw - стандартная отрисовка.
Возникает вопрос: зачем нам отдельно добавлять событие отрисовки, если объект спокойно отрисовывается и без него?
Ответ: в событии отрисовки можно написать код, который позволит нам менять параметры отрисовки, будь то: изменение размера (соотношения сторон); изменение цвета; создание дополнительных графических эффектов; etc.
Если вы добавили событие отрисовки, то вам необходимо написать в коде следующую строку:
В противном случае, объект не будет отрисован.Проделайте это сами. :)
Итак. Что мы можем делать в событиях отрисовки:-) Отрисовка спрайтов:--) draw_self() - отрисовывает спрайт текущего объекта с настройками по умолчанию.--) draw_sprite_ext( название спрайта в обозревателе, номер изображения, x, y, x-масштабирование, y-масштабирование, поворот, цвет, прозрачность) - отрисовка спрайта "расширенная" - т.е. с настройками. Можно отрисовать любой спрайт, как и везде, где его нужно отдельно указывать.
--) draw_sprite_part(название спрайта в обозревателе, номер изображения, x координата левой верхней точки спрайта, y координата левой верхней точки спрайта, ширина, высота, x, y) - отрисовка части спрайта--) draw_sprite_stretched(название спрайта в обозревателе, номер изображения, x, y, ширина, высота) - отрисовка спрайта с его "растяжением". Если у спрайта включить и настроить функцию Nine Slice (девять "срезов"), то можно создавать масштабируемые (с одинаковым разрешением, пропорциями и качеством по итогу) элементы интерфейса: окошки, кнопки, etc.
-) Отрисовка фигур:--) draw_circle(x, y, радиус, заполнение (True/False)) - рисуем круг. Аналогичное есть для прямоугольника (rectangle), стрелки (arrow), эллипса (ellipse), линии (line).--) draw_button(x, y, x2, y2, up) - Отрисовка кнопки. Где up - True или False, нажата кнопка или нет.
-) Настройки отрисовки.--) ВАЖНО. Любые настройки отрисовки нужно проводить ПЕРЕД отрисовкой. Можно делать в любом событии, но для удобства лучше тут же.--) draw_set_color(col) - устанавливаем цвет отрисовки. Базовые значения цветов начинаются с приписки "c_". Пример: c_black, c_red. ---) Для продвинутых - можно использовать HEX, как в CSS: #11CCFF как пример. Используется стандартная RGB система. Если заменить решётку на $, то система сменится на BBGGRR, т.е. наоборот.--) draw_set_alpha(alpha) - прозрачность отрисовки --) draw_set_font(font) - устанавливаем шрифт--) draw_set_halign(halign) - Расположение текста по горизонтальной оси (fa_ left/center/right)--) draw_set_valign(valign) - Расположение текста по горизонтальной оси (fa_ top/middle/bottom)
Это та часть, что касается кода и с которой мы с вами будем непосредственно работать. Можете держать как памятку у себя перед глазами, когда начнём писать код.
Коллизия - иначе говоря, столкновение объектов, происходит благодаря "маске" спрайта. Если спрайт - это картинка объекта, то маска - это "твёрдое тело", отвечающее за считывание столкновений.Чтобы её посмотреть, откройте настройки спрайта и разверните пункт "Collision Mask"
Хотя маска может иметь несколько форм, о которых будет написано ниже, устанавливается она исходя из четырёх позиций - левого верхнего и правого нижнего углов. Именно эта маска считывает столкновения, а не сам спрайт.
Mode - режим маски. Варианты: - Автоматическая- На всё изображение- Ручная
Type - тип маски. Варианты:- Прямоугольник- Прямоугольник с поворотом- Эллипс (работает медленнее)- "Алмаз" (ромб) (работает медленнее)- Предрасчет / Точный (медленнее) - ГМС постарается сам подстроить маску под форму объекта.- Предрасчет / Точный по кадрам.
Мой совет:Если игра небольшая, ставьте ручной режим и настраивайте сами те объекты, для которых это важно. Особенно те объекты, столкновение с которыми должно происходить только в определённой позиции (скажем, как в Стардью, сталкиваешься с основанием дерева, но не с его кроной).
В нашей игре данная настройка нам практически не пригодится, так как будет использоваться система поиска путей. Тем не менее, с помощью маски можно будет реализовать "выталкивание" застрявших персонажей, а также настроить косметическое "расталкивание" персонажей, если те проходят через занятую другим персонажем клетку.
Как работают скрипты.Скрипт (функция) - именованный блок кода, выполняющий определённую задачу. Все команды, которые мы использовали до этого, являются такими "скриптами". Иногда я буду называть их функциями, оба варианта правильные.
Создать скрипт просто. Для этого нажмите ПКМ по папке "Scripts" и выберете там "Script". Назовите его scrMoveУ вас откроется следующее окно:
scrMove - название нашей функции. В круглых скобках мы в неё можем передавать аргументы. Аргумент - это переменная, которая используется только в функции, передаётся в неё при вызове и может иметь любое название. В скобках напишите spd - это будет как раз наша переменная скорости.Вырежьте код движения из Step-события у игрока и вставьте в скрипт, а в Step-событии игрока напишите следующий код:scrMove(player_speed);Получится следующая картина:
Как вы заметили, "x" и "y" у нас по прежнему работают. Это потому, что мы выполняем скрипт в объекте oPlayer: данные переменные подтягиваются из объекта автоматически.
Когда нужно применять скрипты?-) Когда код, который мы пишем, нужно использовать несколько раз.-) Когда код, который мы пишем, занимает много места, чтобы его было проще отладить.-) Когда нам нужно получить какое-либо значение на основе каких-либо данных, так как скрипт может не просто выполнять код, но и возвращать значения.
Для того, чтобы скрипт вернул какое-то значение, нужно написать:
Переменную можно написать только одну. Команда return прерывает выполнение скрипта. Переменную / возвращаемое значение можно не писать, тогда скрипт просто прервётся. Конструкцию с прерыванием следует добавлять везде, где это может повлиять на игру, особенно её работоспособность.
Раньше скрипты выполнялись медленнее, чем код, написанный просто в объекте. Сейчас это не так (или, по крайней мере, не так критично).
Также стоит понимать, что создав объект "Скрипт", вы, по сути, создали просто файл для хранения кода. В одном скрипте может быть собрано сразу множество разных функций, отвечающих за выполнение одной задачи. При вызове обращаться нужно именно к имени функции.
Скрипт для отрисовки текста с обводкой.
Создаём новый скрипт и называем его scrOutlinedTextНам понадобятся следующие аргументы:-) xPos - позиция текста по X-) yPos - позиция текста по y-) col - цвет текста-) outlineCol - цвет обводки-) text - текст-) curdepth - текущая "глубина" объекта.-) Шрифт, которым будем рисовать.-) Позиционирование по X-) Позиционирование по Y
Общая схема работы скрипта:1) Назначаем максимальную "глубину". 2) Рисуем текст цветом обводки.3) Поверх рисуем текст нужным нам цветом, но с небольшим смещением.4) Возвращаем глубину к первоначальным показателям.
Код: https://pastebin.com/TahYZKc8 P.S. Изначально код не мой, а честно стырен подсмотрен с интернета, но я в него добавил больше переменных для лучшей настройки и отрисовки. Фактически, значение глубины тоже можно передавать как аргумент, чтобы разный текст выводился по разному.
Создание русского шрифта
Проверим, как работает скрипт. Сначала мы создадим русский шрифт. Для этого найдём папку "Fonts", нажмём ПКМ и создадим "Font".Выбирайте любой шрифт и размер, который хотите. Нам с вами нужна кнопка "Add", чтобы добавить диапазон с русским шрифтом.
Здесь напишем 1040 to 1105, чтобы захватить весь русский алфавит, затем нажмём "Add Range".Шрифт называйте на Ваше усмотрение. Я назову его: fontArialRusSmall
Теперь перейдём к объекту игрока. Создадим событие Draw GUI и напишем следующий код:scrOutlinedText(10, 10, c_yellow, c_red, "Скорость: " + string(player_speed), depth, fontArialRusSmall, fa_left, fa_top)Результат:
Можете заменить c_yellow и c_red на любые цвета. Помните, что можно передавать цвета в HEX с помощью # и $ (в обратном порядке).
Скрипт для поиска значения в массиве:
Так как эту тему мы не проходили, заострять внимание не буду, но вдруг кому пригодится: https://pastebin.com/1VTNg4Bp
Переходы между комнатами. Создаём меню.
Наконец, с матчастью мы закончили. Приступим к тому, что создадим меню нашей игры.Создаём новую комнату и обзываем её rmMainMenu. Затем нажмите на значок "домика", чтобы поменять стартовую комнату (ту, что отображается при запуске игры).
Сделайте так, чтобы домик был напротив созданной комнаты.Заметьте, что при создании новой комнаты у неё изменяются все настройки! Это важно.
Создадим в "Objects" папку "Меню", где создадим следующие объекты: "oMStart", "oMSettings", "oMExit", "oMMenu".OMStart - кнопка для старта нашей игры.OMSettings - кнопка для перехода в настройки игры.oMExit - кнопка для выхода из игрыoMLoad - кнопка для загрузки игрыoMMenu - управляющий объект, который будет располагать наши кнопки.
Зайдём в комнату MainMenu и поставим там объект oMMenu.Затем перейдём в код oMMenu и выберем событие создания (Create)
Там напишем следующий код:
Теперь нужно каждую из этих кнопок разместить. Но, чтобы правильно их разместить, сделаем на все кнопки один спрайт. Размер подбирайте под себя.
У каждого объекта создадим два события: Draw, которое оставим пустым, и Draw GUI, где напишем draw_self();Там же напишем:
Где "Здесь текст кнопки" - заменить на нужное: "Начать игру", "Загрузить игру", "Настройки", "Выход"
Вернёмся в OMMenu.Нажмите правой кнопкой мыши. Наведитесь на пункт "Code Snippets" и выберите пункт 5.В данном меню расположены готовые шаблоны кода, а мы создали шаблон цикла.Напишите следующий код:
for (var i = 0; i < array_length(buttons); ++i)
Теперь нужно сделать отслеживание нашей мышки. Кнопку "Настройки" мы сделаем чуть позже. Пока перейдём к кнопке для выхода из игры.
До текста отрисовки нашего текста, нам нужно сделать небольшую подсветку нашей кнопки, чтобы мы знали, что она работает. Для этого нам нужно отслеживать позицию нашей мыши относительно интерфейса, для чего есть специальная команда.
ОК. Теперь мы отслеживаем нашу мышку относительно GUI. Нужно проверить, наведены ли мы на конкретный объект и если да - нарисовать поверх него полупрозрачный белый прямоугольник. Для этого есть команда:instance_position(x, y, obj)
В x и y мы передадим переменные выше, а в качестве объекта напишем "self", чтобы проверять координаты нашего объекта.
Уже после этого пишем наш текст.Теперь при наведении на кнопку "выхода" мы увидим, что она "активна".
Вырежем наши две переменные, добавим их в событие create. Теперь мы можем использовать эти переменные в любом коде этого объекта.
Теперь перейдём в событие step. Сюда также добавим наши две переменные. Теперь они будут постоянно обновляться.Начнём настраивать логику работы.
Можете скопировать тот код, что мы писали выше, но удалить всё изнутри.Нам нужно сделать проверку:Если мы наведены на объект И нажата левая кнопка мыши - выйти из игры. То есть, немного её дополнить.
Для проверки нажатия кнопки левой мыши будем использовать команду:mouse_check_button_pressed(mb_left)Для выхода из игры: game_end()
В итоге, в коде step будет следующий код:
Итого, если мы наведёмся на кнопку выхода - она подсветится. Нажмём - игра закроется.
Аналогичный код с подсветкой и проверкой просто скопируем в oMStart. В других кнопках логику работы в step пока не настраиваем на них, работаем с кнопкой начала игры.Нужно внести одно изменение.
Вместо game_end() нам нужно написать код для смены уровня.Для этого напишем:room_goto(Room1) Где Room1 - название комнаты, в которую вы хотите переместиться. Оно должно совпадать с названием комнаты в браузере ассетов.
Итого, мы можем выйти из игры или перейти в комнату, где у нас есть игрок, которым мы можем управлять. Осталось дополнить написать код, чтобы мы могли вернуться обратно в меню, при этом так, чтобы положение игрока на экране сохранялось.
Для этого нам придётся создать новый объект и назвать его oGameManager.Разместим его в самой первой комнате и отметим у него чекбокс "Persistent"Затем напишем у него в Step код:
Если индекс текущей комнаты != 0 и нажата ESC - деактивируем все инстансы, кроме текущего и переходим в меню.
При старте комнаты, если эта комната не является меню, мы будем запускать все инстансы.
Реализация сложнее. О том, как работает Nine Slice.
Ниже будет приведена реализация меню с использованием функции Nine Slice, которая позволит нам делать кнопки других размеров.Используя Nine Slice мы не сможем проверять, наведены ли мы сейчас на нужный нам объект. Также эта функция годится только для отрисовки типовых форм с типовым шрифтом. Если у вас спрайт кнопки идёт с текстом, то данная функция не подойдёт. Чтобы отслеживать нажатия и наведение, нам необходимо проверять, находятся ли координаты мыши в нужной области. Для этого существует команда
Итак. Первый шаг - это убрать спрайт у каждой нашей кнопки.Для этого откроем объект каждой кнопки, нажмём на выбор спрайта и выберем "None".
Шаг второй.У каждой кнопки в событии Create мы пропишем:
При создании кнопок мы и так будем знать их x и y координаты, а ширину и высоту будем передавать.
Шаг третийНарисуем спрайт в виде одноцветного квадрата размером 9х9 пикселей.Затем добавим ему две обводки разных цветов, каждая шириной в 1 пиксель.Получим следующую картину:
Теперь перейдём в настройки спрайта.Назовём его sButtonSlice.Слева выберем и откроем пункт Nine Slice.
После чего поставим галочку "Activate Nine Slice".Далее - смотрим на наш спрайт и настраиваем его, как показано на скриншоте ниже:
Чтобы двигать фиолетовые полоски - достаточно на них навестись и зажать ЛКМ.В правом окошке вы можете порастягивать данный спрайт, как хотите и посмотреть на итоговый результат.Растягивающаяся рамка для кнопок готова!
Шаг четвертый.
Пропишем в Draw GUI отрисовку кнопки вместо draw_self():
Таким образом, мы отрисовываем кнопку.
Вернёмся в oMMenu, в Create, где немного допишем наш код.Мы создадим переменную, которой будем присваивать значение ID только что созданного объекта. Затем, обращаясь к этой переменной, мы будем менять у данного объекта параметры: ширину и высоту.
Меняя значения bwidth и bheight вы сможете сами настроить нужные размеры кнопок.
Обратите внимание, что создавая инстанс мы можем передать struct - то есть структуру данных.Тогда не нужно будет обращаться к инстансу через переменную, но придется удалить код
При создании наших объектов, а сам код для создания инстанса преобразится следующим образом:
Какой из вариантов удобнее - решать вам.Вариант с переменными, которые объявляются при создании объекта, я лично считаю более надёжным, а функциональных отличий между данными командами не так много. С другой стороны, такая возможность появилось недавно и её нужно тестировать.
Теперь задача - сделать так, чтобы мы могли считывать, наведены ли мы сейчас на кнопку.
Для этого мы берём координаты x и y, после чего проверяем, находится ли наша мышь в области:
Остальное - копируем из прошлого кода. Получается следующий блок:
Шаг седьмой.
Проделать тоже самое в событии Step. (По сути, просто заменяем instance_position)
Шаг восьмой.
Донастраиваем все кнопки.
Какой из вариантов использовать - решать Вам.
Можно вообще использовать следующий вариант:
Создать спрайт нужных размеров и залить его белым цветом с "прозрачностью" в 1 единицу. Совершенно незаметно. Используя bbox_left / top / right / bottom нарисовать поверх прозрачной основы кнопку и проверять уже не через "point_in_rectangle", а как в первом способе.
На этом сегодняшний гайд подходит к концу.Вкратце мы разобрали события отрисовки; как с ними работать; как работать со скриптами; как работать со шрифтами; а также вы можете смело скопировать себе и использовать несколько других скриптов в своих проектах.
Что нужно подготовить к следующему гайду:- Минимум два спрайта "породы" - на фон (темнее) и для объекта (светлее). Размер одного спрайта - 32х32.- Желательно спрайты нескольких объектов, вроде стен, дверей. Исходите из того, что размер одной клетки будет равен 32х32 пикселей.- Спрайт для неба/космоса/прочего на фон на ваше усмотрение.
Я подготовлю свои варианты и, используя их, на следующем занятии мы с вами разберёмся, как работает камера.Сделаем меню настроек с возможностью переключения в полноэкранный режим.
И небольшой спойлер на будущее.Взаимодействовать с миром можно будет двумя способами: через ЛКМ и наведением "тела" игрока на тот или иной объект.
Загрузить файл проекта и всё пощупать самому можно по ссылке:Яндекс диск
- Камера и её настройка. Разные способы реализации: от простого к сложному.
- Иерархия объектов. «Объекты-родители» и их «дети». Решение часто встречающихся проблем и немного про то, как удобно выстраивать взаимодействие с объектами. Глобальные переменные.
- Массивы и с чем их едят, а также grid (сетка комнаты), размещение объектов по сетке. Включая объяснение, в каких случаях лучше использовать встроенные функции, в каких – писать свои с нуля.
- Пути. Один большой гайд, включая скрипты поиска путей для различных сеток в т.ч. с примерами из моего личного проекта.
- Иные способы хранения информации в GMS2, когда их стоит или не стоит использовать.
- Сохранение. Встроенное VS самописное.
Есть вопрос - задавай, постараюсь ответить.Что-то не получилось, вылезла ошибка и так далее - тоже пиши, помогу или поможем.