netlib.narod.ru | < Назад | Оглавление | Далее > |
Как вы уже видели в предыдущей главе, нарисовать небольшой прямоугольный текстурированный полигон, который замечательно подойдет для представления ваших блоков, совсем не сложно. Вы можете выполнить это с помощью специального объекта D3DX с именем ID3DXSprite. В главе 6, «Создаем ядро игры», я упоминал интерфейс ID3DXSprite, когда обсуждал графическое ядро; теперь у вас есть шанс поближе познакомиться с этим интерфейсом.
У объекта ID3DXSprite одна задача — рисовать на экране прямоугольные полигоны, используя назначенную вами текстуру. Конечно же, в предоставленной текстуре будут упакованы блоки.
Чтобы начать использовать блоки в Direct3D, объявите экземпляр объекта ID3DXSprite и используйте для его инициализации функцию D3DXCreateSprite:
// g_pD3DDevice = ранее инициализированный объект устройства ID3DXSprite *pSprite = NULL; if(FAILED(D3DXCreateSprite(g_pD3DDevice, &pSprite))) { // Произошла ошибка }
Как видите, функция D3DXCreateSprite получает два параметра — указатель на ранее инициализированный объект трехмерного устройства и указатель на создаваемый объект ID3DXSprite. После этого вызова функции все готово к работе. Вам необходимо только загрузить текстуру, представляющую блок (или блоки), который вы собираетесь рисовать и воспользоваться функцией ID3DXSprite::Draw для рисования блока:
HRESULT ID3DXSprite::Draw( IDirect3DTexture9 *pSrcTexture, // Используемая текстура CONST RECT *pSrcRect, // Прямоугольник источника CONST D3DXVECTOR2 *pScaling, // Вектор масштабирования CONST D3DXVECTOR2 *pRotationCenter, // Центр вращения FLOAT Rotation, // Угол поворота CONST D3DVECTOR2 *pTranslation, // Вектор перемещения D3DCOLOR Color); // Модуляция цвета
Трюк в использовании функции Draw заключается в конструировании структуры RECT для прямоугольника источника, содержащей координаты фрагмента внутри текстуры, который вы хотите использовать как блок. Например, у вас есть текстура, размером 256 × 256 пикселей, содержащая 64 блока (каждый размером 32 × 32 пикселя), упакованных в 8 строк и 8 столбцов (как показано на рис. 7.2).
Рис. 7.2. 64 блока, упорядоченные в таблицу 8 × 8
Если вы рисуете второй слева и третий сверху блок, прямоугольник источника будет занимать координаты от 64, 96 до 96, 128. Поскольку размер всех блоков 32 × 32, необходимо знать только координаты верхнего левого угла блока в текстуре; для вычисления нижней и правой координат блока достаточно просто прибавить 32. Вот как инициализируется структура RECT с использованием этих значений:
RECT SrcRect; SrcRect.left = 64; // Левая координата блока SrcRect.top = 96; // Верхняя координата блока SrcRect.right = SrcRect.left + 32; // Добавляем ширину блока SrcRect.bottom = SrcRect.top + 32; // Добавляем высоту блока
Обратите внимание, что если pSrcRect присвоить значение NULL, то функция рисования будет использовать в качестве блока всю текстуру целиком. Параметр pScaling — это вектор, определяющий как вы хотите масштабировать размеры блока. Если вы не будете использовать масштабирование, укажите NULL; в ином случае сконструируйте вектор, содержащий коэффициенты масштабирования.
Масштабирование — это единственная из специальных возможностей работы с блоками, которую я собираюсь использовать. Так что вы можете присвоить pRotationCenter значение NULL, а Rotation — 0,0.
Заслуживает внимания параметр pTransform, представляющий собой вектор, который сообщает объекту спрайта в каком месте (в экранных координатах от 0 до ширины или высоты окна) рисовать блок. Обратите внимание, что все блоки рисуются сверху вниз и слева направо, подразумевая, что началу координат соответствует левый верхний угол блока.
Последний аргумент функции Draw — это Color, являющийся значением типа D3DCOLOR, используемым для корректировки выходной текстуры. Обычно вы здесь задаете значение 0xFFFFFFFF, чтобы блоки рисовались точно так же, как они выглядят в текстуре, но, используя макрос D3DCOLOR_RGBA вы можете при рисовании изменять цвета или альфа-значения блоков.
Например, чтобы нарисовать блок с половинным альфа-значением, используйте:
D3DCOLOR_RGBA(255,255,255,127); // 127 = половина
Как видите, значения берутся из диапазона от 0 (цвет или альфа-значение удаляется) до 255 (цвет или альфа-значение остается неизменным). Точно так же, как мы меняли альфа-значение, можно полностью вырезать красную составляющую, используя следующий код:
D3DCLOR_RGBA(0,255,255,255); // 0 = нет цвета
Это открывает ряд роскошных возможностей, таких как создание дневных и ночных сцен или блоки, через которые видна расположенная за ними графика (например, окна).
Вернемся к основной теме и посмотрим, как можно использовать функцию Draw для рисования блоков. Ниже приведен пример функции, которая получает текстуру, координаты источника и координаты места назначения для рисования блока (обратите внимание, что у координат тип float, хотя они определены в экранном пространстве, то есть в размерах области отображения).
// Убедитесь, что перед вызовом этой функции вы вызвали // IDirect3DDevice::BeginScene и загрузили текстуру, // используемую для хранения блоков. // pSprite = ранее инициализированный объект ID3DXSprite void DrawTile(float SrcX, float SrcY, float DestX, float DestY, float TileWidth, float TileHeight float ScaleX, float ScaleY, IDirect3DTexture9 *pTileTexture, D3DCOLOR Color) { RECT SrcRect; // Прямоугольник источника // Задаем прямоугольник источника SrcRect.left = SrcX; SrcRect.top = SrcY; SrcRect.right = SrcRect.left + TileWidth; SrcRect.bottom = SrcRect.top + TileHeight; // Рисуем блок, используя заданные координаты, // цвет и масштаб. Если вы хотите, чтобы блок // был нарисован в исходном масштабе, установите // для ScaleX и ScaleY значения 1.0 pSprite->Draw(pTileTexture, &SrcRect, &D3DXVECTOR2(ScaleX, ScaleY), NULL, 0.0f, &D3DXVECTOR2(DestX, DestY), Color); }
Хотя использование объекта ID3DXSprite для рисования блоков сперва может показаться странным (и слегка неоптимизированным), я уверяю вас, что он хорошо справляется со своей работой. Далее у нас есть один путь — построить специальный класс, который будет работать с блоками за вас, включая их загрузку и рисование.
Поскольку вы узнали, как рисовать блоки, наступило хорошее время для конструирования класса, который будет для вас работать с блоками. Класс должен быть минимальным — достаточно загрузки текстуры блоков и рисования их на указанном устройстве. Взгляните на созданный мной простой класс работы с блоками. Чтобы упростить разработку я интегрировал в него графическое ядро.
class cTiles { private: cGraphics *m_Graphics; // Родительский объект cGraphics long m_NumTextures; // Количество текстур cTexture *m_Textures; // Массив cTexture short *m_Widths; // Массив широт блоков short *m_Heights; // Массив высот блоков short *m_Columns; // Количество столбцов в текстуре public: cTiles(); ~cTiles(); // Функции для создания и освобождения интерфейса блока BOOL Create(cGraphics *Graphics, long NumTextures); BOOL Free(); // Функции для загрузки и освобождения отдельной текстуры BOOL Load(long TextureNum, char *Filename, short TileWidth = 0, short TileHeight = 0, D3DCOLOR Transparent = 0, D3DFORMAT Format = D3DFMT_A1R5G5B5); BOOL Free(long TextureNum=-1); // Функции для получения размеров блока // и количества блоков в текстуре long GetWidth(long TextureNum); long GetHeight(long TextureNum); long GetNum(long TextureNum); // Разрешение и запрещение прозрачности BOOL SetTransparent(BOOL Enabled = TRUE); // Рисование блока в указанном месте BOOL Draw(long TextureNum, long TileNum, long ScreenX, long ScreenY, D3DCOLOR Color = 0xFFFFFFFF, float XScale = 1.0f, float YScale = 1.0f); };
Представленный здесь класс cTiles работает выделяя массив объектов cTexture, в которых будет храниться блочная графика. Код класса cTiles находится на прилагаемом к книге компакт-диске в папке с примерами к данной главе (загляните в папку \BookCode\Chap07\). В следующих разделах приведен код открытых функций, описание того, что они делают, и того, как их вызывать.
BOOL cTiles::Create( cGraphics *Graphics, // Ранее инициализированный объект cGraphics long NumTextures); // Количество создаваемых объектов текстур
Первая функция, cTiles::Create, выделяет память для массива объектов cTexture, в которых будет храниться блочная графика. Убедитесь, что передаете функции Create ранее инициализированный объект cGraphics и указываете количество текстур, достаточное для хранения блоков.
BOOL cTiles::Create(cGraphics *Graphics, long NumTextures) { Free(); // Освобождаем все от существующих данных // Проверка ошибок if((m_Graphics = Graphics) == NULL) return FALSE; if((m_NumTextures = NumTextures) == NULL) return FALSE; // Выделяем память для объектов текстур if((m_Textures = new cTexture[m_NumTextures]) == NULL) return FALSE; // Выделяем память для высот, широт и количества столбцов m_Widths = new long[m_NumTextures]; m_Heights = new long[m_NumTextures]; m_Columns = new long[m_NumTextures]; return TRUE; // Успешное завершение! }
BOOL cTiles::Free();
Функция не получает параметров, поскольку освобождает все ресурсы и объекты класса. Никакие вызовы Load, Draw или Free не будут работать, пока экземпляр класса cTiles не будет заново инициализирован вызовом cTiles::Create.
BOOL cTiles::Free() { m_Graphics = NULL; // Освобождаем все текстуры if(m_NumTextures) { for(short i = 0; i < m_NumTextures; i++) m_Textures[i].Free(); } delete [] m_Textures; m_Textures = NULL; // Освобождаем массивы высот широт и столбцов delete [] m_Widths; delete [] m_Heights; delete [] m_Columns; m_Widths = m_Heights = m_Columns = NULL; m_NumTextures = 0; return TRUE; }
BOOL cTilesLoad( long TextureNum, // Номер текстуры, куда загружается графика char *Filename, // Имя загружаемого файла с изображением (*.bmp) short TileWidth, // Ширина блоков в изображении short TileHeight, // Высота блоков в изображении D3DCOLOR Transparent, // Прозрачный цвет (установите альфа=255) D3DFORMAT Format); // Формат хранения
Функция cTiles::Load выполняет загрузку текстуры в указанный элемент массива текстур. Например, если вы создали объект cTiles для использования пяти текстур, можно указать любой элемент от 0 до 4, в который и будет загружена текстура. На все текстуры ссылаются по их индексу в массиве текстур.
Загружая файл текстуры вы должны указать размер хранящихся в текстуре блоков (измеряется в пикселях). Эти блоки должны быть упакованы в текстуру слева направо и сверху вниз, причем первый блок начинается с верхнего левого пикселя текстуры. Например, у вас может быть текстура, содержащая 64 блока, каждый размером 32 × 32 пикселя. Это означает, что в текстуре 8 строк и 8 столбцов блоков, как показано на рис. 7.2.
Последние два параметра полезны только если вы используете копирование с учетом прозрачности. Присвойте параметру Transparent допустимое значение D3DCOLOR (используйте D3DCOLOR_RGBA или аналогичный макрос, убедившись, что указали альфа-значение 255) и либо оставьте для Format значение по умолчанию D3DFMT_A1R5G5B5, либо задайте собтвенный формат из предоставляемого Direct3D списка допустимых форматов.
Вот код функции Load:
BOOL cTiles::Load(long TextureNum, char *Filename, short TileWidth, short TileHeight, D3DCOLOR Transparent, D3DFORMAT Format) { // Проверка ошибок if(TextureNum >= m_NumTextures || m_Textures == NULL) return FALSE; // Освобождаем требуемую текстуру Free(TextureNum); // Загружаем текстуру if(m_Textures[TextureNum].Load(m_Graphics, Filename, Transparent, Format) == FALSE) return FALSE; // Сохраняем значение ширины (получаем ширину текстуры, // если не задан параметр TileWidth). if(!TileWidth) m_Widths[TextureNum] = m_Textures[TextureNum].GetWidth(); else m_Widths[TextureNum] = TileWidth; // Сохраняем значение высоты (получаем высоту текстуры, // если не задан параметр TileHeight). if(!TileHeight) m_Heights[TextureNum] = m_Textures[TextureNum].GetHeight(); else m_Heights[TextureNum] = TileHeight; // Вычисляем склько столбцов блоков находится в текстуре. // Это используется для ускорения вычислений при рисовании блоков. m_Columns[TextureNum] = m_Textures[TextureNum].GetWidth() / m_Widths[TextureNum]; return TRUE; }
BOOL cTiles::Free(long TextureNum); // Номер освобождаемой текстуры
Функция освобождает отдельную текстуру из массива, оставляя возможность ее повторного использования путем вызова cTiles::Load. Просто укажите номер освобождаемой текстуры в аргументе TextureNum. Функцию можно использовать для освобождения старых текстур и выделения места для новых.
BOOL cTiles::Free(long TextureNum) { // Проверка ошибок if(TextureNum >= m_NumTextures || m_Textures == NULL) return FALSE; // Освобождаем ресурсы отдельной текстуры m_Textures[TextureNum].Free(); return TRUE; }
long cTiles::GetWidth(long TextureNum); // Номер интересующей текстуры long cTiles::GetHeight(long TextureNum); long cTiles::GetNum(long TextureNum);
Вы используете эти три функции для получения ширины, высоты и количества блоков в текстуре, соответственно. Вы будете редко обращаться к этим функциям напрямую, но их полезно иметь в классе работы с блоками.
long cTiles::GetWidth(long TextureNum) { // Проверка ошибок if(TextureNum >= m_NumTextures || m_Widths == NULL) return 0; return m_Widths[TextureNum]; } long cTiles::GetHeight(long TextureNum) { // Проверка ошибок if(TextureNum >= m_NumTextures || m_Heights == NULL) return 0; return m_Heights[TextureNum]; } long cTiles::GetNum(long TextureNum) { // Проверка ошибок if(TextureNum >= m_NumTextures || m_Textures == NULL || m_Columns == NULL || m_Widths == NULL || m_Heights == NULL) return 0; return m_Columns[TextureNum] + m_Textures[TextureNum].GetHeight() / m_Heights[TextureNum]; }
BOOL cTiles::SetTransparent(BOOL Enabled); // Разрешить/запретить
Функция cTiles::SetTransparent разрешает или запрещает альфа-проверку, а это значит, что текстуры, загруженные с соответствующим цветовым ключом и форматом когда разрешено, будут использовать копирование с учетом прозрачности. По умолчанию параметру Enable присваивается TRUE. Взгляните на код функции SetTransparent:
BOOL cTiles::SetTransparent(BOOL Enabled) { // Проверка ошибок if(m_Graphics == NULL) return FALSE; return m_Graphics->EnableAlphaTesting(Enabled); }
BOOL cTiles::Draw( long TextureNum, // Номер рисуемой текстуры long TileNum, // Номер рисуемого блока long ScreenX, // Координата X long ScreenY, // Координата Y D3DCOLOR Color, // Значение модулирования цвета float XScale, // Коэффициент масштабирования по x float YScale); // Коэффициент масштабирования по y
Эта функция используется для рисования ваших блоков (через метод Blit вашего объекта ID3DXSprite). Как только текстура загружена в массив, вы можете рисовать отдельные, содержащиеся в ней блоки. Все блоки нумеруются, начиная с нуля; нумерация идет с верхнего левого угла и увеличивается слева направо и сверху вниз. Например, в текстуре с 64 блоками (в сетке 8 × 8) нумерация идет от 0 до 63. Блок 0 — это верхний левый блок, под ним располагается блок 8, а справа от него — блок 1.
При рисовании вам надо указать экранные координаты в переменных типа long (позднее они будут преобразованы в значения float), а также коэффициенты масштабирования (для увеличения или уменьшения размера блока относительно исходного). Как упоминалось ранее, значение модуляции применяется для изменения цвета или альфа-значения с использованием интерфейса ID3DXSprite.
BOOL cTiles::Draw(long TextureNum, long TileNum, long ScreenX, long ScreenY, D3DCOLOR Color, float XScale, float YScale) { long SrcX, SrcY; // Проверка ошибок if(m_Graphics == NULL) return FALSE; if(TextureNum >= m_NumTextures || m_Textures == NULL) return FALSE; // Вычисление координат источника блока в текстуре SrcX = (TileNum % m_Columns[TextureNum]) * m_Widths[TextureNum]; SrcY = (TileNum / m_Columns[TextureNum]) * m_Heights[TextureNum]; return m_Textures[TextureNum].Blit(ScreenX, ScreenY, SrcX, SrcY, m_Widths[TextureNum], m_Heights[TextureNum], XScale, YScale); }
Вот пример, который загружает две текстуры с блоками. Первая текстура содержит 64 блока, каждый размером 32 × 32 пикселя. Вторая текстура содержит 16 блоков, каждый размером 64 × 64 пикселя.
// Graphics = ранее инициализированный объект cGraphics cTiles Tile; // Создаем класс блоков с местом для двух текстур Tile.Create(Graphics, 2); // Загружаем обе текстуры, указывая, // что черный цвет будет прозрачным Tile.Load(0, "Tiles1.bmp", 32, 32, D3DCOLOR_RGBA(0,0,0,255), D3DFMT_A1R5G5B5); Tile.Load(1, "Tiles2.bmp", 64, 64, D3DCOLOR_RGBA(0,0,0,255), D3DFMT_A8R8G8B8); // Рисуем пару блоков из первой текстуры без прозрачности Tile.SetTransparent(FALSE); // Блок 0 (в точке 128, 128) и 3 (в точке 0, 0) Tile.Draw(0, 0, 128, 128); Tile.Draw(0, 3, 0, 0); // Рисуем пару блоков из второй текстуры с прозрачностью Tile.SetTransparent(TRUE); // Блок 1 (в точке 28, 18) и 16 (в точке 100, 90) Tile.Draw(1, 1, 28, 18); Tile.Draw(1, 16, 100, 90); // Освобождаем класс работы с блоками и текстуры Tile.Free();
Вот и все. Класс работы с блоками компактен и замечательно дополнит вашу библиотеку двухмерной графики, поскольку выполняет за вас все операции, связанные с рисованием блоков. От вас требуется только предоставить классу графику, которую вы хотите использовать, и все готово к работе.
netlib.narod.ru | < Назад | Оглавление | Далее > |