Четверг, 02 Мая 2024, 01:00

Приветствую Вас Гость

Меню сайта
Наш опрос
Что в своих играх используете от стороннего производителя?
Всего ответов: 9572
Блог » 2012 » марта » 5 » Пишу свой игровой движок - 1 (первая часть)
Пишу свой игровой движок - 1 (первая часть)
17:28
Продолжаю. Сегодня нужно создать окно и инициализировать рендер.

Вчера я определил, какие у движка должны быть библиотеки. Но то была больше теория. Сегодня я создал проект и применил теорию на практике. Вот что у меня получилось:

Вот так вот я организовал структуру своего движка. Давайте я рассмотрю подробней. Начну с папок. Всего создано шесть папок. Первая - _Doc, будет содержать ссылки на используемую документацию по движку (история, лист задач и т.д.). Фишка: обратили внимание на подчеркивание? Так вот, это сделано специально чтобы папка была в самом вверху и не смешивалась с папками движка (папки сортируются по имени).
Application - это уровень приложения. Как вы видите в ней всего один проект с тем же именем. Данный проект - это динамическая библиотека (подробнее о видах библиотек можете прочитать http://ru.wikipedia.org/wiki/Библиотека_(программирование)). Пользователь в идеале должен будет работать только с этой библиотекой.
Папка Core - это основа из схемы. Она содержит 4 проекта. Каждый проект - это статическая библиотека.
Папка Engine - это ядро из схемы. Все проекты из папки, также являются статическими библиотеками.
Папка Framework пуста, потому что пока нет смысла в ней что-то делать.
И папка Test будет хранить приложения для тестирования частей движка.

То есть в итоге у нас одна динамическая библиотека, одинадцать статических которые будут линковаться к динамической. И проект.

Вот мой проект - http://www.gamedev.ru/files/?id=76612

Я не стану пояснять как я создал такой проект, так как это долго. Да и не расчитанно на новичков.

На этом подготовка завершена. Продолжим.

Любое приложение должно с чего-то начинаться. Это называется точкой старта (или входа) приложения. Обычно это или main() или WinMain(), а также все их производные... Но в движке нет точки старта, потому что движок не запускается сам по себе. Точку старта задает пользователь при создании приложения. Но я начну именно с нее проектировать и писать код. Наша точка старта будет в приложении test.

Пользователь должен при старте, создать наследника от класса Application, полиморфировать нужные методы, инициализировать наследник, запустить его на выполнение и затем очистить ресурсы.
То есть получается так:

Поясню. Start - это условный момент запуска приложения. End - это условный момент завершения приложения. WinMain и MyApplication - это уровень приложения. Данные сущности пишутся пользователем. iApplication - это интерфейс из библиотеки Application. Как вы видите, я выделил их цветом согласно схемы из предыдущей статьи. Я так буду поступать и дальше чтобы вам было легче ориентироваться.

Начнем писать код. Для начала создадим класс приложения. Находим Application и пишем:
Application.h

Code

#pragma once

#include "export.h"

class ENGINE_DLL Application
{
public:
  Application();
  virtual ~Application();

  // Инициализация приложения.
  bool Create();

  // выполнение одного кадра
  bool RunOneFrame();

  // Выполнение приложения.
  void Run();

  // Завершение приложения.
  void Shutdown();

  bool IsInit(){return _isinit;}
  bool IsExit() {return _exit;}

protected:
  // пользовательская инициализация
  virtual bool _init() = 0;
  // пользовательский кадр
  virtual bool _frame() = 0;
  // пользовательская очистка
  virtual void _close() = 0;

private:
  bool _isinit;
  bool _exit;
};


Application.cpp
Code

#include "Application.h"

Application::Application() :
  _isinit(false),
  _exit(false)
{
}

Application::~Application()
{
  // чистим за нерадивым программистом
  if (_isinit==true)
  Shutdown();
}

bool Application::Create()
{
  /*
  Инициализация всех систем движка
  */

  // пользовательская инициализация
  if (_init() == false)
  return false;
  // сообщаем что приложение инициализировано
  _isinit=true;
  // сбрасываем флаг сообщающий о выходе
  _exit = false;
  return true;
}

bool Application::RunOneFrame()
{
  // следим чтобы не было попытки рисовать при неудачно инициализации
  if (_isinit == false)
  return false;

  return _frame();
}

void Application::Run()
{
  // Инициализируем
  if(_isinit == false)
  Create();

  // выполняем бесконечный цикл
  while ( _exit==false && _isinit==true )
  {
  _exit = !(RunOneFrame());
  }

  // очищаем ресурсы
  Shutdown();
}

void Application::Shutdown()
{
  _isinit = false;
}


Если вам нужен файл export.h, то он такой:
Code

#pragma once

#ifdef WIN32
#ifdef _BUILD_ENGINE
#define ENGINE_DLL __declspec(dllexport)
#else
#define ENGINE_DLL __declspec(dllimport)
#endif
#else
#define ENGINE_DLL
#endif


При этом, где-то в Application должен быть объявлен _BUILD_ENGINE. В проекте на который я дал ссылку выше, все это уже есть.

Теперь вернемся к приложению:
main.cpp
Code

#include <Windows.h>
#include "../Application/Application.h"

class MyApp : public Application
{
private:
protected:
  // пишем пользовательские классы
  virtual bool _init() { return true; }
  virtual bool _frame() { return true; }
  virtual void _close() {}
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow)
{
  MyApp app;

  app.Run();

  return 0;
}

Собственно вы уже можете собрать (но не забудьте прилинковать Application). Но запускать не советую - уйдет в бесконечность:) Мы ведь еще условий выхода не написали.

Так, теперь двигаемся дальше. Вспомним для чего нам был нужен Application - для обертывания остальных сущностей. Среди которых главной была Engine. Значит теперь ее нужно создать. Далее, по текущей задаче мы должны создать окно и инициализировать рендер. Значит в модуле рендера появляются два класса - Window и Render. Но так как мы решили что сущность рендера абстрактна, то тогда нужен еще один класс RenderDX11 и WindowWin32, которые будут выполнять реализацию использования в рендере DirectX 11 в окне операционной системы Windows.

Схема усложняется:).
Начнем выполнение с конца. Находим библиотеку рендера и пишем код:
Window.h

Code

#pragma once

#include <string>

class Window
{
public:
  Window();
  virtual ~Window();

  /// Создать окно
  virtual bool createWindow(const std::wstring &caption, unsigned int width, unsigned int height)=0;

  /// Закрыть окно.
  virtual void closeWindow()=0;

  /** Показать/Скрыть окно.
  @param
  value определяет, показывать ли окно (при true) или не показывать.
  */
  virtual void showWindow(bool value)=0;

  /// Вернуть заголовок окна
  virtual const std::wstring& getWindowName() const {return _caption;}

  /// Изменить заголовок окна.
  virtual void setWindowCaption(const std::wstring& _caption)=0;

protected:
  std::wstring _caption; ///< заголовок окна
  unsigned int _width; ///< ширина клиентской части окна
  unsigned int _height; ///< высота клиентской части окна
};


Window.cpp
Code

#include "Window.h"

Window::Window()
{
  _width = 800;
  _height= 600;
  _caption = L"Engine";
}

Window::~Window()
{
}



Теперь нам нужен класс Render. Но тут я хочу сказать вот что - наш рендер должен быть единственным. Реализуем мы это через паттерн синглтон (погуглите, кому интересны подробности, прям по запросу "паттерн синглтон"). Так что сначала надо написать сам паттерн. Переходим к библиотеке Common. и пишем:
macros.h
Code
#pragma once

#include <cassert>

#define Assert(a, b) assert(a && b)

Singleton.h
Code

#pragma once

#include "macros.h"

/** Singleton pattern implemented using templates.
  @remarks
  Using singleton is useful for objects such as engine
  or managers which should have no more than a single
  instance in the whole application.
  @remarks
  If you want to make object of some class singleton you
  have to derive this class from Singleton class.
  @remarks
  If something goes wrong during singleton object creation,
  deletion or on attempt to access it, assertion arises.
  By "something going wrong" I also mean attempts to create
  more than single object of class marked singleton.
*/
template <typename T>
class Singleton
{
public:
  /** Creates singleton of type T.
  */
  Singleton()
  {
  Assert(!s_pSingleton, "Singleton class already instantiated.");

  #if defined( _MSC_VER ) && _MSC_VER < 1200
  int offset=(int)(T*)1-(int)(Singleton <T>*)(T*)1;
  s_pSingleton=(T*)((int)this+offset);
  #else
  s_pSingleton=static_cast<T*>(this);
  #endif
  }

  virtual ~Singleton()
  {
  Assert(s_pSingleton, "Singleton class not instantiated.");
  s_pSingleton=0;
  }

  /** Returns reference to a singleton.
  */
  static T& getSingleton()
  {
  Assert(s_pSingleton, "Singleton class not instantiated.");
  return(*s_pSingleton);
  }

  /** Returns pointer to a singleton.
  */
  static T* getSingletonPtr()
  {
  return s_pSingleton;
  }

  /// A static pointer to an object of T type.
  /// This is the core member of the singleton.  
  /// As it is declared as static no more than
  /// one object of this type can exist at the time.
  static T* s_pSingleton;
};

template <typename T> T* Singleton <T>::s_pSingleton=0;

Паттерн не мой, поэтому сохранен язык оригинала :-D

Вообщем, я к сожалением на сегодня на этом завершаю:-( Хотел бы написать дальше, но 12 часов - пора спать. Ждите до завтра, будет продолжение. Мы наконец-то создадим окно и инициализируем движок.
Категория: Программирование | Просмотров: 11511 | Рейтинг: 4.0/4 |
Теги: Создание движка, Свой игровой движок, DirectX, DX11, Свой движок, программирование, движок, DirectX SDK, Ядро движка, Энджин
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Пишу свой игровой движок - 1 (первая часть)», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 14
+0-
14 Asteroid   (11 Марта 2013 06:05) [Материал]
Где продолжение ? smile

+1-
13 Demy   (22 Марта 2012 14:33) [Материал]
DemyНасчет игрового цикла, тут вроде бы все не так-то просто, была на хабре статья - http://habrahabr.ru/post/136878/
Вроде, оч разумно.

+2-
6 td09c   (06 Марта 2012 18:26) [Материал]
Молодец, очень интересно,есть вопрос, а будет ли реализация редактора уровней и ресурсов, или всё прийдёться ручками через код делать?

+3-
7 warzes   (06 Марта 2012 18:50) [Материал]
warzesБудет редактор, но много-много позже. Сейчас движок главней.. А может и не так поздно. В общем посмотрю когда начну вторую статью про работу с 3D моделями

+2-
8 td09c   (06 Марта 2012 19:23) [Материал]
а если к примеру моя видеокарта не поддерживает DX 11, а только DX 10.1 то что мне делать не подскажите sad

+2-
9 Морриарте   (06 Марта 2012 20:10) [Материал]
МорриартеИспользовать то что поддерживает... DX 9 или OpenGL и т.п.

+2-
10 warzes   (07 Марта 2012 05:38) [Материал]
warzesТак. Отпишись в теме http://gcup.ru/forum/3-21180-1
Если найдется пять человек которым это надо, я осуществлю рендер на dx 9 (по качеству оно будет одинаково, но это будет эмуляция фич dx 11)

+2-
11 warzes   (07 Марта 2012 05:40) [Материал]
warzesЛибо да, вместо dx 11 инициализировать dx 10... И вообще-то данный код, по идее так и должен был сделать (там есть обход всех поддерживаемых версий от 11 до 9)

+2-
5 warzes   (06 Марта 2012 17:30) [Материал]
warzesВыложил исходники - смотреть сюда - http://gcup.ru/forum/3-21180-1#349112

+2-
3 Gavolot   (06 Марта 2012 03:36) [Материал]
GavolotЭх материала много, а я мало понял... Я в смысле о коде, еще читать и читать кучу книг, но хоть кое-где я принцип понял, а по визуальным подсказкам более менее лучше понятно. Но я думаю из этого можно было бы даже книгу составлять. "Пишем свой, игровой движок" или более пафосно и гордо "Технология создания игрового движка".

+3-
4 warzes   (06 Марта 2012 08:40) [Материал]
warzesопыта еще недостаточно много для книги sad

+5-
2 warzes   (05 Марта 2012 17:59) [Материал]
warzesИ кому интересно, мой план:
- завтра появится завершающая статья. В ней мы создадим окно, инициализируем DX11 (поэтому если у вас до сих пор нет DirectX SDK - поспешите скачать самый новый (или не совсем новый)B) ), также мы начнем реализацию модуля ввода
Затем будет очень сложная статья, по завершению которой мы сможем выводить модели созданные в сторонних программах (3ds max, maya, blender). То есть никаких треугольников, квадратов и шариков - только реальные задачи, только реальный опыт:D. Сразу оговорюсь - статья будет сложной, будут затронуты очень сложные темы (уменьшение количества DIP к примеру, текстурирование, материалы). Возможно статья будет разбита на части. Но из-за сложности самой статьи и малом количестве времени которое я могу этому уделять - статья будет не скоро. Сожалею - но там слишком много материала.

+4-
1 warzes   (05 Марта 2012 17:29) [Материал]
warzesИ надеюсь вы извините меня за ошибки dry все поправлю завтра - спешил

+1-
12 kashara15   (22 Марта 2012 13:26) [Материал]
kashara15biggrin

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Автор материала
Ник:
warzes
Ещё:
Все его записи
Календарь заметок
«  марта 2012  »
ПнВтСрЧтПтСбВс
   1234
567891011
12131415161718
19202122232425
262728293031
Поиск по сайту
Записи по месяцам
Друзья сайта
Игровой форум GFAQ.ru Перевод консольных игр
Все права сохранены. GcUp.ru © 2008-2024 Рейтинг