Как я начал писать движки  для визуальных новелл на C++. Часть 1: Начало пути.

Как я начал писать движки для визуальных новелл на C++. Часть 1: Начало пути.

1.1. Введение.


Бывает такое чувство, что ты изучаешь что-то не то. Вроде интересно, а вроде и нет. Именно такое чувство у меня и было, когда я изучал Unreal Engine — мощный движок, можно сделать от шутера в открытом мире до платформера, но интереса нет. До Unreal Engine я испробовал Ren’Py, Unity, Java и C#, но, как и с Unreal Engine, у меня не было интереса. Мне хотелось чего-то одновременно интересного и сложного.

Именно в декабре 2020 года я и решил — хочу написать собственный движок для визуальных новелл на C++. Почему для визуальных новелл? Мне они просто нравятся, вот и весь ответ.

1.2. Немного обо мне.


Зовут Никита, проживаю в Красноярске. С 13 лет занимаюсь программированием, успел выпустить 2 визуальные новеллы на собственных движках, 5 раз собирал команду для разработки своих проектов. Изучал C#, Java, Python, Kotlin.
Сейчас мне 16 лет, программирую на C++, пишу собственный движок — Oneiro Engine.

1.3. От идеи к коду.


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





Кстати, для каждого урока я создавал отдельный проект, чтобы можно было потом вернуться для повторения материала. Как итог у меня набралось около 200 проектов весом 5-10 Гб (точные цифры не помню, так как проекты удалил).

Спустя около 4 месяцев я уже знал основы C++: ООП, STL, многопоточность, алгоритмы и так далее. Хоть знания и были поверхностными их всё равно хватило на попытку написания движка.

Первым же делом я начал искать нужные мне библиотеки и информацию о том, как написать движок для визуальных новелл. В итоге была найдена библиотека для 2D графики — SFML. А вот информация о том, как написать движок для визуальных новелл — не была найдена, но я не отчаивался.




Из-за нулевой (или около нулевой) информации о том, как создать движок для визуальных новелл мне пришлось идти на GitHub и искать там уже готовые/wip движки. Поняв, как можно написать движок, я создал проект в Visual Studio и пошёл писать код, точнее — копипастить его. Пересказывать, как я копипастил код, думаю, не надо — это будет неинтересно.





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

1.4. Поверхностное изучение архитектуры движков и OpenGL.


Когда я пытался писать движок на SFML, я понял одну вещь: данная библиотека весьма ограничена. Как минимум изменение размера окна работало не так, как нужно было, мало возможностей в 3D и так далее. Поискав информацию в интернете о замене SFML, я наткнулся на OpenGL.

Увидев треугольник на OpenGL, который я сам вывел, я получил массу дофамина. Тогда я решил — я хочу быть программистом графики. Потом я вывел 3D куб, сделал базовый свет, попробовал ImGui, но было одно “но” — я не понимал базовых вещей в OpenGL. Именно это одно “но” мне помешало в дальнейшем написании движка на Anivisual Jam #3.






1.5. Вторая попытка написания движка.


11 июля 2021 года я увидел пост на Anivisual о новом джеме под номером 3. До этого я и сценарист команды, в которой я состоял как кодер, пытались поучаствовать во 2-ом Anivisual джеме, но из-за нехватки ресурсов мы всё забросили. А на 3-ем Anivisual джеме у нас были шансы, так как я знал художницу, которая точно не откажется рисовать нам спрайты. Все остальные ассеты (например музыку и бэкграунды) мы могли найти в интернете. До анонса 3-его Anivusal джема я уже писал второй движок, который как раз таки был использован для новеллы на джеме.

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

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






На релизе большинство багов было исправлено, но костылями. К примеру, для решения проблемы со спрайтами и текстом было запрещено изменять размер окна, но на других мониторах (не 16:9) всё плыло.
Что касается кода, то он очень и очень плох. Я не задумывался над оптимизацией, да и над качеством кода тоже, так что если сейчас взглянуть на код, к примеру, показа спрайта, то можно в прямом смысле умереть от ужаса. Хотя не всё так плохо:)



1.5.1. “Коротко” о том, как я реализовал базовые функции визуальных новелл.


К примеру, у нас есть две команды:
1. Покажи спрайт sprite;
2. Выведи текст “Hello World!”.
Движок парсит их всех и собирает в единый контейнер команд для дальнейшего разбора. Когда игрок переходит на следующую команду (т.е нажимает пробел/левую кнопку мыши), то движок просто увеличивает текущий итератор на 1 и начинает разбирать команду по кусочкам: какой у неё тип, какие данные и так далее.
Вернёмся к примеру, мы зашли в игру, потом движок автоматически пропарсил наши команды до вывода текста. В начале игры мы увидим сам спрайт “sprite” и текст “Hello World”. Если дальше есть команды и игрок нажал пробел, то логика идёт таким же образом до показа текста:

Код

show bg — it++ и разбор команды
show sprite — it++ и разбор команды
me “Hello, World!” — ждём нажатие кнопки мыши или чего-то другого
me “Next command.” — it++ и разбор команды
hide sprite — it++ и разбор команды
hide bg — it++ и разбор команды
me “Goodbye!” — следующих команд нет, ожидаем выход из игры


1.5.2. И немного про костыли.


Из-за того что я не был знаком с кодировками текста и библиотекой freetype, мне пришлось делать костыли для загрузки русских символов (глифов). Для того чтобы написать русские символы, надо было сменить кодировку файла на windows 1251. При этом кодировку не простого скриптового файла, а C++ исходника, так как у движка не было скриптового языка.
Для работы с аудио я взял библиотеку irrKlang free, которую нельзя использовать в коммерческих проектах. Как я писал раньше, я не сделал изменение громкости в игре, из-за чего игрокам приходилось изменять громкость в микшере.
Про кривые спрайты и кнопки, я думаю, не надо рассказывать, так как тут и так всё понятно.

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

1.6. Улучшение навыков.


Точно не помню, как и когда я наткнулся на канал The Cherno, но именно от Яна Черникова я впервые познал, что такое движки и с чем их едят. Тогда он только начинал писать свой движок — Hazel Engine. Посмотрев парочку видео Яна, у меня появилось представление о том, как можно написать ядро движка, нормальный рендер на OpenGL и так далее. Именно по его видео я пытался писать движок в третий раз.
Кстати, по сей день я смотрю его видео и иногда стримы, чтобы узнать что-то новое, да и просто получить вдохновение.

Перед разработкой 3-его движка я решил испробовать Vulkan API, при этом не зная как устроена графика внутри. Как итог спустя 2 недели я решил бросить изучение Vulkan API.



1.7. Третья попытка написания движка.


Где-то в сентябре 2021 года было решено начать разработку третьего движка — Rebirth Engine 2.0.
Как и всегда, представления о том, как будут работать и взаимодействовать компоненты движка, не было. Зато была идея использовать скриптовый язык lua для написания скриптов новелл с похожим на синтаксис Ren’Py.





В принципе, движок был написан по той же самой схеме, как и все прошлые, но были добавлены анимации (простой dissolve эффект), синхронизация с разными разрешениями и соотношениями сторон и так далее. Основным нововведением был скриптовый язык Lua через который я пытался реализовать похожий на Ren’Py синтаксис скриптинга новелл.



Кстати, схема разбора скрипта новелл была такая же.




1.8. Конец?


Вместо полноценной статьи получилось сочинение на тему “Чем вы занимались весь год?” :D
Кстати, после изучения Vulkan API я пошёл пилить демки на OpenGL (OGLD), но там ничего интересного нет, а после разработки Rebirth Engine 2.0 я решил впервые в жизни зайти на реддит и сделать пост по поводу следующего движка (ссылка на пост будет в конце).
Следующая статья уже будет о текущем движке, его структуре, используемых технологиях, проблемах с которыми я столкнулся при разработке и так далее.

1.9. Ссылки.


GitHub
Движок, который использовался как пример в разработке первого движка
Первый движок (Simple Visual Novel Engine)
Второй движок (использовался на Anivisual Jam #3)
Новелла созданная на втором движке
Проект для изучения Vulkan API
Третий движок
Пост на реддите
18
Декабрь
8
4.6
1991
Добавлять комментарии могут только зарегистрированные пользователи.

Комментарии к записи: 8

avatar
#1 Хемуль
87473
в 10:50 (11/Окт/2022)
0
Статья интересная. И все же зачем, если уже есть Ренпи?
avatar
#2 Dezlow
334629
в 12:55 (11/Окт/2022)
1
По плану в следующей статье будут раскрыты все карты.

Первая часть содержит в себе вводную информацию и ничего более.
avatar
#3 Vertikali
521695
в 17:59 (11/Окт/2022)
0
Gaijin Jam — это хакатон по разработке компьютерных игр на своем движке, проходящий при поддержке компании Gaijin Entertainment.
https://vk.com/gaijinjam
Так же у них есть сервер в Discord-е, на котором можно искать единомышленников:
https://discord.gg/52JRtUwG
avatar
#4 Dezlow
334629
в 13:58 (12/Окт/2022)
1
Выглядит как реклама:D
На первом джеме от гаджинов можно было использовать свой движок, а вот на втором нет.
По поводу следующего джема нет информации, но скорее всего также будут заставлять писать на daScript'e.
avatar
#5 Bicardine
444656
в 17:38 (23/Окт/2022)
0
Похвально, но мало написано про сам движок. Или де/сериализация и отрисовка графики с текстом это весь функционал? Мало, чтобы вырасти из учебного проекта, хотя для практики самое то, у меня у самого компоненты для забавы на юнити написаны, коих достаточно для новелки.

Локализацию завести, встраиваемые в сценарий команды, микшинг звуков, шейдеры для текста. Хотя даже в этом случае тебе надо как-то извернуться и что-то эдакое внести, чтобы выделиться по сравнению с другими движками. И если с Ренпаем всё понятно, чтобы его переплюнуть достаточно партикл-системы, 3D, пост-процессинга и шейдеров, то вот что предложить покруче полноценных движков, учитывая что дальше пая большинству-то в целом ничего и не нужно, а уж тем более использование всяких профайлер-оптимизаторов, кастомных эдиторов и т.д., это уже к полноценным играм.

Послежу ради интереса.
avatar
#6 Dezlow
334629
в 04:40 (31/Окт/2022)
0
> Похвально, но мало написано про сам движок.
Основная цель была написать про мой начальный путь в написании движков и не более того. В самих движках ничего нового не было, так что и рассказывать об их устройстве нет смысла, да и код там говно.

Спасибо за комментарий:)
avatar
#7 SonCat
163179
в 22:48 (19/Ноя/2022)
0
Задумка интересная на мой взгляд. Пусть я и не профессионал, но советую попробовать научиться реверс-инженерии. Поясню. Многие люди для лучшей защиты данных от кражи, пишут на собственных движках новеллы (ведь файлы из renpy новеллы очень легко достать). Особенно японцы. Вы могли бы с помощью обратной разработки увидеть все преимущества и недостатки того или иного движка непосредственно в среде разработки, чтобы потом применить полученные знания для усовершенствования своего проекта. И еще, попробуйте поискать статьи на разных языках по разработке движков (я также год назад вроде видела на этом сайте, как кто-то тоже пилил движок, может чем-то поможет). Успехов вам!)
avatar
#8 ValtraVal13
357696
в 01:02 (20/Ноя/2022)
0
мне кажется это сложнее чем написать свой движок =)))  nmda