| 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 | < Назад | Оглавление | Далее > |