Загрузка...
 
Печать
Кодим 3D RPG под DirectX 8

1.1 Создаём ядро игры




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-приложений не станет компилироваться.
Закрыть
noteПримечание

Существуют 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.
Закрыть
noteПримечание

Желательно также скачать и установить (в отдельную папку) последнюю версию 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 C++ 2010;
  • DirectX SDK 8.1
  • Windows SDK (для программирования под Win7/8/10).
Все эти штуки:
  • + инструкции по их установке ты найдёшь в разделе "Софт" нашего сайта;
  • Бесплатны;
  • Без труда гуглятся.

  • Создай Проект с именем Test01 (потом переименуем).
В мастере проектов выбери тип: Проект Win32.
Image
Строки "Расположение" и "Имя Решения" заполняются автоматически (при необходимости изменяем). Под Проект GameProject01 будет автоматически создано Решение с тем же именем.
  • Жмём ОК.
Появится окно мастера приложений (Application Wizzard).
  • Жмём Далее.
Напомним, что на протяжении всей первой части данного курса мы программируем игровой движок, который физически будет содержаться в одном файле статической библиотеки с расширением .lib. Поэтому...
  • На странице мастера "Параметры приложения" отмечаем пункт "Статическая библиотека" и убираем галку у пункта "Предварительно скомпилированный заголовок":
Image
  • Жмём Готово.
Проект автоматически разместится внутри Решения с таким же именем. (Весь процесс подробно расписан в статье MS Visual Cpp 2010 Express. Установка и указание путей к DirectX SDK _.
Это неправильно, так как согласно замысла, внутри Решения Test01 будут содержаться два Проекта: Core (ядро, движок; статическая библиотека с расширением .lib) и Game (игра на базе этого движка; будущий файл приложения с расширением .exe). Напомним, что в первой части курса мы разрабатываем движок в виде статической LIB-библиотеки.
  • Поэтому переименую вновь созданный Проект с Test01 в Core (правой кнопкой мыши по названию Проекта -> из контекстного меню выбрать "Переименовать").
Решение тоже переименуй на своё усмотрение.
  • Сохрани Решение (Файл -> Сохранить все).
Проект создан.
В левой части главного окна MS Visual C++ 2010 расположен "Обозреватель решений". (Если его нет, в главном меню выбираем: Вид->Другие окна->Обозреватель решений. Или комбинация горячих клавиш Ctrl+Alt+L.) В Обозревателе решений видна древовидная структура Проектов, входящих в данное Решение. Чуть ниже названия Проекта видим специально заготовленные папки (в MSVC++2010 они называются "фильтры") для файлов Проекта.

Добавляем в Проект Core_Global.h

Напомним, что именно Core_Global.h будет включать в себя ссылки на остальные заголовки движка и, таким образом, будет использоваться в качестве единой точки контакта.
  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы" Проекта Core.
  • Во всплывающем меню Добавить->Создать элемент...
Image
  • В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_Global.h. Жмём "Добавить".
Image
Добавленный файл сразу откроется в правой части MSVC++2010.
  • В только что созданном и открытом файле набираем следующий код:
Core_Global.h
/**************************************************
Core_Global.h
GameCore Component

Programming Role-Playing Games with DirectX
by Jim Adams (01 Jan 2002)
**************************************************/

#ifndef _CORE_GLOBAL_H_
#define _CORE_GLOBAL_H_

// 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"
#include "Core_Graphics.h"
#include "Core_Input.h"
#include "Core_Sound.h"
#include "Core_Network.h"

#endif

  • Сохрани Решение (Файл -> Сохранить все).
Содержит в себе кучу инклудов из DirectX SDK 8.1.

Добавляем в Проект Core_System.h

  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы" Проекта Core.
  • Во всплывающем меню Добавить->Создать элемент...
  • В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_System.h. Жмём "Добавить".
Image
Добавленный файл сразу откроется в правой части MSVC++2010.
  • В только что созданном и открытом файле набираем следующий код:
Core_System.h
/**************************************************
Core_System.h
GameCore Component

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);

    int WINAPI 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) игрового движка. Подробнее о них читай в статьях:
Чуть ниже мы увидим, как файле исходного кода Core_System.cpp будут созданы (пустые) объекты данных классов.

Добавляем в Проект Core_System.cpp

Если помнишь, для написания хорошо структурированного кода мы размещаем объявления всех классов и функций в заголовочных файлах (.h), а их реализацию в файлах исходного кода (.cpp), в которых также прописываем ссылку на "сопровождающий" его хэдер (=заголовочный файл .h).
Создадим файл исходного кода Core_System.cpp, в котором будут содержаться реализации функций, объявленных в заголовочном файле Core_System.h.
  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Файлы исходного кода" Проекта Core.
  • Во всплывающем меню Добавить->Создать элемент...
Image
  • В появившемся окне выбери "Файл С++ (.cpp)" и в поле "Имя" введи Core_System.cpp. Жмём "Добавить".
Image
Добавленный файл сразу откроется в правой части MSVC++2010.
  • В только что созданном и открытом файле WinMain.cpp набираем следующий код:
Core_System.cpp
/**************************************************
Core_System.cpp
GameCore Component

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
  // Заполняем данные оконного класса
  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)
      if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
      } else {
        // Do per-frame processing, break on FALSE return value
		  // Выполняем предкадровую обработку.
        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
// Оконная процедура. Сейчас в ней только сообщение, при котором завершаем работу.
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
  // Возвращаем TRUE, если ещё остались стейты
  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, который уже содержит ссылки на все остальные заголовочные файлы:
Фрагмент CoreSystem.h
...
#include "Core_Global.h"
...

В конце листинга видим реализацию трёх классов, которые пригодятся нам в будущем:
  • cStateManager (менеджер стейтов),
  • cProcessManager (менеджер процессов),
  • cDataPackage (система работы с данными приложения).

Готовим Проект Core к компиляции


Выбираем многобайтовую кодировку

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, данный Проект мы настроим под многобайтовую кодировку.

Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...
  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Core.
В Обозревателе решений видим: "Решение "Core"", а строкой ниже жирным шрифтом название Проекта (тоже Core).
  • В Обозревателе решений щёлкаем правой кнопкой мыши по названию Проекта Core.
  • Во всплывающем контекстном меню выбираем "Свойства".
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации->Общие, в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
Image
  • Жмём ОК.
  • Сохрани Решение (Файл->Сохранить все).

Указываем пути к DirectX SDK 8.1

Если попытаться скомпилировать Проект Core в таком виде, то ничего не выйдет.
В единственном файле исходного кода WinMain.cpp можно увидеть инклуды различных заголовочных файлов DirectX (весии 8):
Фрагмент Core_Global.h
...
// DirectX includes
#include "d3d8.h"
#include "d3dx8.h"
#include "dmusici.h"
#include "dsound.h"
#include "dplay8.h"
#include "dpaddr.h"
#include "dinput.h"
...

На данном этапе MSVC++2010 ничего не знает об их местоположении. В статье MS Visual Cpp 2010 Express. Установка и указание путей к DirectX SDK мы указывали пути к DirectX SDK (версии 9 и выше). Для DirectX SDK 8 это делается аналогично. Начнём.
  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Core.
  • Убедись, что DirectX SDK 8.1 установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.
В Обозревателе решений видим: "Решение "Test01"", а строкой ниже жирным шрифтом название Проекта Core.
  • Жмём правой кнопкой мыши по названию Проекта Core. Во всплывающем меню выбираем пункт "Свойства".
Image
Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC+ + . В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) DirectX SDK 8.1
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
Image
В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным файлам DirectX SDK 8.1 (include). В нашем случае это C:\DXSDK8\include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Жмём "ОК".

Указываем каталог библиотек (lib) DirectX SDK 8.1
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к 32-разрядным версиям файлов библиотек DirectX SDK 8.1 (lib). В нашем случае это c:\DXSDK8\lib\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (File -> Save All).
Готово.

Указываем пути к Windows SDK

Помимо указания путей к заголовкам (include) и библиотекам (lib) DirectX SDK, для любого DirectX-Проекта также необходимо указать пути к заголовкам (include) и библиотекам (lib) Windows SDK. Самое смешное, что в MS Visual C++ 2010 эти пути указываются по умолчанию для каждого создаваемого Проекта. В этом нетрудно убедиться, если ещё раз открыть Проект -> Свойства -> Свойства конфигурации - >Каталоги VC++ -> Каталоги включения -> Изменить. В окне "Каталоги включения" в нижней (недоступной для редактирования) части видим список "Унаследованные значения", где в третьей строке стоит значение:
$(WindowsSdkDir)include

В результате видим, что добавленные пути к DirectX SDK (в обоих окнах: include и lib) расположены вверху списка, а пути к Windows SDK - в недоступной области, на несколько строк ниже.
Но! Заголовочным файлам DirectX SDK "жизненно важно", чтобы они включались после включений заголовков Windows SDK.
Закрыть
noteВажно!

Каталоги, пути к которым указаны в окнах "Каталоги включения" и "Каталоги библиотек" при компиляции считываются один за другим по списку сверху вниз. Поэтому для корректного указания путей надо разместить пути к Windows SDK выше, а к DirectX SDK - ниже по списку (чтобы они считывались последними).

В данной ситуации мы не можем поднять пути к Windows SDK, прописанные по умолчанию при создании Проекта, т.к. они расположены в специальной нередактируемой области (ограничение бесплатной версии MSVC++2010 Express). Но можем схитрить и добавить ещё раз пути к тем же самым каталогам Windows SDK, подняв эти строки выше строк DirectX SDK.
Выше мы указывали пути к DirectX SDK. Для путей к Windows SDK это делается аналогично. Начнём.
  • Убедись, что MSVC++2010 запущена и в ней открыт Проект, с которым работаешь в данный момент.
  • Убедись, что Windows SDK (в нашем случае версия 7) установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.
В Обозревателе решений видим: "Решение "..."", а строкой ниже жирным шрифтом название Проекта с тем же названием (если не менял вручную).
  • Жмём правой кнопкой мыши по названию Проекта. Во всплывающем меню выбираем пункт "Свойства".
Image
Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++ . В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас, как и в прошлый раз, интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) Windows SDK
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
Image
В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Меняй порядок считывания каталогов включений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".
Каталог включений DirectX SDK должен всегда стоять в самом конце списка, как на этом скриншоте:
Image
  • Жмём "ОК".

Указываем каталог библиотек (lib) Windows SDK
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к папке с файлами библиотек (lib) Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\MicrosoftSDKs\Windows\v7.0A\Lib. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна.
Каталог библиотек DirectX SDK должен всегда стоять в самом конце списка, как на этом скриншоте:
Image
  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (File -> Save All). Готово.

Компилируем Проект Core

  • В Обозревателе решений щёлкни правой кнопкой мыши по названию Проекта Core.
  • Во всплывающем контекстном меню выбери Перестроить.
MSVC++2010 выдаст ошибку:
1>c:\users\...\test01\test01\core_global.h(29): fatal error C1083:
Не удается открыть файл включение: Core_Graphics.h: No such file or directory

Ссылки на включение заголовков ядра при компиляции считываются один за другим. Не найдя очередной заголовок, IDE "спотыкается" и останавливается. Пока у нас есть только заголовок Core_System.h . Поэтому...
  • В Core_Global.h найди фрагмент:
Фрагмент Core_Global.h
...
// Core includes
#include "Core_System.h"
#include "Core_Graphics.h"
#include "Core_Input.h"
#include "Core_Sound.h"
#include "Core_Network.h"
...

  • Закомментируй все инклуды, кроме Core_System.h:
Фрагмент Core_Global.h
...
// Core includes
#include "Core_System.h"
//#include "Core_Graphics.h"
//#include "Core_Input.h"
//#include "Core_Sound.h"
//#include "Core_Network.h"
...

  • Перекомпилируй Проект Core.
Проект успешно скомпилируется. В док-окне Output видим:
Перестроение всех: успешно: 1, с ошибками: 0, пропущено: 0

Скомпилированный файл LIB-библиотеки в нашем случае (Win7 x64) расположен по пути: C:\Users\<Имя пользователя>\Documents\Visual Studio 2010\Projects\Test01\Debug\Core.lib. Размер: 77 Кб. Само собой, статическая lib-библиотека в среде Windows не запустится и окна не покажет. Напомним, LIB-библиотеки используются в C++ в качестве хранилищ данных (функций, классов, переменных и т.д.). Позднее мы добавим эту скомпилированную "либу" в Проект с будущей игрой и будем активно юзать размещённые в ней функции.
В док-окне Output также видим кучу предупреждений (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 и др.).

Источники


1. Adams J. Programming Role Playing Games with DirectX 8.0. - Premier Press, 2002


Последние изменения страницы Воскресенье 03 / Июль, 2022 10:36:18 MSK

Последние комментарии wiki

No records to display

Search Wiki Page

Точное совпадение

Категории

|--> C#
|--> C++