Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DirectX. Продвинутая Анимация (2004) [rus].pdf
Скачиваний:
335
Добавлен:
16.08.2013
Размер:
8.39 Mб
Скачать

Синхронизацияанимацииидвижения

69

Рис.2.7. Комбинация двух прямолинейных и одной криволинейной траектории образует сложный маршрут

{ PATH_STRAIGHT, D3DXVECTOR3(25.0f, 0.0f, -50.0f), \

D3DXVECTOR3(-50.0f, 0.0f, 0.0f), \

D3DXVECTOR3(0.0f, 0.0f, 0.0f), \

D3DXVECTOR3(0.0f, 0.0f, 0.0f) } \

Конечно же вы не должны вручную записывать маршруты в ваших проектах; более правильным было бы использование внешнего источника, такого как .X файл для хранения данных вашего маршрута.

Создание анализатора маршрутов .X файла

Самым простым местом, откуда можно получать данные о траектории, как вы уже, наверное, догадались, являются .X файлы! Вы можете создать несколько простых шаблонов, используя специализированный анализатор .X файлов, для получения траекторий, используемых в ваших проектах. Вы даже можете создавать маршруты из ваших шаблонов траекторий для облегчения жизни!

70

Глава2

Вы

можете скопировать основные структуры траекторий, разработанные

в предыдущем разделе, для использования их в качестве шаблонов ваших .X файлов. Основной шаблон траектории будет содержать четыре вектора: первые два являются начальной и конечной точкой траектории (как для прямолинейной, так и для криволинейной), а оставшиеся два - для вспомогательных координат (криволинейных траекторий). Посмотрите на одно объявление шаблона, которое вы можете использовать.

//{F8569BED-53B6-4 923-AF0B-59A09271D556}

//DEFINE_GUID(Path,

//0xf8569bed, 0x53b6, 0x4923,

//0xaf, 0xb, 0x59, 0ха0, 0x92, 0x71, 0xd5, 0x56);

template Path {

<F8569BED-53B6-4 923-AF0B-59A09271D556>

DWORD Type; // 0=прямолинейный, 1=криволинейный Vector Start; // начальная точка

Vector Point1; // промежуточная 1 Vector Point2; // промежуточная 2 Vector End; // конечная точка

}

После того как вы определили шаблон траектории вашего .X файла, вы можете использовать его неограниченное число раз в ваших файлах данных. Для загрузки этих траекторий вам необходимо создать шаблон маршрута (называемый Route), который позволит вам определять множественные объекты траекторий. Этот шаблон маршрута просто содержит массив объектов Path, как показано ниже:

//{18AA1C92-16AB-47a3-B002-6178F9D2D12F}

//DEFINE_GUID(Route,

//0x18aa1c92, 0x16ab, 0x47a3,

//0xb0, 0x2, 0x61, 0x78, 0xf9, 0xd2, 0xd1, 0x2f);

template Route { <18AA1C92-16AB-47a3-B002-6178F9D2D12F> DWORD NumPaths;

array Path Paths[NumPaths];

}

В качестве примера использования шаблона Route давайте посмотрим, как будет выглядеть рассмотренный выше маршрут в .X файле.

Route MyRoute {

3; // 3 траектории 0; // прямолинейный тип траектории -50.0, 0.0, 0.0; 0.0, 0.0, 0.0; 0.0, 0.0, 0.0; -50.0, 0.0, 25.0;,

Синхронизация анимации идвижения

71

1; // криволинейный тип траектории -50.0, 0.0, 25.0; 0.0, 0.0, 50.0; 50.0, 0.0, 0.0; 25.0, 0.0, -50.0;,

0; // прямолинейный тип траектории 25.0, 0.0, -50.0; 0.0, 0.0, 0.0; 0.0, 0.0, 0.0; -50.0, 0.0, 0.0;;

}

Вы можете получить доступ к вашему объекту маршрута из .X файла, используя специальный анализатор. Этот анализатор ищет только объекты Route. Когда он их находит, то создает массив структур sPath и считывает данные. Данные маршрутов при этом хранятся в виде связанного списка, так что вы можете загружать несколько маршрутов. Эти данные маршрута используют следующий класс:

class cRoute

{

public:

DWORD m_NumPaths; // # траекторий в списке sPath *m_Paths; // список траекторий

cRoute *m_Next; // следующий маршрут в связанном списке

public:

cRoute() { m_Paths = NULL; m_Next = NULL; } ~cRoute() { delete [] m_Paths; delete m_Next; }

};

Необходимо также немного улучшить структуру sPath. Нужно добавить в нее длину каждой траектории и начальное положение в наборе. Сделать это очень просто. Длина, как вы могли убедиться ранее, является вещественным числом, а начальное положение траектории - это просто сумма длин всех предыдущих траекторий в списке. Новая структура sPath будет выглядеть так:

typedef struct { DWORD Type;

D3DXVECTOR3 vecStart, vecEnd; D3DXVECTOR3 vecPoint1, vecPoint2; float Start; // Начальное положение float Length; // Длина траектории

} sPath;

Основной причиной включения длины и начальной позиции траектории в структуру sPath является забота о скорости. Предварительное вычисление значения длин при загрузке данных траектории позволяет вам быстро получать доступ к этим

72

Глава2

данным (длине и начальному положению) при определении траектории, на которой сейчас расположен объект, используя для этого смещение в маршруте.

Язнаю, что это звучит странно, но подумайте сами - начальное положение

идлина каждой траектории являются подобием ключевых кадров; просто вместо измерения времени вы измеряете длины траекторий. Имея расположение объекта на маршруте (скажем 516 единиц), вы можете просмотреть список траекторий, чтобы определить на какой находится объект.

Предположим, маршрут использует шесть траекторий, и четвертая начинается в 400 единицах. Четвертая траектория имеет длину 128 единиц, т. е. перекрывает значения от 400 до 528. Объект, расположенный в 516 единицах, находится на четвертой траектории; отняв положение объекта (516) от конечного положения траектории (528), вы найдете смещение в траектории, которое можете использовать для вычисления скаляра, необходимого при вычислении координат объекта, лежащего на траектории. В этом случае позиция будет 528-516, или 12 единиц, и скаляр будет 12/128, или 0.09375.

Достаточно разговоров, давайте перейдем к коду! Нижеприведенный класс cXRouteParser наследуется от cXParser, таким образом, вы имеете доступ к данным объекта анализатора. Класс cXRouteParser должен просто находить объекты Route и загружать подходящие данные траекторий в созданный класс Route, который привязан к списку маршрутов.

Посмотрите объявление класса cXRouteParser, который содержит указатель на корневой объект Route и шесть функций (три из которых содержат встроенный код класса).

class cXRouteParser : public cXParser

{

protected:

BOOL ParseTemplate(IDirectXFileData *pDataObj, \ IDirectXFileData *pParentDataObj, \ DWORD Depth, \

void **Data, BOOL Reference);

public:

cRoute *m_Route;

public:

cXRouteParser() { m_Route = NULL; } - cXRouteParser () { Free(); }

void Free() { delete m_Route; m_Route = NULL; } void Load(char *Filename);

void Locate(DWORD Distance, D3DXVECTOR3 *vecPos);

Синхронизация анимации идвижения

73

Основной функцией cXRouteParser является, конечно же, шаблон анализатора данных. Немного позже я покажу вам функцию анализатора. Функция Load просто выполняет настройку перед вызовом Parse, которая преобразует загруженные шаблоны Route в связанный список. Функция Locate вычисляет положение на траектории маршрута, которое вы можете использовать для перемещения объекта. Код функции Locate я покажу вам также немного позже. А пока я хочу вернуться к функции ParseTemplate.

Функция ParseTemplate ищет только один шаблон объекта - Route. Как только он найден, создается класс cRoute, структуры траекторий и загружаются данные. Класс cRoute помещается в список загруженных маршрутов, после чего продолжается разбор .X файла. Вот как выглядит код функции ParseTemplate:

BOOL cXRouteParser::ParseTemplate(IDirectXFileData *pDataObj, \ IDirectXFileData *pParentDataObj, \

DWORD Depth, \

void **Data, BOOL Reference)

{

const GUID *Type = GetTemplateGUID(pDataObj);

// Обрабатывать только объекты Route if(*Type == Route) {

// получить указатель на данные

DWORD *DataPtr = (DWORD*)GetTemplateData(pDataObj, NULL);

//Создать и связать маршрут cRoute *Route = new cRoute(); Route->m_Next = m_Route; m_Route = Route;

//Получить # траекторий в маршруте и создать список Route->m_NumPaths = *DataPtr++;

Route->m_Paths = new sPath[Route->m_NumPaths];

//Получить данные траектории

for(DWORD i=0;i<Route->m_NumPaths;i++) {

// Получить тип траектории Route->m_Paths[i].Type = *DataPtr++;

// Получить векторы

D3DXVECTOR3 *vecPtr = (D3DXVECTOR3*)DataPtr; DataPtr+=12; // skip ptr ahead Route->m_Paths[i].vecStart = *vecPtr++; Route->m_Paths[i].vecPoint1 = *vecPtr++; Route->m_Paths[i].vecPoint2 = *vecPtr++; Route->m_Paths[i].vecEnd = *vecPtr++;

// Вычислить длину траектории на основании ее типа if(Route->m_Paths[i].Type == PATH_STRAIGHT) {

Route->m_Paths[i].Length = D3DXVec3Length( \

74

&(Route->m_Paths[i].vecEnd - \ Route->m_Paths[i].vecStart));

}else {

float Length01 = D3DXVec3Length( \ &(Route->m_Paths[i].vecPoint1 - \

Route->m_Paths[i].vecStart)); float Length12 = D3DXVec3Length( \

&(Route->m_Paths[i].vecPoint2 - \ Route->m_Paths[i].vecPoint1));

float Length23 = D3DXVec3Length( \ &(Route->m_Paths[i].vecEnd - \

Route->m_Paths[i].vecPoint2)); float Length03 = D3DXVec3Length( \

&(Route->m_Paths[i].vecEnd - \ Route->m_Paths[i].vecStart));

Route->m_Paths[i].Length = (Length01+Length12+ \ Length23)*0.5f+Length03*0.5f;

}

// сохранить начальное положение траектории

if(i)

Route->m_Paths[i].Start = Route->._Paths[i-1].Start + \ Route->m_Paths[i-1].Length;

else

Route->m_Paths[i].Start = 0.0f;

}

}

// анализировать дочерние шаблоны

return ParseChildTemplates(pDataObj, Depth, Data, Reference);

}

Обычно вы не вызываете ParseTemplate напрямую - функция cXRouteParser::Load вызывает Parse, которая, в свою очередь, вызывает ParseTemplate. Зная это, посмотрите на функцию Load (единственным параметром которой является имя анализируемого .X файла)

void cXRouteParser::Load(char *Filename)

{

Free (); // Освободить загруженные маршруты Parse(Filename);

}

Коротко и ясно - как я люблю! Функция Load на самом деле является промежуточной и используется для освобождения данных ранее загруженных маршрутов и дальнейшего вызова функции Parse. Вот и все!

После того как вы определили шаблоны, создав специализированный класс, загрузили данные маршрута, пришло время двигать объекты! Для этого вернемся к функции cXRouteParser::Locate. Вы, наверное, уже заметили, что в прототипе

Синхронизация анимации идвижения

75

функции Locate используются только вещественное число и вектор. Для упрощения, я буду просматривать только первый маршрут в списке для нахождения расположения объекта.

Совет. Для просмотра множества маршрутов функцией Locate вы могли бы хранить имя каждого объекта Route, которое можно было бы использовать в качестве параметра при вызове функции Locate, для задания просматриваемого маршрута.

Имея текущее время и расстояния (в трехмерных единицах), на которое необходимо сдвинуть объект, вы можете просмотреть все траектории для отыскания той, на которой находится объект в заданное время. После этого вы вычисляете, как далеко между началом и концом этой траектории находится объект, и корректируете его положение.

Предположим, имеем объект, движущийся со скоростью 200 единиц в секунду, т.е 0.2 единицы в миллисекунду. Умножив скорость в секунду на общее время движения вдоль траектории, получим положение объекта на маршруте. Используйте это положение как параметр Distance при вызове функции Locate.

Функция Locate берет предоставленное ей расстояние и просматривает каждую траекторию, содержащуюся в маршруте. Помните, вы уже вычислили начальную точку и длину для каждой траектории, так что достаточно простой проверки, чтобы убедиться, что расстояние объекта больше начала траектории и меньше ее длины. Посмотрите код функции Locate, чтобы понять, что я имею ввиду.

void cXRouteParser::Locate(float Distance, D3DXVECTOR3 *vecPos)

{

//Просмотр первого маршрута в списке cRoute *Route = m_Route;

if(!Route)

return;

//Просмотр каждой траектории в маршруте for(DWORD i=0;i<Route->m_NumPaths;i++) {

//Посмотреть, попадает ли расстояние на текущую траекторию if(Distance >= Route->m_Paths[i].Start && \

Distance < Route->m_Paths[i].Start + \ Route->m_Paths[i].Length) {

//Использовать Distance для получения смещения

//в текущей траектории

Distance -= Route->m_Paths[i].Start;

// Calculate the scalar value to use

float Scalar = (float)Distance/Route->m_Paths[i].Length;

// Вычислить координаты на основании типа траектории if(Route->m_Paths[i].Type == PATH_STRATGHT) {

*vecPos = (Route->m_Paths[i].vecEnd - \

Соседние файлы в предмете Программирование на C++