1. Создаём ядро игры
Содержание
- 1. Создаём ядро игры
- Intro
- Устанавливаем Microsoft Windows SDK for Windows 7
- Устанавливаем Microsoft DirectX SDK 8
- Создаём пустой Проект Core
- Настраиваем Проект Core
- Создаём Core_Global.h
- Создаём Core_System.h
- Создаём Core_System.cpp
- Настраиваем Проект Core: указываем пути к DirectX SDK 8.1
- Настраиваем Проект Core: указываем пути к Windows SDK
- Компилируем Проект Core
- Итоги главы
- Источники:
Intro
Вообще, ядро будет не одно, а целых 5:1
- System Core (Системное ядро)
- Graphics Core (Графическое ядро)
- Input Core (Ядро ввода)
- Sound Core (Звуковое ядро)
- Network Core (Сетевое ядро).
Все они будут заключены в одной lib-библиотеке, подключаемой к игровому Проекту через контестное меню MS Visual C++ 2010.
Устанавливаем Microsoft Windows SDK for Windows 7
Windows SDK содержит в себе набор необходимых заголовков, библиотек, документации и утилит для создания приложений под ОС Windows 7. На Windows SDK опирается MS Visual Studio при разработке приложений в том числе под DirectX. Содержит в себе такие важные заголовки, как windows.h, windowsx.h, winnt.h. Без них исходный код большинства Windows-приложений не станет компилироваться.


Существуют SDK и для других ОС Windows. То есть, если прогаешь под Win10, погугли Windows SDK под свою ОС. Софт с Win7 без труда пойдёт под Win10.
Данная версия Windows SDK ставится автоматом вместе с MS Visual Studio 2010.
Если нет, то его можно установить отдельно:
Англоязычный инсталлятор не слишком докучает вопросами. Просто жмём Next, Next...
Путь установки по умолчанию (Win7 x64): c:\Program Files\Microsoft SDKs\Windows\v7.1\
Устанавливаем Microsoft DirectX SDK 8
В нашем случае это Microsoft DirectX SDK 8.1 . Представляет собой набор необходимых библиотек, документации и утилит для создания приложений с использованием DirectX. Вышел в свет аж в 2001 г. До сих пор активно применяется разработчиками инди-игр и специализированных приложений. Кладезь информации по программированию графики средствами DirectX посредством неуправляемого (unmanaged, т.е. на чистом C++ без .NET-фреймворка) кода.
В DirectX SDK 8.1 есть куча уникального стафа, тупо вырезанного в последующих версиях. Кроме того, данный курс написан для DirectX 8.
Объём прибл. 164 Мб.
- Скачанный ZIP-архив извлекаем (распаковываем) в любую папку на жёстком диске и запускаем Setup.exe.
Процесс установки несложен.
- На странице программы-установщика с лицензионным соглашением поставь галку у пункта 'I Accept the terms of the license agreement' (принимаем его условия вобщем). Жми Next.
- На странице 'Customer information' в поля 'User Name' и 'Company Name' введи любые значения. Жми Next.
Каталог установки по умолчанию C:\DXSDK можно оставить как есть. Но мы переименуем его в C:\DXSDK8. Для наглядности.
Список устанавливаемых компонентов тоже оставь как есть. Пусть ставится всё. Жми Next.


Желательно также скачать и установить (в отдельную папку) последнюю версию DirectX SDK с сайта Microsoft. На всякий случай.
DirectX SDK 8.1 не имеет своей программной оболочки. Для просмотра содержимого открой соответствующий каталог (в нашем случае C:\DXSDK8) с помощью Проводника Windows.
В соответствующих каталогах расположены файлы:
bin | Внутри него расположен единственный каталог DXUtils, содержащий различный утилиты (=вспомогательные программы). Самая интересная из них - mview.exe (Mesh viewer, просмотрщик моделей формата .x). Позднее мы не раз увидим её в деле. |
doc | Документация по DirectX SDK. Всё на английском языке. Размещена в двух CHM-файлах (файлы справки Windows): directx8_c для языка программирования C++ и directx8_vb для языка программирования Visual Basic. Компилированные файлы справки Windows (CHM) имеют удобную древовидную навигацию и поиск. Напомним, в данном курсе весь исходный код представлен на языке C++. |
include | Заголовочные файлы для импорта функций DirectX. Самый нужный каталог. Путь к нему мы чуть позднее укажем в настройках MS Visual C++. |
lib | Библиотеки DLL, содержащие функции DirectX. Самый нужный каталог. Путь к нему мы чуть позднее укажем в настройках MS Visual C++. |
Redist | Т.н. "библиотека времени выполнения". Необходима для запуска игр, спрограммированных под MS DirectX, на ПК. Включена по умолчанию во все версии ОС MS Windows XP/Vista/7. Но на всякий случай добавлена в DirectX SDK. |
samples | 109 мегабайт примеров! Разложены по папкам в виде проектов, у каждого из которых есть 2 главных файла: dsp-файл проекта и dsw-файл (т.н. воркспейс, англ. workspace - рабочее пространство). Оба они являются "родными" для актуальной на тот момент MS Visual C++ 6. В принципе они равнозначны, отличаются нюансами. dsw-файлы служат для объединения нескольких проектов (=dsp-файлов) в группу. Если открыть dsw-файл в блокноте, то в нём можно увидеть путь к dsp-файлу. Dsw-воркспейсы открываются и компилируются в исполняемый .exe файл в (правильно сконфигурированной) среде MS Visual Studio 6.0 (1998 года). |
Создаём пустой Проект Core
- Стартуй MS Visual Studio 2010, если не сделал этого ранее.
Видим Окно приветствия (т.е. в IDE ни один Проект пока не открыт).
- Создай новый пустой Проект Win32. (В Главном меню: Файл->Создать->Проект...).
В окне "Создать Проект":
- Отмечаем щелчком пункт "Проект Win32".
- В поле Имя введи Test01.
Строки "Расположение" и "Имя Решения" заполняются автоматически (при необходимости изменяем).
Под Проект Test01 будет автоматически создано Решение с тем же именем.
- Жмём ОК.
Появится окно Мастера приложений.
- Жмём Далее.
Напомним, что в первой части данного курса мы программируем игровое ядро (или движок), которое физически будет содержаться в одном файле статической библиотеки с расширением .lib. Поэтому:
- На странице "Параметры приложения" отмечаем пункт "Статическая библиотека" и убираем галку у пункта "Предварительно скомпилированный заголовок":
- Жмём "Готово".
При создании наш Проект автоматически размещается внутри Решения с тем же именем. В результате в левой колонке обозревателя решений видим Решение Test01, которое содержит Проект Test01.
Это неправильно, так как согласно замысла, внутри Решения Test01 будут содержаться два Проекта: Core (ядро, движок; статическая библиотека с расширением .lib) и Game (игра на базе этого движка; файл приложения с расширением .exe). Напомним, что в первой части курса мы разрабатываем движок.
- Поэтому смени имя вновь созданного Проекта с Test01 на Core (правой кнопкой мыши по названию Проекта -> из контекстного меню выбрать "Переименовать").
Решение тоже переименуй на своё усмотрение.
- Сохрани Решение (File -> Save All).
Проект создан. Так как это "Пустой проект", он не содержит в себе никаких файлов. В левой части главного окна MS Visual C++ 2010 расположен "Обозреватель решений". (Если его нет, в главном меню выбираем: Вид->Другие окна->Обозреватель решений. Или комбинация горячих клавиш Ctrl+Alt+L.) В Обозревателе решений видна древовидная структура Проектов, входящих в данное Решение. Чуть ниже названия Проекта видим специально заготовленные папки (в MSVC++2010 они называются "фильтры") для файлов Проекта.
Настраиваем Проект Core
Выбираем мультибайтовую кодировку


В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...
- В Обозревателе решений щёлкаем по названию только что созданного Проекта Core.
- Во всплывающем меню выбираем "Свойства"
- В появившемся окне установки свойств Проекта жмём Свойства конфигурации->Общие, в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
- Жмём ОК.
- Сохрани Решение (Файл->Сохранить все)
Напомним, что такую настройку необходимо повторно проделывать при создании каждого нового Проекта.
Отключаем инкрементную компоновку (incremental linking)
Инкрементная компоновка призвана сократить время компилирования. Но на деле её присутствие часто вызывает ошибки вроде этой:
Error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден
Отключается в свойствах открытого Проекта. Для MS Visual C++ 2010 порядок следующий:
- В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства.
- В появившемся окне установки свойств Проекта жмём Свойства конфигурации -> Компоновщик -> Общие (Configuration Properties -> Linker -> General), в правой части в строке "Включить инкрементную компоновку" ставим значение "Нет (/INCREMENTAL:NO)".
- Жмём ОК.
Создаём Core_Global.h
Напомним, что именно Core_Global.h будет включать в себя ссылки на остальные заголовки движка и, таким образом, будет использоваться в качестве единой точки контакта.
- В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы".
- Во всплывающем меню Добавить->Создать элемент...
- В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_Global.h.
- Жмём "Добавить".
Добавленный файл сразу откроется в правой части MSVC++2010.
- В только что созданном и открытом файле Core_Global.h набираем следующий код:
/************************************************** Core_Global.h GameCore Component Original SourceCode: Programming Role-Playing Games with DirectX by Jim Adams (01 Jan 2002) **************************************************/ #ifndef _CORE_GLOBAL_H_ #define _CORE_GLOBAL_H_ //----------------------------------- // Макроопределение на использование 8 версии DirectInput (Функционала 8 версии будет предостаточно) //----------------------------------- #define DIRECTINPUT_VERSION 0x0800 // Windows includes #include <windows.h> // Standard ANSI-C includes #include <stdio.h> // DirectX includes #include "d3d8.h" #include "d3dx8.h" #include "dmusici.h" #include "dsound.h" #include "dplay8.h" #include "dpaddr.h" #include "dinput.h" // Core includes #include "Core_System.h" #endif
- Сохрани созданное Решение и входяшие в него Проекты (Файл -> Сохранить все).
Содержит в себе кучу инклудов из DirectX SDK 8.1.
Создаём Core_System.h
- В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы".
- Во всплывающем меню Добавить->Создать элемент...
- В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_System.h.
- Жмём "Добавить".
Добавленный файл сразу откроется в правой части MSVC++2010.
- В только что созданном и открытом файле Core_System.h набираем следующий код:
/************************************************** Core_System.h Заголовок Системного ядра. Содержит объявления классов и функций, чьи реализации размещены в Core_System.cpp Original SourceCode: Programming Role-Playing Games with DirectX by Jim Adams (01 Jan 2002) **************************************************/ #ifndef _SYSTEM_H_ #define _SYSTEM_H_ class cApplication { private: HINSTANCE m_hInst; HWND m_hWnd; protected: char m_Class[MAX_PATH]; char m_Caption[MAX_PATH]; WNDCLASSEX m_wcex; DWORD m_Style; DWORD m_XPos; DWORD m_YPos; DWORD m_Width; DWORD m_Height; public: cApplication(); HWND GethWnd(); HINSTANCE GethInst(); BOOL Run(); BOOL Error(BOOL Fatal, char *Text, ...); BOOL Move(long XPos, long YPos); BOOL Resize(long Width, long Height); BOOL ShowMouse(BOOL Show = TRUE); virtual LRESULT CALLBACK MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hWnd, uMsg, wParam, lParam); } virtual BOOL Init() { return TRUE; } virtual BOOL Shutdown() { return TRUE; } virtual BOOL Frame() { return TRUE; } }; static cApplication *g_pApplication = NULL; static long FAR PASCAL AppWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); enum Purposes { NOPURPOSE = 0, INITPURPOSE, SHUTDOWNPURPOSE, FRAMEPURPOSE }; class cStateManager { typedef struct sState { void (*Function)(void *Ptr, long Purpose); sState *Next; sState() { Function = NULL; Next = NULL; } ~sState() { delete Next; } } sState; protected: sState *m_StateParent; public: cStateManager(); ~cStateManager(); void Push(void (*Function)(void *Ptr, long Purpose), void *DataPtr = NULL); BOOL Pop(void *DataPtr = NULL); void PopAll(void *DataPtr = NULL); BOOL Process(void *DataPtr = NULL); }; class cProcessManager { typedef struct sProcess { void (*Function)(void *Ptr, long Purpose); sProcess *Next; sProcess() { Function = NULL; Next = NULL; } ~sProcess() { delete Next; } } sProcess; protected: sProcess *m_ProcessParent; public: cProcessManager(); ~cProcessManager(); void Push(void (*Process)(void *Ptr, long Purpose), void *DataPtr = NULL); BOOL Pop(void *DataPtr = NULL); void PopAll(void *DataPtr = NULL); void Process(void *Ptr = NULL); }; class cDataPackage { protected: void *m_Buf; unsigned long m_Size; public: cDataPackage(); ~cDataPackage(); void *Create(unsigned long Size); void Free(); BOOL Save(char *Filename); void *Load(char *Filename, unsigned long *Size); void *GetPtr(); unsigned long GetSize(); }; #endif
- Сохрани созданное Решение и входяшие в него Проекты (Файл -> Сохранить все).
Исследуем код Core_System.h
Помимо всего прочего, в Core_System.h объявлены 3 важных класса:
- cStateManager (менеджер стейтов),
- cProcessManager (менеджер процессов),
- cDataPackage (система сохранения и загрузки данных).
Три данных класса в том или ином виде почти повсеместно применяются при создании каркаса (=framework) игрового движка. Подробнее о них читай в статье Программный поток (Program Flow). Чуть ниже мы увидим, как файле исходного кода Core_System.cpp будут созданы (пустые) объекты данных классов.
Создаём Core_System.cpp
Если помнишь, для написания хорошо структурированного кода мы размещаем объявления всех классов и функций в заголовочных файлах (.h), а их реализацию в файлах исходного кода (.cpp), в которых также прописываем ссылку на "сопровождающий" его хэдер (=заголовочный файл .h).
Создадим файл исходного кода Core_System.cpp, в котором будут содержаться реализации функций, объявленных в заголовочном файле Core_System.h:
- В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Файлы исходного кода" Проекта Core.
- Во всплывающем меню Добавить->Создать элемент...
- В появившемся окне выбери "Файл С++ (.cpp)" и в поле "Имя" введи Core_System.cpp.
- Жмём "Добавить".
Добавленный файл сразу откроется в правой части MSVC++2010.
- В только что созданном и открытом файле Engine.cpp набираем следующий код:
/************************************************** Core_system.cpp GameCore Component Original SourceCode: Programming Role-Playing Games with DirectX by Jim Adams (01 Jan 2002) **************************************************/ #include "Core_Global.h" cApplication::cApplication() { // Save instance handle. Сохраняем дескриптор инстанса. g_pApplication = this; // Get the instance handle. Получаем дескриптор инстанса. m_hInst = GetModuleHandle(NULL); // Set a default window class and caption strcpy(m_Class, "AppClass"); strcpy(m_Caption, "Application Caption"); // Set default window style, position, width, height m_Style = WS_OVERLAPPEDWINDOW; m_XPos = 0; m_YPos = 0; m_Width = 256; m_Height = 256; // Set default WNDCLASSEX structure. Заполняем структуру оконного класса WNDCLASSEX. m_wcex.cbSize = sizeof(WNDCLASSEX); m_wcex.style = CS_CLASSDC; m_wcex.lpfnWndProc = AppWindowProc; m_wcex.cbClsExtra = 0; m_wcex.cbWndExtra = 0; m_wcex.hInstance = m_hInst; m_wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); m_wcex.hCursor = LoadCursor(NULL, IDC_ARROW); m_wcex.hbrBackground = NULL; m_wcex.lpszMenuName = NULL; m_wcex.lpszClassName = m_Class; m_wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); } HWND cApplication::GethWnd() { return m_hWnd; } HINSTANCE cApplication::GethInst() { return m_hInst; } BOOL cApplication::Run() { MSG Msg; // Register window class if(!RegisterClassEx(&m_wcex)) return FALSE; // Create the Main Window m_hWnd = CreateWindow(m_Class, m_Caption, m_Style, m_XPos, m_YPos, m_Width, m_Height, NULL, NULL, m_hInst, NULL); if(!m_hWnd) return FALSE; // Show and update the window ShowWindow(m_hWnd, SW_NORMAL); UpdateWindow(m_hWnd); // Make sure client area is correct size. Проверяем, что клиентская область окна нужного размера. Resize(m_Width, m_Height); // Initialize COM CoInitialize(NULL); if(Init() == TRUE) { // Enter the message pump. Входим в цикл выборки сообщений. ZeroMemory(&Msg, sizeof(MSG)); while(Msg.message != WM_QUIT) { // Handle Windows messages (if any). Обрабатываем сообщения Windows (если таковые есть). if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } else { // Do per-frame processing, break on FALSE return value // Выполняем ежекадровый процессинг. Выходим, если текущий фрейм - FALSE. if(Frame() == FALSE) break; } } } Shutdown(); // Shutdown COM CoUninitialize(); // Unregister the window class UnregisterClass(m_Class, m_hInst); return TRUE; } BOOL cApplication::Error(BOOL Fatal, char *Text, ...) { char CaptionText[12]; char ErrorText[2048]; va_list valist; // Build the message box caption based on fatal flag // Создаём диалоговое окно с заголовком, основываясь на содержимом флага критической ошибки. if(Fatal == FALSE) strcpy(CaptionText, "Error"); else strcpy(CaptionText, "Fatal Error"); // Build variable text buffer // Создаём изменяемый текстовый буфер va_start(valist, Text); vsprintf(ErrorText, Text, valist); va_end(valist); // Display the message box MessageBox(NULL, ErrorText, CaptionText, MB_OK | MB_ICONEXCLAMATION); // Post a quit message if error was fatal if(Fatal == TRUE) PostQuitMessage(0); return TRUE; } BOOL cApplication::Move(long XPos, long YPos) { RECT ClientRect; GetClientRect(m_hWnd, &ClientRect); MoveWindow(m_hWnd, XPos, YPos, ClientRect.right, ClientRect.bottom, TRUE); return TRUE; } BOOL cApplication::Resize(long Width, long Height) { RECT WndRect, ClientRect; long WndWidth, WndHeight; GetWindowRect(m_hWnd, &WndRect); GetClientRect(m_hWnd, &ClientRect); WndWidth = (WndRect.right - (ClientRect.right - Width)) - WndRect.left; WndHeight = (WndRect.bottom - (ClientRect.bottom - Height)) - WndRect.top; MoveWindow(m_hWnd, WndRect.left, WndRect.top, WndWidth, WndHeight, TRUE); return TRUE; } BOOL cApplication::ShowMouse(BOOL Show) { ShowCursor(Show); return TRUE; } // The message procedure - empty except for destroy message. // Стандартная процедура выборки сообщений. Пуста, за исключением сообщения DESTROY, закрывающем окно. long FAR PASCAL AppWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; default: return g_pApplication->MsgProc(hWnd, uMsg, wParam, lParam); } } cStateManager::cStateManager() { m_StateParent = NULL; } cStateManager::~cStateManager() { PopAll(); } // Push a function on to the stack void cStateManager::Push(void (*Function)(void *Ptr, long Purpose), void *DataPtr) { sState *StatePtr; // Don't push a NULL value // Не помещать нулевое значение if(Function != NULL) { // Allocate a new state and push it on stack // Создать новый стейт и поместить его в стэк. StatePtr = new sState(); StatePtr->Function = Function; StatePtr->Next = m_StateParent; m_StateParent = StatePtr; // Call state with init purpose StatePtr->Function(DataPtr, INITPURPOSE); } } BOOL cStateManager::Pop(void *DataPtr) { sState *StatePtr; // Remove the head of stack (if any) if((StatePtr = m_StateParent) != NULL) { // First call with shutdown purpose m_StateParent->Function(DataPtr, SHUTDOWNPURPOSE); m_StateParent = StatePtr->Next; StatePtr->Next = NULL; delete StatePtr; } // return TRUE if more states exist, FALSE otherwise if(m_StateParent == NULL) return FALSE; return TRUE; } void cStateManager::PopAll(void *DataPtr) { while(Pop(DataPtr) == TRUE); } BOOL cStateManager::Process(void *DataPtr) { // return an error if no more states if(m_StateParent == NULL) return FALSE; // Process the top-most state m_StateParent->Function(DataPtr, FRAMEPURPOSE); return TRUE; } cProcessManager::cProcessManager() { m_ProcessParent = NULL; } cProcessManager::~cProcessManager() { // Pop each process while(Pop()==TRUE); } // Push a function on to the stack void cProcessManager::Push(void (*Process)(void *Ptr, long Purpose), void *DataPtr) { // Don't push a NULL value if(Process != NULL) { // Allocate a new process and push it on stack sProcess *ProcessPtr = new sProcess(); ProcessPtr->Function = Process; ProcessPtr->Next = m_ProcessParent; m_ProcessParent = ProcessPtr; // Call process with init purpose ProcessPtr->Function(DataPtr, INITPURPOSE); } } // Pop top process from stack BOOL cProcessManager::Pop(void *DataPtr) { sProcess *ProcessPtr; // Remove the head of stack (if any) if((ProcessPtr = m_ProcessParent) != NULL) { // First call with shutdown purpose m_ProcessParent->Function(DataPtr, SHUTDOWNPURPOSE); m_ProcessParent = ProcessPtr->Next; ProcessPtr->Next = NULL; delete ProcessPtr; } // return TRUE if more processes exist, FALSE otherwise if(m_ProcessParent == NULL) return FALSE; return TRUE; } void cProcessManager::PopAll(void *DataPtr) { while(Pop(DataPtr) == TRUE); } // Process all functions void cProcessManager::Process(void *DataPtr) { sProcess *ProcessPtr = m_ProcessParent; while(ProcessPtr != NULL) { ProcessPtr->Function(DataPtr, FRAMEPURPOSE); ProcessPtr = ProcessPtr->Next; } } cDataPackage::cDataPackage() { m_Buf = NULL; m_Size = 0; } cDataPackage::~cDataPackage() { Free(); } void *cDataPackage::Create(unsigned long Size) { // Free a previously created buffer. Обнуляем ранее созданный буфер. Free(); // Allocate some memory and return a pointer return (m_Buf = (void*)new char[(m_Size = Size)]); } // Free the allocated memory void cDataPackage::Free() { delete m_Buf; m_Buf = NULL; m_Size = 0; } BOOL cDataPackage::Save(char *Filename) { FILE *fp; // Make sure there's something to write // Проверим, что в самом деле есть, что записывать. if(m_Buf != NULL && m_Size) { // Open file, write size and data if((fp=fopen(Filename, "wb")) != NULL) { fwrite(&m_Size, 1, 4, fp); fwrite(m_Buf, 1, m_Size, fp); fclose(fp); return TRUE; } } return FALSE; } void *cDataPackage::Load(char *Filename, unsigned long *Size) { FILE *fp; // Free a prior buffer Free(); if((fp=fopen(Filename, "rb"))!=NULL) { // Read in size and data fread(&m_Size, 1, 4, fp); if((m_Buf = (void*)new char[m_Size]) != NULL) fread(m_Buf, 1, m_Size, fp); fclose(fp); // Store size to return if(Size != NULL) *Size = m_Size; // return pointer return m_Buf; } return NULL; } void *cDataPackage::GetPtr() { return m_Buf; } unsigned long cDataPackage::GetSize() { return m_Size; }
- Сохрани Решение (Файл -> Сохранить все).
Для тех, кто скажет "чё так много", спешим напомнить, что это, по сути, пустой каркас, куда позднее будут добавляться другие элементы.
Исследуем код Core_System.cpp
Следуя ранее утверждённой концепции "единой точки контакта", подключаем заголовок Core_Global.h, который уже содержит ссылки на все остальные заголовочные файлы:
... #include "Core_Global.h" ...
В конце листинга видим реализацию трёх классов, которые пригодятся нам в будущем:
- cStateManager (менеджер стейтов),
- cProcessManager (менеджер процессов),
- cDataPackage (система работы с данными приложения).
Все они подробно рассматриваются в статье Программный поток (Program Flow).
Настраиваем Проект Core: указываем пути к DirectX SDK 8.1
Если попытаться скомпилировать Проект Core в таком виде, то ничего не выйдет.
В заголовке Core_Global.h можно увидеть инклуды различных заголовочных файлов DirectX (весии 8). MSVC++2010 ничего не знает об их местоположении. В статье Настройка MS Visual C plus plus 2010 и DirectX SDK мы указывали пути к DirectX SDK (версии 9 и выше). Для DirectX SDK 8 это делается аналогично. Начнём.
- В Обозревателе решений видим: "Решение "Test01"", а строкой ниже жирным шрифтом название Проекта (Core). Щелчок правой кнопкой мыши по названию Проекта Core. Во всплывающем меню выбираем пункт "Свойства". Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
- В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.
Указываем каталог включений (include) DirectX SDK 8.1
- Щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
- В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам DirectX SDK 8.1. В нашем случае это C:\DXSDK8\include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
- Жмём "ОК".
Указываем каталог библиотек (lib) DirectX SDK 8.1
- Щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
- В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к 32-разрядным версиям файлов библиотек (lib) DirectX SDK 8.1. В нашем случае это c:\DXSDK8\lib\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
- Жмём "ОК".
- На Странице свойств тоже жмём "ОК".
- Сохрани Решение (File -> Save All).
Готово.
Настраиваем Проект Core: указываем пути к Windows SDK
Если попытаться скомпилировать Проект Core в таком виде, то ничего не выйдет. Процесс компилирования "споткнётся" о множество ошибок. А всё из-за неверного порядка указания дополнительных каталогов вложений (include) и библиотек (lib). Вообще, помимо указания путей к DirectX SDK, также необходимо аналогичным образом указать пути к заголовкам (include) и библиотекам (lib) Windows SDK. Самое смешное, что в MS Visual C++ 2010 эти пути указываются по умолчанию для каждого создаваемого Проекта. В этом нетрудно убедиться, если ещё раз открыть Проект -> Свойства -> Свойства конфигурации - >Каталоги VC++ -> Каталоги включения -> Изменить. В окне "Каталоги включения" в нижней части видим список "Унаследованные значения", где в третьей строке стоит значение:
$(WindowsSdkDir)include
Однако менять порядок перечисления элементов данного списка мы не можем - ограничение бесплатной версии MSVC++2010 Express.
Но заголовочным файлам DirectX SDK 8.1 "жизненно важно", чтобы они включались после включений заголовков Windows SDK.
Поэтому, аналогично тому, как мы указывали пути к каталогам включения (include) и библиотек (lib) DirectX SDK 8.1, добавим ещё раз пути к таким же каталогам для Windows SDK.


Каталоги, пути к которым указаны в окнах "Каталоги включения" и "Каталоги библиотек" при компиляции считываются один за другим по списку сверху вниз. Поэтому для корректного указания путей размести пути к Windows SDK выше, а к DirectX SDK 8.1 - ниже по списку (чтобы они считывались последними).
Выше мы указывали пути к DirectX SDK 8.1. Для Windows SDK это делается аналогично. Начнём.
- В Обозревателе решений видим: "Решение "Test01"", а строкой ниже жирным шрифтом название Проекта (Core). Щелчок правой кнопкой мыши по названию Проекта Core. Во всплывающем меню выбираем пункт "Свойства". Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
- В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.
Указываем каталог включений (include) Windows SDK
- Щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
- В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
- Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".
Каталог включений DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
- Жмём "ОК".
Указываем каталог библиотек (lib) Windows SDK
- Щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
- В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к папке с файлами библиотек (lib) Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
- Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".
Каталог включений DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
- Жмём "ОК".
- На Странице свойств тоже жмём "ОК".
- Сохрани Решение (File -> Save All).
Готово.
Компилируем Проект Core
- В Обозревателе решений щёлкни правой кнопкой мыши по названию Проекта Core.
- Во всплывающем контекстном меню выбери Перестроить.
Проект успешно скомпилируется.
Само собой, статическая lib-библиотека в среде Windows не запустится и окна не покажет.
В док-окне Output видим:
1> Test01.vcxproj -> C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Debug\Core.lib ========== Перестроение всех: успешно: 1, с ошибками: 0, пропущено: 0 ==========
Выше также видим кучу предупреждений (Warnings) о том, что многие операторы в исходниках признаны небезопасными (unsafe). Мы знали, на что шли, начиная прогать под DirectX 8 и потому эти предупреждения можно смело игнорировать. Но лучше просто отключить их вывод.
Отключаем предупреждения (warnings) об устаревших (deprecated) или небезопасных (unsafe) операторах и функциях
- Открой Проект -> Свойства -> С++ -> Препроцессор -> Определения препроцессора -> Изменить
- В появившемся окне "Определения препроцессора" добавь в конце списка на новой строке:
_CRT_SECURE_NO_WARNINGS
Готово. При перекомпиляции такие сообщения не появляются.
Итоги главы
DirectX SDK 8 изначально "затачивалась" под применение в актуальной на тот момент MS Visual Studio 6.0, стоившей порядка 500 USD (версия Pro). Бесплатные (для некоммерческого использования) версии (Express) этой IDE впервые появились только в 2005 году (MS Visual Studio 2005 Express) с целью популяризации своих платных собратьев... Ну и иксбокса, конечно же.)
В этой статье мы доказали, что бесплатная (для некоммерческого использования) MS Visual C++ 2010 Express (из пакета MS Visual Studio 2010 Express) вполне годится для создания классических Win32-приложений и lib-библиотек. Таким образом игрокодинг в домашних условиях доступен каждому: достаточно наличие компьютера с ОС Windows и доступа в Интернет.
Всё вышенаписанное справедливо и для других версий MS Visual C++ Express (2008, 2015, 2017 и др.).
Последние комментарии