Хочу написать библиотеку которую буду использовать как для игр так и для простого и не очень ПО. В данной теме буду публиковать детальное описание своим действиям.
Главная цель это показать действия тем людям кто хочет написать своё и не знает с чего начать. Как говорят, кто ничего в жизни не добился, идут в учителя. Но тем не менее я считаю себя достаточно опытным.
Всё уже написано по миллиону раз. Это будет финальная итерация.
Код и архитектура не претендует на эталон. Но я напишу определённые моменты к которым я пришёл, и я считаю их истинно верными.
Какой результат я ожидаю? Хочу сделать минимальный набор функций и набор редакторов (написанные на этой библиотеке). Именно набор а не одну единственную программу всё-в-одном. Графика будет в последнюю очередь, я её даже не включил в список дел.
Ниже будут ссылки на сообщения в данной теме. В каждом сообщении будет находится ссылка на репозиторий той версии когда было закончено написание сообщения. Всё написано последовательно, от старого к новому. Если в списке чего-то нет, это не значит что этого не будет вообще.
* Начало * Макросы, память, DLL, assert * String, Stacktracer, Log * Framework * Окно, Delta time * Ввод * Математика * Вектр * UID * Графическая Система * D3D11 * Цвет * Матрица * Кватернион * Рисование 3D линии * Камера * AABB * Луч * 3D Mesh * Mesh creator * Массив * Список * Загрузка OBJ * GPU Mesh * Image * Загрузка BMP * Текстура * Загрузка из архива * Загрузка PNG * Загрузка JPG * Загрузка TGA * Render Target Texture * GUI: шейдер * GUI: рисование прямоугольника * GUI: шрифт * GUI: текст * GUI: база для GUI элементов * GUI: GUI окно * GUI: кнопка * GUI: выравнивание * GUI: текст для кнопки * GUI: дефолтный шрифт * GUI: скроллинг * GUI: checkbox и radio button * GUI: редактор текста * GUI: listbox * GUI: slider * Нормальная демо программа * Ray-triangle intersection * Спрайты * Билборды * Камера для редактора * Текст в 3D * Генерация куба * Генерация сферы * Генерация UV (planar mapping) * Frustum culling * Вращение объектов * Occlusion culling * Скелетная анимация - основа * Чтение SMD * Скелетная анимация - проигрывание анимации * Физика * Аудио * Ландшафт * ???
Необходимо время для того чтобы я написал Начало... "Do you think we can fly? Well, I do."
Сообщение отредактировал BADCOIQ - Понедельник, 27 Мая 2024, 22:25
Пишу (очень медленно) лайтовый (то есть не все классы), но расширенный аналог STL. Расширенный в смысле новыми структурами данных и математикой (в т.ч. геометрией). Заточено под геймдев. Ссылку пока не оставлю. Добью сначала основные контейнеры.
Цитата
Необходимо время для того чтобы я написал Начало...
Мне же больше необходим глубокий сон и энергия. Мля... не могу выспаться нормально. Поэтому, все очень долго тянется.
Если бы я был уверен, что допишу, то предложил бы тебе не писать эту часть. Но я не уверен, так что пиши.
Цитата
Графика будет в последнюю очередь, я её даже не включил в список дел.
Сообщение отредактировал graveman - Суббота, 01 Июля 2023, 16:40
Да пока не хочу. Там только вспомогательное метапрограммирование есть. Существенный функционал локально на компе (ассерт, часть вектора). Могу только сказать, что делаю по всем канонам STL (стараюсь). Как будет выложен vector, shared_ptr, assert, string, то дам ссылку. Интерфейс будет тот же, только будет макрос для выключения эксепшнов. Юнит-тесты только нужны будут на catch
Добавлено (01 Июля 2023, 16:58) --------------------------------------------- Короче, я тоже пришел к определенным выводам. Многочисленные безуспешные и малоуспешные попытки написать движок у меня всегда сводились к написанию улучшенных или недостающих штук из STL, окна и вывода кубика-чайника или модели в окне. Осознав это, я решился писать эту библиотеку. Раз и навсегда. Вектор планировалось сделать еще в феврале. Но я споткнулся о метапрограммирование, был много времени в депрессии и хроническом недосыпе. Теперь я более-менее могу понимать исходный код STL.
Сообщение отредактировал graveman - Суббота, 01 Июля 2023, 16:48
Мне по вектору, нужен такой, который методом clear просто сделает m_size = 0; а не освободит память совсем.
Так он и не освобождает память. Он ее освободит только в деструкторе или же при достижении size() == capacity(). clear() вызывает деструкторы элементов. Вот не помню, как там насчет POD-типов. Но если уж не хочется их вообще вызывать, то сделаю специализированную версию. Но процентов 90, что если деструктор тривиальный, он не вызовется при clear().
Сообщение отредактировал graveman - Суббота, 01 Июля 2023, 17:10
Что необходимо сделать чтобы получился ДВИЖОК? Сначала надо освоить создание библиотек. Как только получится вызвать функцию из библиотеки, сразу придёт понимание что такое этот ваш ДВИЖОК - это просто библиотека, или их набор.
Библиотеки могут быть динамическими или статическими. Динамическую библиотеку можно использовать во множестве языков программирования. Она имеет вид отдельного файла.
Статические библиотеки это набор кодов, которые становятся частью программы, они вшиваются в исполняемый файл. Эти библиотеки нельзя использовать в иных ЯП. Наверно.
Здесь, я буду использовать статические библиотеки. Почему: - всё быстро компилируется - их проще подключить (для Visual Studio)
Минус статической библиотеки в том что нужно использовать конкретную версию компилятора, это касается VS, на GCC их не писал. Поэтому при начале работы, лучше скомпилировать библиотеки тем компилятором что есть.
Мысли о коде Я не имею опыт современного C++. Даже не знаю есть ли C++23 и далее, и вообще что там было после C++17, даже не интересно. Что-то увижу полезное, приму. Но никаких концептов, модулей, жёсткого STL и т.д.
* Запрещено использовать STL в API. Необходимо написать свои вещи.
* Общий стиль кода должен быть простым в стиле СИ. Имена функций и пользовательских типов должны иметь префикс, в моём случае я буду использовать `bq`. Сишные префиксы гораздо удобнее использования namespace.
* Вообще namespace даёт кучу неудобностей, поэтому запрещено использовать его в API. Допускается его использование для внутренних вещей.
Код
namespace bq { namespace internal { } }
* Фигурные скобки тел функций и пользовательских типов должны быть на уникальной строке. * Тела if for и прочих могут находится на той-же строке что и инструкция.
* Имена для API нужно писать в CamelCase. Код проще редактировать, не нужно тратить время на ввод симола `_` Получается, имена будут выглядить так: bqPolygonMesh
* Использование имён_такого_типа допускается только в классах которые схожи с классами в STL.
* В коде лучше использовать сишные\сырые массивы. Ненужно использовать динамические если без них не обойтись.
* Допускается использование макросов, но не сложных. Например, запрещено использовать макросы для генерации функций
* Использовать строки char* в API или свой класс bqString. Использовать wchar_t* для открытия файла не имеет смысла. char* работает подобно UTF-8.
* Использование uint8_t и подобных имён для стандартных типов.
* Запрещено кидать исключения.
* В пользовательских типах, имена переменных должны иметь префикс 'm_', имена открытых методов должны начинаться с Большой буквы, имена закрытых методов должны начинаться с символа `_`.
* Наследование используется только для классов.
Мысль об архитектуре Не нужно писать супер класс который всё сможет сделать. Лучше написать простой класс, потом поверх него чуть сложнее, потом ещё сложнее если надо. Типа слой за слоем. И эти сложные слои должны находиться на стороне программы. Например, работа со сценой, рисование теней и т.д. Всё должно делаться на стороне программы.
Создаваемые вещи должны быть максимально независимыми. Если делаем рендер с уникальными технологиями, то более простые рендеры всё равно должны работать.
Шаблоны проектирования под сомнением. Их не будет до той поры пока я не осознаю их пользу.
Внутри основной библиотеки будет главный класс Framework. Он будет содержать все необходимые вещи, будет инициализировать библиотеки, иметь загрузчики ресурсов и т.д. Для простоты будет глобальный указатель на объект данного класса. Простая глобальная переменная. И ненужны никакие заумные вещи.
В общем, главное простота, а не выпендрёж.
Для простых вещей будут использоваться классы со статическими методами.
Структура проекта Я использую Visual Studio 2022. Буду использовать его возможности.
Папки: `build` - там лежат файлы проектов `inc` - #include файлы движка `libs` - туда прилетят скомпилированные библиотеки `src` - тут лежат исходные коды движка и сторонних библиотек.
По ходу прогресса появятся папки `data` - ресурсы для демки `bin32\bin64` - демка и инструмены `tools` - исходные коды для инструментов
Создание проекта Visual Studio Я всегда создаю пустые (Empty) проекты, и настраиваю их. Некоторые проекты я просто копирую в проводнике, редактирую в Notepad и кидаю в Visual Studio.
После создания проекта, надо добавить какой нибудь файл с исходным кодом. В папке 'src' надо создать папку для исходного кода движка. Далее создать текстовый файл и переименовать его установив расширение .cpp Далее кидаем этот файл в Visual Studio.
Это надо сделать, потому что Visual Studio не показывает C/C++ параметры если в проекте нет файла с исходным кодом.
Содержимое данного файла
Код
/* BSD 2-Clause License
Copyright (c) 2023, badcoiq All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "badcoiq.h"
В каждом файле (.h .c .c++ .cxx и т.д.) должна быть указана лицензия. Так-же практически в каждом файле (.c .cpp) потребуется минимальный набор include файлов. Все эти файлы указаны в `badcoiq.h`
Надо создать badcoiq.h Он будет находится в папке `inc`. Просто копирую badcoiq.cpp и изменю расширение.
Теперь настрою параметры проекта.
Параметр Target Name. В результате имя библиотеки получиться типа такое
badcoiq_v143_x86_Debug.lib
Укажу include папку
Для Release укажу оптимизацию
Можно изменить Floating Point Model Fast даст чуть скорости.
Указываю NODEFAULTLIB Возможно они и так будут игнориться, но укажу на всякий случай.
Всё, настройка завершена.
Далее нажимаю в редакторе правой кнопкой по badcoiq.h и выбираю Go To Document Для быстроты я привык нажимать просто Ctrl+Shift+G
Откроется badcoiq.h и можно приступить к его редактированию.
В данном моменте там должна быть лицензия и include guard
Код
#pragma once #ifndef __BQ_BADCOIQ_H__ #define __BQ_BADCOIQ_H__
#endif
Указываю #pragma once тоже, первее include guard
Всё, можно строить.
В папке libs будут такие файлы
В Visual Studio можно включить кнопку для построения. Правой кнопкой по GUI и выбираем Build. Добавлю кнопку Compile
Осталось отредактировать .gitignore и сделать коммит
Добавлю файл, который будет подключать стандартные файлы, и в котором будут макросы. Что такое макрос и для чего они нужны.
Макросы это то что создаётся используя слово define
Код
#define ABC
Они могут быть в виде функций.
Их используют чтобы добиться мультиплатформенного кода, для краткости написания кода и для много чего. Вставляешь макрос, и на его месте будет то что было справа от него.
Возможно ненужная вещь. Пережиток прошлого и всё такое. Многие библиотеки используют это. `inline` не гарантирует что функция будет inline. Предположу что "не гарантирует" если функция написана в .c .cpp файлах(или файл инклюдится в них), ведь такие функции не встраиваемые, по умолчанию. Вместо inline теперь надо писать BQ_FORCEINLINE
Добавлено (02 Июля 2023, 09:14) --------------------------------------------- Память
Я всегда делал DLL библиотеки, и требовалось чтобы выделение и освобождение памяти происходило в одном месте. Если в DLL память была выделена используя new, а в EXE она будет delete, то может так случиться что эти new и delete будут из разных мест (различные Run Time). От этого произойдёт ошибка и программа упадёт. Решение проблемы - создать место которое будет выделять\освобождать память.
Возможно для статических библиотек это не проблема. Но я планирую использовать .dll как плагины. Поэтому надо это создать и использовать только эти функции.
#ifdef BQ_PLATFORM_WINDOWS #define WIN32_LEAN_AND_MEAN #include <Windows.h> // Windows функции для работы с памятью требуют Process Heap // Ничего страшного в глобальной переменной нет. static HANDLE g_processHeap = GetProcessHeap(); #endif
Данные функции неудобно использовать для замены new и delete Потому что для объектов классов нужно вызывать конструктор и деструктор. Добавлю две функции которые будут выделять\освобождать память и вызывать конструктор\деструктор.
Добавлено (02 Июля 2023, 11:22) --------------------------------------------- DLL
Изначально был план дать возможность использовать библиотеку как DLL. Добавил функции для работы с DLL. Потом отказался от поддержки DLL версии, так как много возни. Но сами функции пригодятся в будущем.
Новый include файл
inc/badcoiq/system/bqDLL.h
Подобно bqMemory, это класс со статическими методами.
Код
// надо завести свои имена для хэндла и указателя на функцию typedef void* bqDLLHandle; // HMODULE в Windows typedef void* bqDLLFunction;
class bqDLL { public:
// Загрузить динамическую библиотеку // Вернётся указатель если загрузилось. // При завершении работы надо вызвать free и передать bqDLLHandle static bqDLLHandle load(const char* libraryName);
// Получить функцию из библиотеки // Вернёт NULL если была какая-то ошибка. static bqDLLFunction get_proc(bqDLLHandle library, const char* functionName); };
Если ошибка произойдёт, то в функцию передадутся выражение используемое в if (#expression), имя файла и строка где находится BQ_ASSERT.
assert работает только в debug режиме. Его нужно добавлять в код везде где можно, в релизной версии там будет пустота.
BQ_ASSERT вызвет функцию bq::internal::OnAssert() Эта функция должна быть реализована в библиотеке. Она должна использовать указатель на функцию - дефолтная OnAssert Дефолтную функцию можно изменить, послав свой указатель функцией SetOnAssert. ...этот код должен быть выше макросов BQ_ASSERT
Код
// Скрою в неймспейсах вспомогательные функции namespace bq { namespace internal { enum flag_onAssert { // использовать системное окно которое показывается как при использовании assert() flag_onAssert_useDefaultSystemWindow = 0x1,
// использовать stack tracer flag_onAssert_useStackTrace = 0x2, };
В своём assert я хотел иметь возможность использовать дефолтную Visual Studio функцию _wassert, но она требует wchar_t строку. Когда-то я замарочился и сделал 100% рабочий конвертер UTF-8\16\32 Я подумал и решил, что не нужен мне сам конвертер. Мне нужна UTF-32 строка. Эта строка должна иметь методы для конвертации.
Эту строку лучше использовать для текста который мы будем рисовать.
Для внутреннего использования необходимы более простые строки. Начну с них.
Полностью описывать классы будет очень долго. В коде есть комментарии. В общем всё элементарно. Можно объяснить основные принципы.
Класс строки содержит массив символов + 1 символ для завершающего нуля. При создании объекта надо заранее выделить немного памяти. При добавлении символов, если необходимо, перевыделяем память.
Главное реализовать функцию push_back. Все остальные функции (append assign operator=) и конструкторы в итоге будут вызывать push_back.
Код
template<typename other_type> bqString_base(const other_type* str) { _reallocate(m_allocated); assign(str); // просто присвоение } ... // присвоить содержимое другой строки template<typename other_type> void assign(other_type str) { clear(); if (str) append(str); } ... template<typename other_type> void append(const other_type* str) { append(str, bqStringLen(str)); } ... // добавить в конец template<typename other_type> void append(const other_type* str, size_t l) { for (size_t i = 0; i < l; ++i) { push_back((char_type)str[i]); } } ... // добавить в конец символ void push_back(char_type c) { size_t new_size = m_size + 1u; if ((new_size + 1u) > m_allocated) _reallocate((new_size + 1u) + (1 + (uint32_t)(m_size * 0.5f))); m_data[m_size] = c; m_size = new_size; m_data[m_size] = 0; }
Очень необходимо реализовать добавление чисел. Например float:
Код
void append(float f) { char buf[32]; int nob = sprintf_s(buf, 32, "%.4f", f); if (nob) { for (int i = 0; i < nob; ++i) { push_back((char_type)buf[i]); } } }
Полный код шаблонной строки не влезает в сообщение. Реализовано куча полезных методов.
Код
// Функция чтобы получить длинну строки template<typename other_type> size_t bqStringLen(const other_type* str) { // Считаем пока не встретится ноль unsigned int len = 0u; if (str[0] != 0) { const other_type* p = &str[0u]; while ((size_t)*p++) ++len; } return len; }
// Традиционный строковый класс template<typename char_type> class bqString_base { char_type* m_data = 0; // массив size_t m_allocated = 10; // количество символов которое влезает в m_data учитывая завершающий ноль size_t m_size = 0; // текущее количество символов
// если количество символов m_size равно или больше m_allocated то надо вызвать это // указав новый m_allocated void _reallocate(size_t new_allocated) { // выделение новой памяти, копирование содержимого, освобождение старой, сохранение нового указателя char_type* new_data = (char_type*)bqMemory::malloc(new_allocated * sizeof(char_type)); if (m_data) { memcpy(new_data, m_data, m_size * sizeof(char_type)); bqMemory::free(m_data); } else { memset(new_data, 0, new_allocated); } m_data = new_data; m_allocated = new_allocated; }
public: // в конструкторах надо вызвать _reallocate() чтобы инициализировать массив m_data bqString_base() { _reallocate(m_allocated); }
Для конвертации в UTF-8 и UTF-16 LE я использую таблицу, заранее вычисленную.
Код
struct slUnicodeCharNode { uint32_t m_utf8; // код для UTF-8 uint32_t m_utf16;// код для UTF-16 }; static slUnicodeCharNode g_UnicodeChars[0x32000] = { #include "UnicodeChars.inl" // коды здесь };
Добавление char* и wchar_t* строк. По сути конвертация UTF-8 и UTF-16 в UTF-32
Код
// UTF-8 to UTF-32 void bqString::append(const char* str) { size_t len = bqStringLen(str);
// для to_utf8 to_utf16. union UC { uint8_t bytes[4]; uint16_t shorts[2]; uint32_t integer; }; ... void bqString::to_utf8(bqStringA& stra) const { stra.clear(); UC uc; auto ut = &g_UnicodeChars[0]; for (size_t i = 0; i < m_size; ++i) { char32_t c = m_data[i]; if (c >= 0x32000) c = '?';
uc.integer = ut[c].m_utf8;
if (uc.bytes[3]) stra.push_back(uc.bytes[3]); if (uc.bytes[2]) stra.push_back(uc.bytes[2]); if (uc.bytes[1]) stra.push_back(uc.bytes[1]); if (uc.bytes[0]) stra.push_back(uc.bytes[0]); } }
void bqString::to_utf16(bqStringW& strw) const { strw.clear(); UC uc; auto ut = &g_UnicodeChars[0]; for (size_t i = 0; i < m_size; ++i) { char32_t c = m_data[i]; if (c >= 0x32000) c = '?';
uc.integer = ut[c].m_utf16;
if (uc.shorts[1]) strw.push_back(uc.shorts[1]); if (uc.shorts[0]) strw.push_back(uc.shorts[0]); } }
Полный код не влезает в сообщение. В общем-то там много дублирования простой строки. Ещё есть дополнительные методы типа to_upper.
Почему-то корректно работает только со статическими библиотеками.
inc/badcoiq/system/bqStacktracer.h
Код
class bqStackTracer { public: static void Print(); };
Реализация src/badcoiq/system/bqStacktracer.cpp
Код взят из Havok Постарался чуть изменить. В общем ничего особенного нет в плане авторского права, одни вызовы системных функций.
Грузим системные библиотеки, получаем указатели на функции, вызываем эти функции. Детали слишком специфичны для ОС, не буду их описывать. Если почитать MSDN то всё станет понятно.
int bqStackTracerImpl::GetStackTrace(size_t* trace, int maxtrace, int framesToSkip) { _init(); if (m_RtlCaptureStackBackTrace) return m_RtlCaptureStackBackTrace(framesToSkip, maxtrace, (PVOID*)trace, NULL); return 0; }
Сама реализация находится в отдельном классе (bqStackTracerImpl). Пока чисто Windows версия. Имеем глобальный объект (g_stackTracer). Статический метод(bqStackTracer::Print) использует его.
Можно создать тестовую программу:
Код
// не забыть указать inc и libs папки в параметрах проекта #include "badcoiq.h"
Добавлено (02 Июля 2023, 19:25) --------------------------------------------- Logging
Надо выводить текстовую информацию, ошибки и предупреждения. Нужно сделать так чтобы можно было указать свою функцию для вывода. Например, чтобы выводить в файл, или в игровую консоль.
+ inc/badcoiq/common/bqLog.h
Код
// Класс для вывода текстового сообщения // Можно установить коллбэки class bqLog { public: static void Print(const char* s, ...); static void Print(const wchar_t* s, ...);
* Запрещено использовать STL в API. Необходимо написать свои вещи.
Я изучал исходный код некоторых контейнеров из STL в gcc 8.0. Не увидел там ничего такого, что способно вызвать тормоза. Да, в STL есть ряд неудобных вещей, являющихся частью реализации. Чем хорош STL - тем, что он отлично документирован и практик изучает его сразу с изучением C++. То есть это вещь хорошо знакома программисту на C++. Можно сказать, что STL откладывается в подкорке. Ну есть еще (где-то читал-слышал и отложилось в памяти, что якобы исключения C++ - это зло для консольных игр). Таким образом, с точки зрения интерфейса, STL - это лучшая из открытых свободных библиотек. Но с точки зрения реализации - да, нужно или писать его аналог (что я и делаю в рамках тех базовых структур данных, что часто употребляются в сегменте геймдева), или же писать свое. Я выбрал писать свою реализацию, потому что не хочу тратить ресурсы на создание документации и поддержание в уме еще одного набора интерфейсов.
Добавлено (02 Июля 2023, 20:15) --------------------------------------------- В целом неплохо написано, мышление процентов на 60 совпадает с моим. Мотивируешь.
Добавлено (02 Июля 2023, 20:18) --------------------------------------------- Да, насчет исключений. Я в библиотеке своей завел макрос, который выключает исключения и меняет реализацию и интерфейс (с точки зрения возвращаемого значения) классов. Так что больше я в STL кроме этого зла не вижу. Потоки, конечно, использовать не стоит для работы с файлами, лучше писать свой контролируемый код.
Все те действия что были описаны выше НЕОБХОДИМЫ. Ошибки надо показывать, для этого нужна своя строка (ибо херня из STL однажды может дать 0, где нибудь в конвертации юникода). Строку нужно написать чтобы удовлетворяла, например чтобы тупо можно было добавить число. Для строки нужны функции для работы с памятью.
Это минимум который должен быть.
Перед тем как продолжить, надо добавить ещё один .h файл inc/badcoiq/common/bqForward.h
Там будут forward declarations.
Код
class bqWindow; class bqWindowCallback;
bqForward.h так-же должен быть указан в badcoiq.h
Что делает forward declarations? Если мы делаем класс и в нём есть объект
Код
class CClass{ ... MyObject m_object; };
То компилятору нужно знать сколько памяти выделить для этого объекта. Компилятор должен видеть описание MyObject. Можно вставить #include, но в итоге время компиляции увеличится. Давно я делал ДВИЖЁК где все include были в одном файле. Это плохой подход.
Нужно использовать указатель.
Код
class MyObject; //forward declaration class CClass{ ... MyObject* m_object = 0; };
Компилятор знает размер указателя и проблем не будет. Нужно всего лишь вставить forward declaration - добавить class ИмяКласса.
В будущем все классы которые не будут входить в минимальный набор (указанный в badcoiq.h) будут добавляться в forward declaration.
Теперь ДВИЖЁК
Framework
Будет внутренний класс, в котором будут находиться все объекты необходимые для работы ДВИЖКА. Управление фреймворком будет происходить через класс со статическими методами
+ inc/badcoiq/framework/bqFramework.h
Код
// Предполагаю что фреймворк должен посылать какие-то сообщения. // Я это ещё не реализовал class bqFrameworkCallback { public: bqFrameworkCallback() {} virtual ~bqFrameworkCallback() {}
virtual void OnMessage() = 0; };
// API для фреймворка. class bqFramework { public: static void Start(bqFrameworkCallback*); static void Stop(); static void Update();
Когда нам нужно запустить ДВИЖЁК, вызываем Start. Он выделит память для внутреннего класса. Stop для завершения работы, он освободит ту память. Update - например, он вычислит DeltaTime, обновит состояния ввода, GUI, окна. Delta Time - время между двумя вызывами Update. Update надо вызывать каждую итерацию главного цикла. Соответственно, это время затраченное на одну итерацию(так-же можно сказать время на рисование 1го кадра). SummonWindow - создать окно. Имя CreateWindow занято WinAPI, как и многие другие. Я решил использовать слово Summon(призвать) вместо Create(создать).
Теперь реализация. src/badcoiq/framework/bqFramework.cpp
Класс который будет хранить всё необходимое.
Код
class bqFrameworkImpl { public: bqFrameworkImpl() {} ~bqFrameworkImpl() {}
Есть шанс что Stop не будет вызван. Надо добавить класс который при закрытии программы в деструкторе вызовет Stop
Код
class bqFrameworkDestroyer { public: bqFrameworkDestroyer() {} ~bqFrameworkDestroyer() { if (g_framework) bqFramework::Stop(); } }; // При закрытии программы этот объект будет уничтожен - будет вызван деструктор // в котором будет вызван bqFramework::Stop(); bqFrameworkDestroyer g_frameworkDestroyer;
Ну вот и всё. Далее необходимо создать окно, и реализовать Update
Зачем этот велосипед? (риторический вопрос) Закон Мерфи: "Если вы уверены, что ваш поступок встретит всеобщее одобрение, кому-то он обязательно не понравится".
Ну вообще движки делают уже состоявшиеся разработчики. Таким людям уже 30+ это точно. Это молодежь 16+ всегда задается вопросом зачем делать движок. Это как отдельное творчество, когда тебе нужно не только указать персонажу сходить по готовому алгоритму к другому персонажу и что-то сделать. Такая простота если честно может добить разработчика, который любит решать сложные вещи. Да, который любит решать сложные вещи. Я каждый свой движок почти что пишу заново, только хорошие части копирую, так как невозможно придумать сразу хорошо, когда мало опыта и большой проект. Обычно ввязываются в конфликт насчет движков молодые неокрепшие умы, которые только любят игры и больше ничего не умеют делать, кроме как на construct или unity задать логику для передвижения персонажа.
Игрострой это не только сюжет, дизайн и т.д. Это ещё и технологии, то что скрыто под капотом.
Ну вот если вспомнить детство, во что играли, денди, сега, кто-то играл на более древних консолях. Я лично всегда задавался вопросом "как сделано то, как сделано это".
Когда ковыряешься в этих вещах, приходят мысли "с этой вещью можно сделать то-то". Изучаешь всё это, и рамки, границы возможностей расширяются.
Моя граница упёрлась в физику. Всё сделано по списку, осталось только она. Я хочу переписать всё, документируя что возможно, и написать свою физику. И я опишу начальные шаги, и все свои ошибки, и 100% всё сделаю, с оптимизациями. Книжку прочитал, оказывается это элементарщина. Будет интересно реализовать.
Как только будет реализовано (всё по списку), буду делать игрульку. Ресурсы есть. Главное не напрягаться и заниматься тем что нравится. "Do you think we can fly? Well, I do."
Ну вообще движки делают уже состоявшиеся разработчики.
Рад прочитать комментарий матерого состоявшегося разработчика, который уже в своем следующем предложении себе противоречит и пишет, что не может продумать свой код и каждый раз заново пишет один и тот же 2D движок. Если серьезно, то читая эту портянку (да и портянки в других темах), как раз таки и сложилось впечатление, что прочитал текст 15-ти летнего школьника, который ни разу не создавал законченный продукт, не говоря уже об огромном солюшине с десятками вложенных проектов, сотнями тысяч строк кода и кучей второстепенных проектов.
Добавлено (04 Июля 2023, 18:38) ---------------------------------------------
ЦитатаBADCOIQ ()
Книжку прочитал, оказывается это элементарщина. Будет интересно реализовать.
Жду в репозитории. А то тут PhysX уже делают лет 20, но все никак не доделают, все выходят новые и новые версии, а вроде бы элементарные вещи, да?!