на главную страницу -|- к архиву статей
Территория
Файлы
Статьи и дополнительная информация
Гостевая книга
Команда
"Aurora Toolset": Анимация NPC

ЧАСТЬ I

АНИМАЦИЯ NPC - ПРЕДИСЛОВИЕ

Здравствуйте! Если Вы добрались и до этого чтива, значит Вам интересно попробовать себя в анимации пресонажей. Позволим себе опустить подробности, которые можно и так прочитать в Lexicon'e и попробуем изложить некоторый накопленный опыт, касающийся АНИМАЦИИ NPC.
Все, что здесь изложено и будет изложено в дальнейшем, было написано и протестировано в процессе работы в составе команды WRG! над проектом "Проклятие Левора".
Если Вы вполне владеете этой темой - возможно Вы и не найдете здесь для себя ничего нового, но если что-то Вам покажется интересным, мы будем рады.
Если Вы обнаружите какие-либо неточности или Ваше мнение в чем либо расходится с данным материалом, напишите нам на poplar85@yandex.ru - и мы с интересом Вас выслушаем.

С уважением, Lex & LexxL.
... Данное исследование не претендует на полноту - это скорее анализ собственного скудного опыта. Все вновь вводимые термины остаются на совести авторов ...

Для начала все же договоримся о терминах, которые мы будем использовать и которые не присутствуют в Lexicon и Aurora Toolset:

Параметры NPC -- набор переменных, описывающих текущее состояние NPC
Сценарий -- некоторая логически завершенная последовательность действий (Actions)
Сюжет -- некоторый связный набор сценариев
Замкнутый NPC -- NPC, параметры которого изменяются внутри его собственных скриптов.
Открытый NPC -- NPC, параметры которого могут изменяются как внутри его собственных скриптов, так и извне.

Здесь вы сможете скачать вспомогательную утилиту COA 1.30, написанную нами для подробного изучения анимации и составления анимационных роликов.

АНИМАЦИЯ NPC - ВСТУПЛЕНИЕ

Как Вы уже успели заметить, относительно нормально функционирующими персонажами в модах обычно являются хенчмены (спутники игрока).

Прочие сюжетные персонажи как правило имеют только диалоги и лишь изредка выполняют какие-либо действия.

Второстепенные (фоновые) жители локаций либо стоят столбами, либо анимированы при помощи стандартных средств ToolSet: (MOBILE_AMBIENT_ANIMATION, IMMOBILE_AMBIENT_ANIMATION).

Возникло желание не столько "анимировать" персонажи, сколько "оживить" их, т.е. придать их деятельности некоторую видимость осмысленной.

При использовании стандартных скриптов персонажи в основном реагируют друг на друга только когда воюют.

Естественно, появляется желание "отскриптовать" всех NPC, находящихся в локации, так, чтобы их поведение было бы, по крайней мере, поверхностно осмысленным.

Как показала практика, это весьма непросто сделать не перегрузив машину. Даже при использовании стандартных скриптов 30-40 анимированных персонажей в локации могут заметно перегрузить компьютер. Что уж говорить о тех вариантах, когда их поведение хотелось бы сделать нестандартным.

Попробуем выделить основные вопросы, связанные с анимацией персонажей.

АНИМАЦИЯ NPC - ЧАСТЬ I

Все обычно начинается с трех вопросов:

1. ЧЕГО ЖЕ МЫ ХОТИМ? - что же Вы собственно ждете от себя и хотите от NPC.
2. СРЕДСТВА - что и как использовать при написании скриптов.
3. КАК ОРГАНИЗОВАТЬ? - каким образом донести Ваши желания и умения до NPC

1. ЧЕГО ЖЕ МЫ ХОТИМ?

При написании собственного модуля возникает желание анимировать в первую очередь сюжетных персонажей.
Например, Ваш герой приходит в кузницу и видит стоящего столбом кузнеца, который замер в ожидании Игрока - в лучшем случае он пододвинется, если вы попытаетесь пройти сквозь него. Да и при диалоге не сделает ни движения.

Почему бы не заставить его немного пошевелиться? - Пусть он постоянно что-то делает - кует, лазает в ящики с инструментами и серьем, смотрит чертежи, пьет что-нибуть, в конце концов. Да и при разговоре с вами пусть немного пожестикулирует (особенно своим молотом у вас под носом).
Создание подобных сценариев достаточно несложно и они будут содержать последовательность необходимых перемещений от объекта к объекту, проигрывание соответствующих анимации самого NPC и Placeables. Это будет Замкнутый NPC.

Но нам мало! Мы хотим, чтобы он реагировал на наши просьбы. Например если мы попросим его починить броню, он должен осмотреть ее, что-то проворчать и приступить к работе. Т.е. он выполнит это только когда Вы (или NPC) его об этом попросит. Это уже труднее. Тут придется повозиться, так как условия, по которым он выбирает сценарий меняются игроком (или NPC). Такой кузнец уже является Открытым NPC.

ПОЭТОМУ ВАЖНО правильно расставить акценты - что же Вы хотите заставить делать тех или иных Ваших NPC, насколько сложным должно быть их поведение, как они должны (или не должны) взаимодействовать с окружающим миром (в том числе и с прочими NPC).
Расписав для себя это в начале, Вы снимете массу проблем при дальнейшем программировании.

В разделе КАК ОРГАНИЗОВАТЬ? мы попытаемся разобрать модели управления NPC - от самой простой, до достаточно сложной.

2. СРЕДСТВА

2.1 НЕОБХОДИМЫЕ ФУНКЦИИ.

Для построения сценариев Вам в основном понадобятся следующие функции:

...перемещения:

ActionMoveToObject(object,int(mode),float(distance));
...подойти к объекту object, mode=TRUE-бежать/FALSE-идти, distance - дистанция на которую надо подойти в футах.

ActionForceMoveToObject(object,int(mode),float(distance),float(time));

...во что бы то ни было подойти к объекту object, mode=TRUE-бежать/FALSE-идти, distance - дистанция на которую надо подойти в футах, time -время, которое Вы отводите на нормальный подход (если NPC по каким то причинам не укладывается в диапазон времени, происходит Jump к этому самому объекту).

ActionMoveAwayFromObject(int(mode),float(distance));

...отойти от объекта object, mode=TRUE-бежать/FALSE-идти, distance - дистанция на которую надо отойти в футах.

ActionRandomWalk();
- бродить по случайному маршруту

...проигрывания анимации:

ActionPlayAnimation(ANIMATION_*,float(speed),float(time));

Проиграть анимацию ANIMATION_* со скоростью speed и временем проигрывания time
тип Action -> становится в очередь действий (*).

PlayAnimation(ANIMATION_*,float(speed),float(time));

то же самое, что и предыдущая, тип void- > не становится в очередь действий

(*) - очередная сказка BioWare, поскольку в отношениях этой функции с очередью действий больше исключений, чем правил.

 

..."строка текста над головой"
ActionSpeakString(); - строка текста над головой объекта (становится в очередь действий)
SpeakString(); - строка текста над головой объекта (не становится в очередь действий)

...имитация кастования спелла
ActionCastFakeSpellAtObject();
ActionCastFakeSpellAtLocation();

а также...
ActionOpenDoor(); - открыть дверь
ActionCloseDoor(); - закрыть дверь
SetCommandable(); разрещить/запретить модификацию стека акций, иными словами, разрешить/запретить добавление команд в очередь
ClearAllActions(); - очистить очередь команд
ActionWait(x.x); - ожидать x.x сек
ActionDoCommand(); - выполнить команду (Void a не Action) - способ поставить НЕ Action команду в очередь;
AssignCommand();- присвоить команду объекту (любому другому, как частный случай - самому себе)
DelayCommand();- задержать выполнение команды на float секунд
SetLocalInt(Float,String,Object,Location); - задать значение локальной переменной с заданным именем для заданного объекта выбранного типа
GetLocalInt(Float,String,Object,Location); - получить значение локальной переменной с заданным именем для заданного объекта выбранного типа

+ конструкции
if-else
switch-case
+ еще множество всяких функций, которые Вам придется использовать, чтобы разнообразить поведение объекта.

 

2.2 ОЧЕРЕДЬ ДЕЙСТВИЙ.

Основа любого сценария - последовательное выполнение команд.
Все команды типа Action можно поставить в очередь-стэк и в этом случае они будут выполняться друг за другом по протоколу FIFO = FirstInputFirstOutput.
Формирование очереди происходит при запуске скрипта:
например, цепочка команд
...
ActionMoveToObject(object,...);
ActionPlayAnimation(ANIMATION_*,...);
ActionSpeakString("Я делаю это");
...
будет выполняться именно в той последовательности, как она записана

Если же Вы хотите поставить в очередь FUNCTION типа void, это можно сделать через ActionDoCommand(FUNCTION);
например следующие строки равнозначны
ActionSpeakString("Я делаю это");
ActionDoCommand(SpeakString("Я делаю это"));

и та и другая команда будут поставлены в очередь.

Есть и еще один важный момент - в очередь NPC (или любого другого объекта) могут быть добавлены действия для другого объекта (или NPC) Это играет очень большую роль, если у вас NPC взаимодействует с объектом. Простой пример - кузнец-NPC открывает ящик. Т.е. кузнец подходит, протягивает руку (чтобы ящик не сам открылся, а как бы кузнец его открыл), и ящик открывается. Дело в том, что ящик нужно открыть после того, как кузнец протянул руку, т.е. открытие ящика (это проигрывание анимации открытия) должно встать в очередь действий Кузнеца.

ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,0.7,0.5);
ActionDoCommand(AssignCommand(oBox,ActionPlayAnimation(ANIMATION_PLACEABLE_OPEN)));

Конструкция ActionDoCommand(AssignCommand(...)); ставит анимацию открытия ящика (это действие производит САМ ЯЩИК) в очередь Кузнеца. Если это же записать без ActionDoCommand(..), то ящик откроется сразу же, как только будет запущена часть скрипта, содержащая команду открытия ящика. ...пока все....................

 

2.3 АНИМАЦИОННЫЕ КОНСТАНТЫ.

АНИМАЦИОННАЯ КОНСТАНТА
int
Описание действия
ANIMATION_LOOPING_PAUSE
0
NPC стоит
ANIMATION_LOOPING_PAUSE2
1
NPC стоит
ANIMATION_LOOPING_LISTEN
2
NPC слушает
ANIMATION_LOOPING_MEDITATE
3
NPC встает на колени и складывает руки перед собой
ANIMATION_LOOPING_WORSHIP
4
NPC встает на колени и кланяется
ANIMATION_LOOPING_LOOK_FAR
5
NPC прикладывает руку ко лбу, имитируя взгляд в даль
ANIMATION_LOOPING_SIT_CHAIR
6
NPC имитирует сидение на стуле. На самом деле он делает шаг вперед и садится на воздух над тем местом, где секунду назад были его ноги
ANIMATION_LOOPING_SIT_CROSS
7
NPC садится на пол скрестив ноги
ANIMATION_LOOPING_TALK_NORMAL
8
NPC имитирует нормальный разговор
ANIMATION_LOOPING_TALK_PLEADING
9
NPC умоляюще протягивает руки.
ANIMATION_LOOPING_TALK_FORCEFUL
10
NPC убеждающе машет руками.
ANIMATION_LOOPING_TALK_LAUGHING
11
NPC смеется.
ANIMATION_LOOPING_GET_LOW
12
NPC приседает на корточки и копошит руками на уровне пола.
ANIMATION_LOOPING_GET_MID
13
NPC протягивает руку, как-бы поворачивая дверную ручку.
ANIMATION_LOOPING_PAUSE_TIRED
14
NPC потягивает спину, немного заломив за нее руки
ANIMATION_LOOPING_PAUSE_DRUNK
15
NPC пьяно покачивается.
ANIMATION_LOOPING_DEAD_FRONT
16
NPC сначала падает на колени, а потом распластывается по полу.
ANIMATION_FIREFORGET_HEAD_TURN_LEFT
100
NPC поворачивает голову влево.
ANIMATION_FIREFORGET_HEAD_TURN_RIGHT
101
NPC поворачивает голову вправо.
ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD
102
NPC чешет голову.
ANIMATION_FIREFORGET_PAUSE_BORED
103
NPC слегка покачивается, как-бы под тяжестью ноши или от усталости..
ANIMATION_FIREFORGET_SALUTE
104
NPC отдает честь.
ANIMATION_FIREFORGET_BOW
105
NPC кланяется в пояс.
ANIMATION_FIREFORGET_STEAL
106
NPC имитирует воровские движения.
ANIMATION_FIREFORGET_GREETING
107
NPC приветственно машет рукой.
ANIMATION_FIREFORGET_TAUNT
108
NPC насмехается.
ANIMATION_FIREFORGET_VICTORY1
109
NPC радуется.
ANIMATION_FIREFORGET_VICTORY2
110
NPC радуется.
ANIMATION_FIREFORGET_VICTORY3
111
NPC радуется.
ANIMATION_FIREFORGET_READ
112
NPC достает сивток и читает его.
ANIMATION_FIREFORGET_DRINK
113
NPC достает бутылку и пьет.
unknown
114
unknown
unknown
115
unknown
unknown
116
unknown
ANIMATION_PLACEABLE_ACTIVATE
200
Анимация активации placeable (пламя, канделябр, фонарный столб, факел, итд.)
ANIMATION_PLACEABLE_DEACTIVATE
201
Анимация деактивации placeable (пламя, канделябр, фонарный столб, факел, итд.)
ANIMATION_PLACEABLE_OPEN
202
Анимация открывания placeable (сундук, ящик, шкаф с дверями, итд.)
ANIMATION_PLACEABLE_CLOSE
203
Анимация закрывания placeable (сундук, ящик, шкаф с дверями, итд.)

Немного подробнее о самих базовых анимационных роликах, которые "скрываются" за анимационными константами.
В таблице и так уже все переведено на русский, поэтому остановимся на различиях FIREFORGET и LOOPING.

При проигрывании анимации движок Aurora оперирует следующими параметрами:

где нам доступны первые три параметра - ANIMATION(собственно анимационная константа), PLAYBACK_SPEED - скорость проигрывания, LENGTH - время проигрывания, а DESIRED_LENGTH (желательное время) определено движком и недоступно.

Вообще стоит различать _FIREFORGET_ и _LOOPING_ анимации.

 

2.3.1 АНИМАЦИОННЫЕ КОНСТАНТЫ ТИПА _FIREFORGET_.

_FIREFORGET_ - это одиночные анимации, т.е. NPC выполняет последовательность движениий один раз, например ANIMATION_FIREFORGET_STEAL заставит NPC сделать последовательность движений "воровства" ТОЛЬКО один раз.


диаг.FIREFORGET 1 T_BEG - стартовый фрагмент, T_INTERNAL - внутреняя часть, T_END - завершающий фрагмент

Примерная временная схема анимации типа FIREFORGET при скорости = 1.0f и общем времени LENGTH <= DESIRED_LENGTH.
Единственное, чем она может отличаться от анимации по умолчанию - если LENGTH много меньше DESIRED_LENGTH - основной ролик INTERNAL будет обрезан, а начальный и конечный ролики - ускорены. Aurora как бы пытается втиснуть анимацию в отведенное время.

ВНИМАНИЕ!!!

Для констант _FIREFORGET_* без вспомогательного предмета (свиток, бутылка) регулируется ТОЛЬКО скорость PLAYBACK SPEED, при этом, какое бы Вы не выставили время LENGTH, время проигрывания определено движком и равно DESIRED_LENGTH.
Например:
...
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING,1.0f,10.0f);
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD,1.0f,10.0f);
...
ничем не отличаются при проигрывании от:
...
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING,1.0f);
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD,1.0f);
...

Причем, для большинства _FIREFORGET_* эффект изменения скорости PLAYBACK SPEED заметен при увеличении > 1.0f


Для констант _FIREFORGET_* с вспомогательным предметом (свиток, бутылка) время регулируется, НО! это время не влияет на реальное время проигрывания анимации, время LENGTH как бы "резервируется" движком для этой анимациию. Т.е следующая за ней в очереди анимация начнется по истечении LENGTH. Например:
...
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0f,10.0f);
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD,1.0f);
...
заметно отличаются при проигрывании от:
...
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0f);
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD,1.0f);
...
тем, что после анимации питья NPC еще (10.0f - DESIRED_LENGTH(для _DRINK)) сек просто стоит.

 

2.3.1 ОСОБЕННОСТИ _FIREFORGET_DRINK и _FIREFORGET_READ.

Рассмотрим случай, когда Вы указали не только скорость, но и время при проигрывании ANIMATION_FIREFORGET_DRINK или ANIMATION_FIREFORGET_READ .
При этом если скорость равна 1.0f, то все будет проигрываться нормально практически при любом указанном времени. Забавность в том, что время визуализации T VIS вспомогательного объекта (бутылка, свиток) рассчитана для нормальной скорости 1.0f


диаг.FIREFORGET 2

Примерная временная схема анимации типа _FIREFORGET* при скорости PLAYBACK_SPEED < 1.0f и общем времени LENGTH > DESIRED_LENGTH
Иначе говоря, бутылка или свиток могут остаться в руке NPC еще некоторое время при скорости < 1.0f Например, при
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,0.1f,15.0f);
NPC c нормальной скоростью глотнет из бутылки (но никак НЕ медленно!) и опустит руку с бутылкой вниз. Бутылка исчезнет из руки только по окончанию 15.0f .


диаг.FIREFORGET 3

Примерная временная схема анимации типа _FIREFORGET_* при скорости PLAYBACK_SPEED > 1.0f и общем времени LENGTH > DESIRED_LENGTH
Иначе говоря, бутылка или свиток могут испариться из руки NPC много раньше, чем должны были бы при скорости = 1.0f. Например, NPC может и не успеть заглотнуть чего-либо из бутылки, поскольку она норовит исчезнуть из руки в самый неподходящий момент при скорости = 3.0f .
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,3.0f,15.0f);

Примерная зависимость времени визуализации дополнительного объекта:
T_VIS = T_NORM / PLAYBACK_SPEED

2.3.2 АНИМАЦИОННЫЕ КОНСТАНТЫ ТИПА _LOOPING_.

_LOOPING_ - многократное повторение последовательности движений = начальный ролик + (основной ролик)*N + конечный ролик.
ANIMATION_LOOPING_GET_LOW заставляет NPC присесть на корточки и указанное время (на самом деле равное LENGTH - T_BEG - T_END) шевелить руками.
Причем скорость и время вполне наглядно меняют характер проигрывания анимации.


диаг.LOOPING 1 Примерная временная схема анимации типа LOOPING

 

НАЛОЖЕНИЕ АНИМАЦИЙ ПРИ ПОМОЩИ ОЧЕРЕДИ ДЕЙСТВИЙ.
( только для ActionPlayAnimation(); )

Накладывать можно только _FIREFORGET_* на _LOOPING_*.
Рассмотрим простой пример:
...
ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE,1.0,10.0f);
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,7.0);
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING,1.0,6.0);
ActionPlayAnimation(ANIMATION_FIREFORGET_READ,1.0,8.0);
...


Реально происходит следующее:

1. Проигрывается начальный ролик ANIMATION_LOOPING_MEDITATE длиной T_BEG

2. Проигрывается основной ролик ANIMATION_LOOPING_MEDITATE длиной LENGTH = 10.0 сек., НО завершающий ролик НЕ проигрывается, т.е. NPC продолжает "медитировать" !!!

3. Проигрывается ANIMATION_FIREFORGET_DRINK, причем реально _DRINK проигрывается DESIRED_LENGTHсек, а оставшееся время (7.0 - DESIRED_LENGTH) сек. NPC продолжает "медитировать"!!!

4. Проигрывается ANIMATION_FIREFORGET_GREETING, причем реально _GREETING проигрывается DESIRED_LENGTH сек, а не 6.0. (см. FIREFORGET без предмета)

5. Проигрывается ANIMATION_FIREFORGET_READ, по тем же правилам, что и DRINK: т.е. NPC снова "медитирует" после чтения (8.0 - DESIRED_LENGTH)сек.

6. Проигрывается завершающий ролик _LOOPING длиной T_END. ТОЛЬКО СЕЙЧАС NPC встанет.

НЕБОЛЬШОЕ ДОПОЛНЕНИЕ:
Если в каком-либо из ANIMATION_FIREFORGET_* будут задействованы ноги NPC, то он обязательно встанет для выполнения этого действия, а по завершению СНОВА СЯДЕТ.

 

2.4. АНИМАЦИЯ NPC В ДИАЛОГЕ:

Самое простое, что Вы можете сделать средствами ToolSet-a, вообще ничего не программируя, это заставить NPC хотя бы поприветствовать Вас в начале диалога или выполнить какую-нибудь другую стандартную анимацию.

В диалоге на любой строке Вы можете воспользоваться следующими возможностями:
в закладке OtherActions - поля Play Animation (проиграть анимацию) и Play Sound (проиграть звук)
(см. рис.1 - 1,2,3)


рис.1

Продолжение следует...

Вторая часть статьи >>>

Внимание!
Данный текст является интеллектуальной собственностью. Любое цитирование материалов допустимо только со ссылкой на наш сайт!


Design by DBColl - 2003 -
Hosted by uCoz