Создание игр » DirectX 9, Featured » Создание игры. Графический движок.
Создание игры. Графический движок.
Вообще, тема создания собственного графического движка для игры достаточно больная и давно заезженная. Я решил включить её в уроки на моём блоге, т.к. многие всё равно, так или иначе, возьмутся делать собственный графический движок. Кроме того, это блог в том числе по программированию графики, потому лишним не будет. Ну и, конечно, создание игры, особенно достаточно простой, нередко подразумевает и создание граф. движка, т.к. простой движок создать достаточно не сложно, а времени сэкономить он может кучу.
Со своей стороны обещаю, что наш графический движок не будет многоруким шивой и мы создадим его максимально компактным и простым. А так же, что на создание этого графического движка для игры у нас не уйдёт слишком много времени – нам же надо в первую очередь сделать игру, а не графический движок.
Итак, какой же минимум работы мы сможем поручить созданному нами графическому движку для игры? Давайте прикинем, что бы нам хотелось:
- Что бы весь код инициализации графики был скрыт в самом движке.
- Что бы движок загружал для нас текстуры и шейдеры
- Если вдруг произошёл LostDevice (например, такое бывает при запуске скринсейвера или блогкировке компа), что бы движок сам восстанавливал все нужные данные (например, текстуры).
- Мы хотим одной функцией завершать всю работу с графикой и выгружать все ресурсы
Пока этого хватит.
Итого получается, текстура:
class CTexture { // класс Graphics будет иметь доступ к protected-членам friend class Graphics; private: //! файл текстуры std::wstring m_strTexSrc; //! это сама текстура LPDIRECT3DTEXTURE9 m_pTex; protected: // доступ только для класса Graphics CTexture(void); ~CTexture(void); void SetTexture(LPDIRECT3DTEXTURE9 pTex); void SetSrc(const std::wstring& src); LPDIRECT3DTEXTURE9 GetTexture() const; public: std::wstring GetSrc() const; }; typedef CTexture* CTexturePtr; // тип указателей на текстуру |
Шейдеры:
class CShader { friend class Graphics; private: //! Это пиксельный шейдер? bool m_bIsPS; //! Здесь ошибки омпиляции LPD3DXBUFFER m_pErrors; //! Здесь скопилированый шейдер LPD3DXBUFFER m_pShaderBuff; //! Таблица констант шейдера LPD3DXCONSTANTTABLE m_pConstTable; //! вертексный и писельный шейдер. экономим память union { LPDIRECT3DPIXELSHADER9 m_pPixelShader; LPDIRECT3DVERTEXSHADER9 m_pVertexShader; } m_pShader; //! Девайс, в котором создан шейдер LPDIRECT3DDEVICE9 m_pShaderDevice; protected: //! Функция загрузки и компиляции шейдера HRESULT Load( const std::string& strFileName, const std::string& strFuncName, const std::string& profile); HRESULT Release(); //! Получение пиксельного шейдера LPDIRECT3DPIXELSHADER9 GetPixelShader(); //! Поулчение вертексного шейдера LPDIRECT3DVERTEXSHADER9 GetVertexShader(); //! Получение буфера скомпилированого шейдера //! нужно для создания HW-шейдера LPD3DXBUFFER GetCompileBuffer(); //! Установка HW-шейдера (пиксельного) void SetHardwareShader(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DPIXELSHADER9 pHWShader); //! Установка HW-шейдера (вертексного) void SetHardwareShader(LPDIRECT3DDEVICE9 pDev, LPDIRECT3DVERTEXSHADER9 pHWShader); CShader(void); ~CShader(void); public: bool IsPS(); // это пиксельный шейдер? bool IsVS(); // или это вертексный шейдер? //! Функция задания матрицы HRESULT SetFloat(D3DXHANDLE hConstant, CONST FLOAT Value); HRESULT SetVector(D3DXHANDLE hConstant, CONST D3DXVECTOR4* pVec4); HRESULT SetMatrix(D3DXHANDLE hConstant, CONST D3DXMATRIX* pMatrix); }; typedef CShader* CShaderPtr; // тип указателей на текстуру |
Кода, вроде, немало, но, мне кажется, всё должно быть понятно из комментариев. Мы вынесли текстуры и шейдеры в отдельные классы, что бы полностью скрыть их реализацию от пользователя движка – так в дальнейшем будет проще программировать. Кроме того, мы сможем не думать о таких вещах, как загружена ли уже нужная нам текстура, что делать при LostDevice и так далее. В общем, просто удобство и, следовательно, скорость программига в будущем.
Теперь сам графический движок:
class Graphics : public singleton<Graphics> { friend class singleton<Graphics>; private: LPDIRECT3D9 m_pD3D; LPDIRECT3DDEVICE9 m_pd3dDevice; // структкура с параметрами девайса D3DPRESENT_PARAMETERS m_d3dpp; typedef std::vector<CTexturePtr> vecTextures; vecTextures m_vecTextures; typedef std::vector<CShaderPtr> vecShaders; vecShaders m_vecShaders; protected: Graphics(void); ~Graphics(void); LPDIRECT3DDEVICE9 GetDevice(); public: bool Init(HWND hRenderWnd, int width, int height); void Cleanup(); HRESULT Reset(); bool StartRender(); HRESULT SetFVF(DWORD FVF); HRESULT SetPixelShader(CShaderPtr shader); HRESULT SetVertexShader(CShaderPtr shader); HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); HRESULT SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); HRESULT SetTexture(DWORD stage, CTexturePtr pTex); HRESULT DPUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride); //! Функция отрисовки спрайта в заданных координатах\ и с заданными размерами HRESULT DrawSprite(CTexturePtr pTex, D3DXVECTOR2 pos, D3DXVECTOR2 size); bool EndRender(); CTexturePtr LoadTexture(const std::wstring& szFileName); CShaderPtr LoadShader( const std::string& strFileName, const std::string& strFuncName, const std::string& profile); }; |
Для начала нам этого хватит. Конечно, этот получился очень примитивный движок – в нём нет умных указателей, счётчиков ссылок, нет приоритетов ресурсов, нет многопоточности, нет даже элементарной возможности выгрузить уже не нужную текстуру. Но нам это сейчас и не нужно. В следующих уроках мы будем постепенно наращивать возможности и, со временем, получим достаточно универсальный и удобный движок. Текущих же возможностей нам вполне хватит для создания несложных игр. А это, согласитесь, уже немало, учитывая, что на всё про всё мы потратили всего полчаса работы…
P/S Исходики к статье будут в следующем уроке, где мы сделаем летающий корабль игрока.
Раздел: DirectX 9, Featured · Теги: Direct3D, Pixel shader, Vertex shader, Создание движка, Создание игр
супер
Вопрос по коду, в цикле, где обрабатываются windows message и код игры, мы каждый кадр заново устанавливаем все матрицы, рендер стейты и прочее.
А почему нельзя это выставить один раз перед тем, как войти в цикл?
Antony, вынести это всё можно и вызывать однократно тоже можно. Но:
1) в реальной боле-мене сложной игре это скорее всего не получится, т.к. там куча изменений стейтов и матриц во время рендера каждого кадра
2) если таким образом вы надеетесь “прооптимизировать” программу – не стоит, толку (буста) от этого не будет
А можно написать в какой очереди их читать а то как то непонятно где 1 урок, а где 2
Читать просто от старых уроков к новым. Справа в меню есть так же “Уроки DirectX” и “Уроки создания игр” – там последовательность чтения указана.
спасибо ещё 1 вопросик wstring это какой файл подключать?
опечатка в комментарии
class CShader
{
friend class Graphics;
private:
//! Это пиксельный шейдер?
bool m_bIsPS;
//! Здесь ошибки омпиляции
LPD3DXBUFFER m_pErrors;
А в какой проге то работать??
Ну всмысле куда это все всавлять что бы работало?
ДА А ВОТ КУДА ПИСАТЬ КОД ЭТОТ????????????????7
Писать код можно в Microsoft Visual Studio, предварительно поставив DirectX SDK, чтобы подцепить нужные заголовочные файлы.
Посты хороши, но для новичка, как я, тяжеловато! Спасибо за пост…
Учение – свет, не учение – 0x00000000
P.S. почему-то не запускается Tutorial.exe из примера. А так же он отказывается компилироваться. Быть может я упустил какой-то момент? Как из примера, получить скомпилированный исполняемый файл?
Немного сложновато для новичка типа меня, но буду пробовать…
Спасибо за статью!