- •Введение
- •Глава 1. Подготовка к изучению книги
- •Установка DirectX SDK
- •Выбор отладочных или рабочих версий библиотек
- •Настройка вашего компилятора
- •Установка директорий DirectX SDK
- •Привязывание к библиотекам DirectX
- •Установка используемого по умолчанию состояния символа
- •Использование вспомогательного кода книги
- •Использование вспомогательных объектов
- •Проверка вспомогательных функций
- •Двигаясь дальше по книге
- •Глава 2. Синхронизация анимации и движения
- •Использование движения, синхронизированного по времени
- •Считывание времени в Windows
- •Анимирование с использованием временных меток
- •Перемещение, синхронизированное со временем
- •Движение вдоль траекторий
- •Создание анализатора маршрутов .X файла
- •Создание внутриигровых кинематографических последовательностей
- •Посмотрите демонстрационные программы
- •TimedAnim
- •TimedMovement
- •Route
- •Cinematic
- •Глава 3. Использование формата файла .X
- •Работа с .X шаблонами и объектами данных
- •Определение шаблонов
- •Работа со стандартными шаблонами DirectX
- •Открытие .X файла
- •Перечисление объектов данных
- •Получение данных объекта
- •Создание класса .X анализатора
- •Загрузка мешей с использованием D3DX
- •Загрузка мешей, используя анализатор .X
- •Загрузка скелетных мешей
- •Загрузка анимации из .X
- •Загрузка специализированных данных из .X
- •Посмотрите демонстрационные программы
- •ParseFrame
- •Глава 4. Работа со скелетной анимацией
- •Начало скелетной анимации
- •Использование структур скелетов и иерархий костей
- •Использование скелетной структуры и скелетного меша
- •Загрузка иерархий из .X
- •Изменение положения костей
- •Обновление иерархии
- •Работа со скелетными мешами
- •Загрузка скелетных мешей из .X
- •Создание контейнера вторичного меша
- •Сопоставление костей фреймам
- •Обновление скелетного меша
- •Визуализация скелетных мешей
- •Глава 5. Использование скелетной анимации, основанной на ключевых кадрах
- •Использование наборов скелетных анимаций, основанных на ключевых кадрах
- •Использование ключей при анимации
- •Работа с четырьмя типами ключей
- •Считывание данных анимации из .X файлов
- •Прикрепление анимации к костям
- •Обновление анимации
- •Посмотрите демонстрационные программы
- •Глава 6. Комбинирование скелетных анимаций
- •Комбинирование скелетных анимаций
- •Соединение преобразований
- •Улучшение объектов скелетной анимации
- •Посмотрите демонстрационные программы
- •Глава 7. Создание кукольной анимации
- •Работа с физикой твердого тела
- •Создание твердого тела
- •Расположение и ориентирование твердых тел
- •Обработка движения твердых тел
- •Использование сил для создания движения
- •Соединение твердых тел с помощью пружин
- •Обеспечение обнаружения столкновений и ответной реакции
- •Создание систем кукольной анимации
- •Определение состояния твердого тела
- •Хранение костей
- •Создание класса управления куклой
- •Создание данных костей
- •Вычисление ограничивающего параллелепипеда кости
- •Установка сил
- •Объединение костей
- •Обработка столкновений
- •Восстановление соединений костей
- •Перестроение иерархии
- •Посмотрите демонстрационные программы
- •Глава 8. Работа с морфирующей анимацией
- •Морфинг в действии
- •Определение исходного и целевого меша
- •Морфинг мешей
- •Создание морфированного меша при помощи обработки
- •Визуализация морфированных мешей
- •Расчленение наборов
- •Создание морфирующего вершинного шейдера
- •Посмотрите демонстрационные программы
- •Глава 9. Использование морфирующей анимации, основанной на ключевых кадрах
- •Использование наборов морфируемой анимации
- •Создание шаблонов .X для морфируемой анимации
- •Загрузка данных морфируемой анимации
- •Визуализации морфированного меша
- •Получение данных морфируемого меша из альтернативных источников
- •Посмотрите демонстрационные программы
- •Глава 10. Комбинирование морфированных анимаций
- •Комбинирование морфированных анимаций
- •Использование базового меша в комбинированных морфированных анимациях
- •Вычисление разностей
- •Комбинирование разностей
- •Создание вершинных шейдеров комбинированного морфирования
- •Использование вершинного шейдера морфируемого комбинирования
- •Посмотрите демонстрационные программы
- •Глава 11. Морфируемая лицевая анимация
- •Основы лицевой анимации
- •Использование комбинированного морфирования
- •Использования фонем для речи
- •Создание лицевых мешей
- •Создание базового меша
- •Создание выражений лица
- •Создание мешей визем
- •Создание анимационных последовательностей
- •Создание последовательностей фонем
- •Использование анализатора файлов .X для последовательностей
- •Проигрывание лицевых последовательностей со звуком
- •Использование DirectShow для звука
- •Синхронизация анимации со звуком
- •Зацикливание воспроизведения звуков
- •Посмотрите демонстрационные программы
- •Глава 12. Использование частиц в анимации
- •Работа с частицами
- •Основы
- •Рисование частиц с помощью квадратных полигонов
- •Работа с точечными спрайтами
- •Улучшения визуализации частиц при помощи вершинных шейдеров
- •Оживление частиц
- •Передвижение частиц при помощи скорости
- •Использование интеллекта при обработке
- •Создание и уничтожение частиц
- •Управление частицами с помощью класса
- •Использование излучателей в проектах
- •Создание движков частиц в вершинных шейдерах
- •Посмотрите демонстрационные программы
- •Глава 13. Имитирование одежды и анимация мешей мягких тел
- •Имитация одежды в ваших проектах
- •Получение данных одежды из мешей
- •Приложение сил для создания движения
- •Воссоздание и визуализация меша одежды
- •Восстановление исходного меша
- •Добавление дополнительных пружин
- •Загрузка данных масс и пружин из .X файла
- •Создание анализатора .X данных одежды
- •Работа с обнаружением столкновений и реакцией на них
- •Определение объектов столкновений
- •Обнаружение и реакция на столкновения
- •Создание класса меша одежды
- •Использование мешей мягких тел
- •Восстановление мешей мягких тел
- •Посмотрите демонстрационные программы
- •Глава 14. Использование анимированных текстур
- •Использование анимации текстур в ваших проектах
- •Работа с преобразованиями текстур
- •Создание преобразования текстур
- •Установка матриц преобразования текстуры
- •Использование преобразования текстур в проектах
- •Использование файлов видео в качестве текстур
- •Импорт видео при помощи DirectShow
- •Создание специализированного фильтра
- •Работа со специализированным фильтром
- •Создание менеджера анимированных текстур
- •Окончание современной анимации
- •Веб-сайты
- •Рекомендуемые книги
- •DirectX 9.0 SDK
- •GoldWave Demo
- •Paint Shop Pro Trial Version
- •TrueSpace Demo
- •Microsoft Agent and LISET
- •Предметный указатель
Глава 2
Синхронизация анимации и движения
Игры наполнены движением. Здесь бегают персонажи, там летают пули, зачастую существует множество объектов, перемещающихся по игровому миру. Очень важным аспектом, который нельзя проигнорировать, является плавное движение всех этих объектов. Вы никогда не думали использовать анимацию и движение, основанные на времени? Использование движения, основанного на времени, ново, и, чтобы не отставать от остального мира, вы должны уяснить, что оно может помочь вам в ваших игровых проектах. На самом деле вам необходимо не просто понять, как использовать движение, основанное на времени, а что такое движение вообще. Вы думаете, только персонажи перемещаются
ввашей игре? Нет, игровые кинематографические камеры также нуждаются
ввашем управлении. В этой главе рассмотрено использование движения, основанного на времени, в ваших проектах.
Вэтой главе вы научитесь:
•Создавать плавную анимацию, основанную на времени;
•Работать с движением, основанным на времени;
•Перемещать объекты вдоль заранее определенных траекторий;
•Создавать внутриигровые кинематографические последовательности.
54 Глава2
Использование движения, синхронизированного по времени
Хотя сначала может показаться, что синхронизация не важна, на самом деле она играет важную роль в игровом проекте. Я не говорю о времени суток, я имею ввиду синхронизацию анимации вплоть до миллисекунд. Такая точная синхронизация необходима для плавного анимирования и движения объектов в вашем проекте. Эта тема является основной, поэтому все программисты игр должны хорошо понимать ее.
Когда вам приходится двигать ваши меши, движение на основе времени является лучшим решением. На протяжении всей книги я, в основном, использую движение, синхронизированное по времени, для контролирования скорости разнообразных анимаций. В этой главе я хочу подробно объяснить использование времени (для анимации и движения).
Смысл использования движения на основе времени прост - движение и анимация выполняются всегда за одно и то же время на любой системе, независимо от скорости компьютера. Например, 2-ГГц система обрабатывающая 20-секундную анимацию, безусловно, выполнит все быстро, и в результате получиться очень плавное движение, когда как та же самая анимация, обработанная на 500-МГц системе будет неплавной, но все равно будет длиться 20 секунд.
Более чем вероятно, что более медленный компьютер пропустит некоторые кадры анимации, чтобы не отставать по скорости от более быстрых компьютеров. В этом весь секрет - медленные компьютеры могут пропускать большее количество кадров и при этом поддерживать что-то похожее на фактическое движение.
Я думаю, вы начинаете понимать идею. Хотя анимация, основанная на времени, настолько же проста, насколько звучит, начинающим программистам игр она может показаться тайной. И хотя это книга об использовании приемов современной анимации, вы должны полностью понимать движение, основанное на времени. Поэтому я хочу представить вам (или напомнить) мир движения, основанного на синхронизации по времени. Я начну с краткого обзора того, как можно считать время в Windows.
Считывание времени в Windows
Самым простым способом считывания времени в Windows является использование функции timeGetTime. Эта функции не имеет параметров. Как показывает следующий код, timeGetTime просто возвращает количество миллисекунд, прошедших с момента запуска Windows.
Синхронизация анимации идвижения |
55 |
DWORD TimeSinceStarted = timeGetTime();
Что полезного она делает? Обычно, функция timeGetTime вызывается один раз за кадр, обрабатываемый вашей игрой. Для каждого кадра вы вычитаете время последнего обработанного кадра из текущего времени, чтобы получить время в миллисекундах, прошедшее с предыдущего кадра. Вы можете использовать это прошедшее время для расчета вашего движения на основе времени.
Хранить время обновления последнего кадра так же просто, как и использовать статические переменные. В начале функции обновления кадра вставьте статическую переменную, хранящую текущее время.
void FrameUpdate()
{
static DWORD LastTime = timeGetTime();
В конце функции |
FrameUpdate вы можете сохранить текущее время |
в переменной LastTime. |
|
LastTime = timeGetTime();
Таким образом (записывая текущее времени в конце функции обновления кадра), вы сумели сохранить время окончания обновления кадра. В последующие вызовы FrameUpdate переменная LastTime все еще будет содержать время последнего обновления кадра. Используя это значение, вы можете вычислить количество времени, прошедшее с последнего вызова FrameUpdate, вычитая LastTime из текущего времени.
DWORD ElapsedTime = timeGetTime() - LastTime;
Именно это прошедшее время и время последнего обновления кадра вы будете использовать на протяжении всей книги. А теперь, как насчет тех случаев, когда вам необходимо вычислить количество миллисекунд, прошедших с заданного времени, например, с начала анимации, вместо того чтобы считать прошедшее время или время с первого вызова функции FrameUpdate?
Так же как вы использовали статическую переменную для хранения времени последнего обновления кадра, вы можете хранить время первого вызова функции. Используя статическую переменную, вы можете определить, сколько миллисекунд прошло с момента первого вызова функции.
void FrameUpdate()
{
static DWORD StartTime = timeGetTime();
DWORD ElapsedTime = timeGetTime() - StartTime;
56
//ElapsedTime - количество миллисекунд,прошедших
спервого вызова
//функции FrameUpdate.
}
Таким же образом, как вышеприведенный код отслеживал количество времени, прошедшее с первого вызова функции FrameUpdate, можно встроить время начала анимации в свои структуры данных. Вы сможете узнать об этом больше в главе 5 "Использование скелетной анимации на основе ключевых кадров".
Если вы читали последующие главы, то вероятно уже знакомы с использованием анимации, основанной на синхронизации по времени. Все демонстрационные программы, поставляемые вместе с книгой, используют анимацию, отсчитываемую в миллисекундах. В сочетании с ключевыми кадрами, анимация на основе синхронизации по времени является превосходным решением для создания плавной анимации. Читайте далее для более подробного рассмотрения использования анимации, основанной на синхронизации по времени.
Анимирование с использованием временных меток
Раньше игры были сделаны так, чтобы анимировать графику основываясь на обработке каждого кадра. Для того чтобы анимация все время выполнялась на одной и той же скорости, эти игры иногда ограничивали количество кадров, обрабатываемых в секунду. Конечно, старые игры были сделаны для компьютеров, которые не могли обрабатывать более 20-30 кадров в секунду, так что было разумно предполагать, что ограничение кадров никогда не будет превосходить 20 или 30 в секунду.
Но это было тогда, а сейчас есть сейчас. Современные компьютеры могут бегать кругами вокруг своих предшественников, и ограничение количества кадров для управления анимацией определенно устарело. Вам необходимо основываться на скорости анимации, на количестве времени, которое прошло с момента начала анимации. Реализовать это не составляет никакого труда, т. к. вы уже знаете, как записывать время начала анимации. Кроме того, для каждого обновляемого кадра вы можете считывать текущее время и вычитать начало анимации. Полученное время является смещением в анимации.
Предположим, вы используете ключевые кадры, основанные на синхронизации по времени, для вашего анимационного движка. Вы можете использовать простую структуру ключевых кадров для хранения времени и матрицы преобразования, как например:
Синхронизацияанимацииидвижения |
57 |
typedef struct sKeyframe { DWORD Time;
D3DMATRIX matTransformation; } sKeyframe;
Что типично для ключевых кадров, вы можете хранить массив матриц, каждая из которых ассоциирована со своим уникальным временем. Эти матрицы хранятся в хронологическом порядке, с меньшими временами вначале. Поэтому вы можете создать небольшую последовательность преобразований для ориентирования объекта во времени. (Смотри рис. 2.1).
Ключевойкадр3
Ключевые кадры 1 и 4
Ключевойкадр2
Рис. 2.1. Ключевые кадры отображают ориентацию куба. Кадры разделены между собой 400 миллисекундами, и для определения промежуточных ориентации производится интерполяция
Для отображения ключевых кадров, показанных на рис. 2.1, я создал массив:
sKeyframe Keyframes[4] = {
{0, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f; },
{400, 0.000796f, 1.00000f, 0.00000f, 0.00000f, -1.00000f, 0.000796f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 50.00000f, 0.00000f, 0.00000f, 1.00000f; },
{800, -0.99999f, 0.001593f, 0.00000f, 0.00000f,
58
-0.001593f, -0.99999f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 25.00000f, 25.00000f, 0.00000f, 1.00000f; },
{ 1200, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f, 0.00000f, 0.00000f, 0.00000f, 0.00000f, 1.00000f; }
};
А теперь самая веселая часть. Используя методы синхронизации, о которых вы читали ранее, вы можете сохранять время начала анимации. И для каждого кадра, обновляющего анимацию, вы можете посчитать время, прошедшее с начала анимации (используя его для нахождения смещения ключевого кадра). Создайте простую функцию обновления кадров, которая будет определять используемое преобразование на основе времени, прошедшего с первого ее вызова.
void FrameUpdate()
{
static DWORD StartTime = timeGetTime(); DWORD Elapsed = timeGetTime() - StartTime;
Имея значение прошедшего времени, вы можете просмотреть ключевые кадры для нахождения двух, в пределах которых оно лежит. Например, если текущее время 60 миллисекунд, то анимация находится между нулевым кадром (0 миллисекунд) и кадром #1 (400 миллисекунд). Быстро просмотрев ключевые кадры, используя прошедшее время, определяем какие из них использовать.
DWORD Keyframe = 0 ; // Начнем с первого кадра for(DWORD i=0;i<4;i++) {
//Если время больше или равно времени ключевого кадра,
//то обновить используемый ключевой кадр
if(Time >= Keyframes[i].Time) Keyframe = i;
}
В конце цикла в переменной Keyframe будет храниться первый из двух кадров, между которыми находится анимационное время. Если Keyframe не последний ключевой кадр в массиве (в котором всего четыре ключевых кадра), то для получения второго ключевого кадра необходимо добавить 1 к Keyframe. Если же Keyframe является последним кадром в массиве, то можно использовать это же самое значение для ваших вычислений.
Намного лучшим решением является использование второй переменной для хранения значения следующего ключевого кадра. Помните, что если Keyframe является последним ключевым кадром в массиве, то новому ключу необходимо присвоить то же самое значение.