среда, 21 ноября 2012 г.

Тутор по созданию многостраничного документа

Тутор по созданию многостраничного документа, а также кое-чего ещё.

Собственно говоря, приступим к делу. С чего же началось написание этого тутора. В принципе, я не планировал делать книги «на несколько страниц», ибо не было серьёзной причины и наличия в моде книг, развернутых на несколько страниц текста. Но, случайно изучая предметы готики, наткнулся на такую вещь, как «Факел», но не тот факел, что обычно использует ГГ, а тот, что использовали артисты на помосте, выдувая пламя изо рта.
Что же в нем такого особенного? А то, что имеет он 5 состояний (с 0-го по 4-й), ибо в скриптах – в расписании этих артистов идёт использование этого факела до 5-го состояния (хотя, достаточно и до 4-го):

"ZS_Spit_Fire" (кликните для показа/скрытия)

Код:
func int ZS_Spit_Fire_loop()

{

 if(Wld_IsFPAvailable(self,"STAND"))

 {

  AI_GotoFP(self,"STAND");

  AI_AlignToFP(self);

 }

 else

 {

  AI_AlignToWP(self);

 };

 if(Npc_GetStateTime(self) > 10)

 {

  AI_UseItemToState(self,ItLsTorchFirespit,5);

  AI_UseItemToState(self,ItLsTorchFirespit,0);

  Npc_SetStateTime(self,0);

 };

 return LOOP_CONTINUE;

};

Об этом свидетельствует и mds-скрипт анимации взаимодействия с этим факелом:

"FIRESPIT" (кликните для показа/скрытия)
// Feuerspucken
ani ("t_FIRESPIT_Stand_2_S0" 1 "s_FIRESPIT_S0" 0.2 0.0 M. "Hum_FireSpit_M01.asc" F 0 69 FPS:10)
{
*eventTag (1 "DEF_INSERT_ITEM" "ZS_RIGHTHAND")
*eventTag (1 "DEF_CREATE_ITEM" "ZS_LEFTHAND" "ItLsTorch")
}
ani ("s_FIRESPIT_S0" 1 "s_FIRESPIT_S0" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 70 70)
ani ("t_FIRESPIT_S0_2_Stand" 1 "" 0.0 0.1 M. "Hum_FireSpit_M01.asc" R 0 69 FPS:10)
{
*eventTag (1 "DEF_REMOVE_ITEM")
}
ani ("t_FIRESPIT_S0_2_S1" 1 "s_FIRESPIT_S1" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 71 126 FPS:10)
{
*eventSFX (80 "SPIT_FIRE" R:1000 EMPTY_SLOT)
*eventPFX (83 1 "FLAMETHROWER" "BIP01 HEAD" ATTACH)
*eventPFXStop (100 1)
}
ani ("s_FIRESPIT_S1" 1 "s_FIRESPIT_S1" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 127 127)
ani ("t_FIRESPIT_S1_2_S2" 1 "s_FIRESPIT_S2" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 128 160 FPS:10)
{
*eventSFX (139 "SPIT_FIRE" R:1000 EMPTY_SLOT)
*eventPFX (140 1 "FLAMETHROWER" "BIP01 HEAD" ATTACH)
*eventPFXStop (155 1)
}
ani ("s_FIRESPIT_S2" 1 "s_FIRESPIT_S2" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 161 161)
ani ("t_FIRESPIT_S2_2_S3" 1 "s_FIRESPIT_S3" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 162 195 FPS:10)
{
*eventSFX (170 "SPIT_FIRE" R:1000 EMPTY_SLOT)
*eventPFX (171 1 "FLAMETHROWER" "BIP01 HEAD" ATTACH)
*eventPFXStop (180 1)
}
ani ("s_FIRESPIT_S3" 1 "s_FIRESPIT_S3" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 196 196)
ani ("t_FIRESPIT_S3_2_S4" 1 "s_FIRESPIT_S4" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 197 229 FPS:10)
{
*eventSFX (204 "SPIT_FIRE" R:1000 EMPTY_SLOT)
*eventPFX (205 1 "FLAMETHROWER" "BIP01 HEAD" ATTACH)
*eventPFXStop (220 1)
}
ani ("s_FIRESPIT_S4" 1 "s_FIRESPIT_S4" 0.0 0.0 M. "Hum_FireSpit_M01.asc" F 230 230)
ani ("t_FIRESPIT_S4_2_Stand" 1 "" 0.0 0.1 M. "Hum_FireSpit_M01.asc" F 230 240)
{
*eventTag (239 "DEF_REMOVE_ITEM")
}

Так, а теперь остановимся на манипулировании сразу более-менее сложными вещами и перейдём к изучению по порядку. Что же мы имеем вначале:
- идея, создать документ с возможностью просмотра более 1 страницы.
Как у нас выглядит стандартный листок бумаги, который мы встречаем в игре. Пример, список трав Константино:

"Список" (кликните для показа/скрытия)

Код:
instance ItWr_Kraeuterliste(C_Item)

{

 name = "Список";

 mainflag = ITEM_KAT_DOCS;

 flags = ITEM_MISSION;

 value = 0;

 visual = "ItWr_Scroll_01.3DS";

 material = MAT_LEATHER;

 on_state[0] = Use_Kraeuterliste;

 scemeName = "MAP";

 description = "Список трав Константино.";

};

func void Use_Kraeuterliste()

{

 var int nDocID;

 nDocID = Doc_Create();

 Doc_SetPages(nDocID,1);

 Doc_SetPage(nDocID,0,"letters.TGA",0);

 Doc_SetFont(nDocID,-1,FONT_Book);

 Doc_SetMargins(nDocID,-1,50,50,50,50,1);

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Алхимические травы");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Лечебная трава");

 Doc_PrintLine(nDocID,0,"Лечебное растение");

 Doc_PrintLine(nDocID,0,"Лечебный корень");

 Doc_PrintLine(nDocID,0,"Огненная крапива");

 Doc_PrintLine(nDocID,0,"Огненная трава");

 Doc_PrintLine(nDocID,0,"Огненный корень");

 Doc_PrintLine(nDocID,0,"Гоблинские ягоды");

 Doc_PrintLine(nDocID,0,"Драконий корень");

 Doc_PrintLine(nDocID,0,"Снеппер-трава");

 Doc_PrintLine(nDocID,0,"Луговой горец");

 Doc_PrintLine(nDocID,0,"Царский щавель");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLines(nDocID,0,"Примечание: Пусть кандидат... ");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"С уважением, Зурис.");

 Doc_SetMargins(nDocID,-1,200,50,50,50,1);

 Doc_Show(nDocID);

};

Обратите внимание на выделенные фрагменты:
Scemename = "MAP";
On_state[0] = Use_Kraeuterliste;
Что же такое scemename? Это название набора анимаций для взаимодействия с предметом или объектом мира.
В частности для нашего документа, что это? Открываем mds-скрипт анимаций человека humans.mds и ищем что-то содержащее слово MAP… ага, вот:

"MAP" (кликните для показа/скрытия)
// Schriftrolle lesen
ani ("t_MAP_Stand_2_S0" 1 "s_MAP_S0" 0.1 0.0 M. "Hum_MapRead_M01.asc" F 1 24)
{
*eventTag (18 "DEF_INSERT_ITEM" "ZS_RIGHTHAND")
*eventSFX (18 "Scroll_Unfold")
}
ani ("s_MAP_S0" 1 "s_MAP_S0" 0.0 0.0 M. "Hum_MapRead_M01.asc" F 25 25)
ani ("t_MAP_S0_2_Stand" 1 "" 0.0 0.1 M. "Hum_MapRead_M01.asc" R 1 24)
{
*eventTag (18 "DEF_REMOVE_ITEM")
*eventSFX (18 "Map_Unfold" R:1000)
}

Я думаю за разъяснением этих строк вам можно обратиться к туторам Vam’a и Kerrax’а.
Вот: Динамическая модель (3DS Max, анимация и меши)
И вот: Динамическая модель: описание eventTag'ов
От себя поясню. Видите, что упомянутое в скрипте слово «MAP» фигурирует здесь в таком вот виде:
T_MAP_Stand_2_S0 , S_MAP_S0, T_MAP_S0_2_Stand.
Что же это:
  • T_MAP_Stand_2_S0 – это переход из состояния «Стою» в нулевое состояние взаимодействия с предметом, (в нашем случае – список), и какая при этом проигрывается анимация.
  • S_MAP_S0 – это пребывание человека в нулевом состоянии, и какая при этом проигрывается анимация.
  • T_MAP_S0_2_Stand – это переход из нулевого состояния в состояние «Стою».
Как мы видим, и это подтверждается на деле в игре, при открытии инвентаря и нажатия кнопки «Действие» на предмете «Список трав константино», ГГ производит следующие действия:
  1. Герою в руку помещается свиток, и он воспроизводит анимацию перехода в режим чтения.
  2. ГГ стоит и читает листок – всего лишь 1 кадр;
  3. ГГ убирает предмет-список в инвентарь – перед этим на экран выводится текст записки.
Всё очень просто, но к чему я всё это объясняю, где эта многостраничность. Идём далее, рассмотрим второй выделенный жирным фрагмент:
On_state[0] = Use_Kraeuterliste;
Поясняю – применительно к предметам инвентаря, это означает ту функцию, которая будет вызвана при выходе, подчеркиваю при выходе из 0-го состояния (если on_state[1] – то функция сработает при выходе из 1-го состояния). В нашем случае, это вывод текста записки на экран.
Чтож истина где-то рядом, но ещё не близко.
Продолжим рассуждение, которое я буду сопровождать пояснением. Значит, делая вывод и всего вышеизложенного, чтобы вывести, например, текст второй записки на экран, нам надо следующее... Нам надо создать функцию, которая выведет на экран текст второй записки. Так, если первая записка вывелась на экран при выходе из 0-го состояния, значит, текст второй записки можно вывести на экран при выходе из 1-го состояния. Всё логично.
Теперь подключаем мозг и учимся думать и применять эмпирический путь для поиска истины.
По аналогии создадим функцию: On_state[1] = Use_ Kraeuterliste_1;

"On_state[1]" (кликните для показа/скрытия)

Код:
func void Use_Kraeuterliste_1()

{

 var int nDocID;

 nDocID = Doc_Create();

 Doc_SetPages(nDocID,1);

 Doc_SetPage(nDocID,0,"letters.TGA",0);

 Doc_SetFont(nDocID,-1,FONT_Book);

 Doc_SetMargins(nDocID,-1,50,50,50,50,1);

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Алхимические травы");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLines(nDocID,0,"Примечание: Пусть кандидат... ");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"С уважением, Зурис.");

 Doc_SetMargins(nDocID,-1,200,50,50,50,1);

 Doc_Show(nDocID);

};

Но не тут то было, даже если мы произведем все нижеуказанные действия, касающиеся анимации, это не выведет на экран текст второй записки, он появится позади этой запиской, ибо в это время на экран выведен первый документ. Следовательно, чтобы в этот момент на экране в той же записке появился другой текст, нам надо заменить текущий текст на тот, что во второй записке. Тааак-с. Давайте исследуем первую функцию, а именно начало и конец:
var int nDocID;
nDocID = Doc_Create();

Doc_Show(nDocID);
Это тот набор функций, отвечающий за создание документа и вывода его на экран (всё можно посмотреть в туторе Vam’а касательно скриптов)
Следовательно, опять подключаем мозг… Делаем вывод, нам не нужно создавать второй документ, а надо изменить первосозданный. Делаем следующее:

"Список" - измененный (кликните для показа/скрытия)

Код:
instance ItWr_Kraeuterliste(C_Item)

{

 name = "Список";

 mainflag = ITEM_KAT_DOCS;

 flags = ITEM_MISSION;

 value = 0;

 visual = "ItWr_Scroll_01.3DS";

 material = MAT_LEATHER;

 on_state[0] = Use_Kraeuterliste;

 on_state[1] = Use_Kraeuterliste_1;

 scemeName = "MAP";

 description = "Список трав Константино.";

};

var int nDocID;

func void Use_Kraeuterliste()

{

 nDocID = Doc_Create();

 Doc_SetPages(nDocID,1);

 Doc_SetPage(nDocID,0,"letters.TGA",0);

 Doc_SetFont(nDocID,-1,FONT_Book);

 Doc_SetMargins(nDocID,-1,50,50,50,50,1);

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Алхимические травы");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Лечебная трава");

 Doc_PrintLine(nDocID,0,"Лечебное растение");

 Doc_PrintLine(nDocID,0,"Лечебный корень");

 Doc_PrintLine(nDocID,0,"Огненная крапива");

 Doc_PrintLine(nDocID,0,"Огненная трава");

 Doc_PrintLine(nDocID,0,"Огненный корень");

 Doc_PrintLine(nDocID,0,"Гоблинские ягоды");

 Doc_PrintLine(nDocID,0,"Драконий корень");

 Doc_PrintLine(nDocID,0,"Снеппер-трава");

 Doc_PrintLine(nDocID,0,"Луговой горец");

 Doc_PrintLine(nDocID,0,"Царский щавель");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLines(nDocID,0,"Примечание: Пусть кандидат... ");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"С уважением, Зурис.");

 Doc_SetMargins(nDocID,-1,200,50,50,50,1);

 Doc_Show(nDocID);

};

func void Use_Kraeuterliste_1()

{

 Doc_SetPages(nDocID,1);

 Doc_SetPage(nDocID,0,"letters.TGA",0);

 Doc_SetFont(nDocID,-1,FONT_Book);

 Doc_SetMargins(nDocID,-1,50,50,50,50,1);

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"Алхимические травы");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLines(nDocID,0,"Примечание: Пусть кандидат... ");

 Doc_PrintLine(nDocID,0,"");

 Doc_PrintLine(nDocID,0,"С уважением, Зурис.");

 Doc_SetMargins(nDocID,-1,200,50,50,50,1);

};

Чтоб не натыкаться на грабли, сразу внесем дополнения в файл humans.mds . Нам же нужны переходы в 1-е состояние.

"MAP - дополненный" (кликните для показа/скрытия)
ani ("t_MAP_Stand_2_S0" 1 "s_MAP_S0" 0.1 0.0 M. "Hum_MapRead_M01.asc" F 1 24)
{
*eventTag (18 "DEF_INSERT_ITEM" "ZS_RIGHTHAND")
*eventSFX (18 "Scroll_Unfold")
}
ani ("s_MAP_S0" 1 "s_MAP_S0" 0.0 0.0 M. "Hum_MapRead_M01.asc" F 25 25)
ani ("t_MAP_S0_2_Stand" 1 "" 0.0 0.1 M. "Hum_MapRead_M01.asc" R 1 24)
{
*eventTag (18 "DEF_REMOVE_ITEM")
*eventSFX (18 "Map_Unfold" R:1000)
}
ani ("t_MAP_S0_2_S1" 1 "s_MAP_S1" 0.1 0.0 M. "Hum_MapRead_M01.asc" F 1 24)
{
*eventSFX (18 "Scroll_Unfold")
}
ani ("s_MAP_S1" 1 "s_MAP_S1" 0.0 0.0 M. "Hum_MapRead_M01.asc" F 25 25)
ani ("t_MAP_S1_2_S0" 1 "" 0.0 0.1 M. "Hum_MapRead_M01.asc" R 1 24)
{
*eventSFX (18 "Map_Unfold" R:1000)
}
ani ("t_MAP_S1_2_Stand" 1 "" 0.0 0.1 M. "Hum_MapRead_M01.asc" R 1 24)
{
*eventTag (18 "DEF_REMOVE_ITEM")
*eventSFX (18 "Map_Unfold" R:1000)
}

Что мы теперь имеем: мы имеем функции, которые выведут на экран две записки, мы имеем анимации для перехода в 0-е и 1-е состояния и выхода из них, следовательно переносим это всё дело в файлы готы. Как?
Учимся на worldofplayers.ru/moddb
Если вы всё скомпилировали и вставили в игру, то пробуем:
Открываем инвентарь, нажимаем на свиток… что ж вывелся текст и всё? А где же вторая записка?
Поясняю. при простом нажатии кнопки действия на предмете инвентаря – это касается вещей, которые не экипируются и у которых имеется взаимодействие: еда, документы, - предмет перводится в 0-е состояние и обратно. Чтобы перевести предмет в 1-е состояние, надо сделать следующее:
  • А) Зажать (! Не нажать !) кнопку «Дествие» на предмете (переведётся в 0-е состояние) и нажать кнопку «Вперед» (переведётся в 1-е состояние) – выведется первая записка на экран.
  • Б) Просто отпускаем кнопку «Действие» (перейдёт из 1-го состояния в состояние «Стою») – выведется на экран текст второй записки.

P.S.
К сожалению, этот способ имеет свое ограничение. Массив on_state имеет максимальный размер – 4 элемента: с 0-го по 3-й; то есть можно использовать переходы вплоть до 3-го состояния. Но, честно говоря, книжные тома, развернутые на бОльшее пространство, читать вряд ли кто-то будет. Да, и вариант листать "вперёд-назад" здесь не канает. Не скажу, что это единственный способ получения многостраничных документов (есть варианты как на базе оригинального набора инструментов Готики, так и с помощью G2Ext), но как вариант – пойдёт.
А ещё, самое главное, по аналогии можно реализовать какие-нибудь интересные вещи, типа «Употреблять еду за 4 укуса» Дерзайте!
За сим откланиваюсь.

18.07.2011, 14:48

0 коммент.:

Отправить комментарий

Open Panel

Blogroll

Вверх

Вниз