Загрузка...
 
Печать
Программируем RPG (Win32, C++, DirectX 8)

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 Studio 2010, если не сделал этого ранее.

Видим Окно приветствия (т.е. в IDE ни один Проект пока не открыт).

  • Создай новый пустой Проект Win32. (В Главном меню: Файл->Создать->Проект...).

В окне "Создать Проект":

  • Отмечаем щелчком пункт "Проект Win32".
  • В поле Имя введи Test01.

Строки "Расположение" и "Имя Решения" заполняются автоматически (при необходимости изменяем).
Под Проект Test01 будет автоматически создано Решение с тем же именем.
Image

  • Жмём ОК.

Появится окно Мастера приложений.

  • Жмём Далее.

Напомним, что в первой части данного курса мы программируем игровое ядро (или движок), которое физически будет содержаться в одном файле статической библиотеки с расширением .lib. Поэтому:

  • На странице "Параметры приложения" отмечаем пункт "Статическая библиотека" и убираем галку у пункта "Предварительно скомпилированный заголовок":

Image

  • Жмём "Готово".

При создании наш Проект автоматически размещается внутри Решения с тем же именем. В результате в левой колонке обозревателя решений видим Решение Test01, которое содержит Проект Test01.
Это неправильно, так как согласно замысла, внутри Решения Test01 будут содержаться два Проекта: Core (ядро, движок; статическая библиотека с расширением .lib) и Game (игра на базе этого движка; файл приложения с расширением .exe). Напомним, что в первой части курса мы разрабатываем движок.

  • Поэтому смени имя вновь созданного Проекта с Test01 на Core (правой кнопкой мыши по названию Проекта -> из контекстного меню выбрать "Переименовать").
Переименовываем Проект GameProject01 в Engine
Переименовываем Проект GameProject01 в Engine

Решение тоже переименуй на своё усмотрение.

  • Сохрани Решение (File -> Save All).


Проект создан. Так как это "Пустой проект", он не содержит в себе никаких файлов. В левой части главного окна MS Visual C++ 2010 расположен "Обозреватель решений". (Если его нет, в главном меню выбираем: Вид->Другие окна->Обозреватель решений. Или комбинация горячих клавиш Ctrl+Alt+L.) В Обозревателе решений видна древовидная структура Проектов, входящих в данное Решение. Чуть ниже названия Проекта видим специально заготовленные папки (в MSVC++2010 они называются "фильтры") для файлов Проекта.

Настраиваем Проект Core: выбираем мультибайтовую кодировку

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

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

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

Image
Напомним, что такую настройку необходимо повторно проделывать при создании каждого нового Проекта.

Создаём Core_Global.h

Напомним, что именно Core_Global.h будет включать в себя ссылки на остальные заголовки движка и, таким образом, будет использоваться в качестве единой точки контакта.

  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы".
  • Во всплывающем меню Добавить->Создать элемент...

Image

  • В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_Global.h.
  • Жмём "Добавить".

Image
Добавленный файл сразу откроется в правой части MSVC++2010.

  • В только что созданном и открытом файле Core_Global.h набираем следующий код:
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 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Заголовочные файлы".
  • Во всплывающем меню Добавить->Создать элемент...

Image

  • В появившемся окне выбери "Заголовочный файл (.h)" и в поле "Имя" введи Core_System.h.
  • Жмём "Добавить".

Image
Добавленный файл сразу откроется в правой части MSVC++2010.

  • В только что созданном и открытом файле Core_System.h набираем следующий код:
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.cpp

Если помнишь, для написания хорошо структурированного кода мы размещаем объявления всех классов и функций в заголовочных файлах (.h), а их реализацию в файлах исходного кода (.cpp), в которых также прописываем ссылку на "сопровождающий" его хэдер (=заголовочный файл .h).
Создадим файл исходного кода Core_System.cpp, в котором будут содержаться реализации функций, объявленных в заголовочном файле Core_System.h:

  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Файлы исходного кода" Проекта Core.
  • Во всплывающем меню Добавить->Создать элемент...
Добавляем исходный файл
Добавляем исходный файл
  • В появившемся окне выбери "Файл С++ (.cpp)" и в поле "Имя" введи Core_System.cpp.
  • Жмём "Добавить".

Image
Добавленный файл сразу откроется в правой части MSVC++2010.

  • В только что созданном и открытом файле Engine.cpp набираем следующий код:
Core_System.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, который уже содержит ссылки на все остальные заголовочные файлы:

Фрагмент файла Core_System.cpp
...
#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.

Image

  • В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) DirectX SDK 8.1

  • Щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."

Image

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

Image

  • Жмём "ОК".

Указываем каталог библиотек (lib) DirectX SDK 8.1

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

Image

  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (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.

Закрыть
noteВажно!

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

Выше мы указывали пути к DirectX SDK 8.1. Для Windows SDK это делается аналогично. Начнём.

  • В Обозревателе решений видим: "Решение "Test01"", а строкой ниже жирным шрифтом название Проекта (Core). Щелчок правой кнопкой мыши по названию Проекта Core. Во всплывающем меню выбираем пункт "Свойства". Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.

Image

  • В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++. В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) Windows SDK

  • Щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."

Image

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

Image

  • Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".

Каталог включений DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
Image

  • Жмём "ОК".

Указываем каталог библиотек (lib) Windows SDK

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

Image

  • Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".

Каталог включений DirectX SDK 8.1 должен всегда стоять в самом конце списка, как на этом скриншоте:
Image

  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (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 и др.).

Источники:


1. Jim Adams. Programming Role Playing Games with DirectX 8.0. - Premier Press. 2001

Contributors to this page: slymentat .
Последнее изменение страницы Вторник 05 / Май, 2020 14:09:49 MSK автор slymentat.

Помочь проекту

Яндекс-деньги: 410011791055108