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

Глава 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 является последним ключевым кадром в массиве, то новому ключу необходимо присвоить то же самое значение.

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