Глава 19. Анимации
В прошлых Главах мы научились выводить картинки и текст на экран. Все бы хорошо, но статичные картинки и текст – это скучно. Они просто стоят на месте, пока игрок не перейдет на следующий шаг, убрав их с экрана
Было бы интересней, например, добавить анимаций для какой-никакой динамики. Например, текст, который проносится через весь экран. Благо, в игре для этого есть отдельный тег <lerp/>, он же Линейная Интерполяция
Шаг 1. Основа анимации <lerp/>
Тег <lerp/> помещается внутрь image/label и заставляет игру плавно менять их параметры от параметров А к парметрам Б. Например, двигать стрелку из точки А в точку Б и постепенно наращивать ее размер по мере продвижения.
<lerp loop="true" reverse="true" speed="2" start="0" wait="0.5"><!-- Тут параметры А и Б --></lerp>
Где:
loop(true/false) – Зациклить анимацию (да/нет). Если стоитfalse, то анимация проиграется один раз и остановитсяreverse(true/false) – Проиграть ли анимацию туда-обратно (да/нет). Пришел из А в Б, а потом вернлся обратно в Аspeed– Скорость анимацииstart– Задержка в секундах перед первым стартом анимацииwait– Пауза в секундах в крайних точках. Условно, когда объект дошел до точки Б, он подождет это время перед тем, как вернуться обратно в А
Анимацию можно играть как на экране вне диалога, так и во время самого диалога. В диалоговом окне базовой точкой по-умолчанию является левый верхний угол диалогового окна

Шаг 2. Контейнеры <from> и <to>
Чтобы игра вообще понимала, что мы собрались анимировать, внутрь <lerp/> нужно поместить два контейнера:
<from>– Откуда начинаем (точка А). Базовое расположение объекта<to>– Куда идем (точка Б)
В этих контейнерах мы указываем теги форматирования, при этом теги, которые будут указаны вне этих контейнеров будут игнорироваться. Исключение: тег <persist/> и текст у <label/>
Теги форматирования:
<persist/>– Остававить на экране после закрытия Диалога.<rotation/>– Поворот в градусах. Против часовой стрелки – положительные координаты (90, 30…), По часовой – отрицательные (-90, -180…)<position/>– Позиция по XY, куда поставитьimage/label. Отсчитывается по левому верхнему краю блокаimage/label<scale/>– Определяет растягивание по XY<color/>– Цвет. Можно в форматеr="0.0"g="0.0"b="0.0"a="0.0"(от 0.0 до 1.0) илиname="ИмяЦвета"(см. GigaList)
Важно! Обязательно указывайте в from/to три тега: <rotation/>, <scale/> и <position/>. Даже если они никак не меняются. Если не указать хотя бы один из них, то анимация просто не будет работать
Шаг 3. Анимация в Квесте
Теперь добавим анимации в подпись с Целью в шаге spider_hunt_step в MyQuest.xml – «Основной Квест», который мы изменяли в прошлой Главе
Допустим, стрелки будут двигаться вперед-назад. По мере приблежения к тексту, размер стрелки будет увеличиваться, по мере отдаления – уменьшаться
Было:
<step id="spider_hunt_step">. . .<!-- Стрелка слева --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><scale x="50" y="50"/><position x="-235" y="-315"/><rotation angle="90"/><persist /></image><!-- Стрелка справа --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><scale x="50" y="50"/><position x="190" y="-315"/><rotation angle="-90"/><persist /></image>. . .</step>
Стало:
При приближении к тексту размер стрелки постепенно увеличивается (scale с 40 до 50), перемещение для левой стрелки с x="-265” до x="-235", для правой – с x="230" до x="190". Задрежка между фазами анимации (wait) = 0.2 секунды. Скорость = 2
<step id="spider_hunt_step">. . .<!-- Стрелка слева --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><persist /><lerp loop="true" reverse="true" speed="2" start="0" wait="0.2"><from><scale x="40" y="40"/><position x="-265" y="-315"/><rotation angle="90"/></from><to><scale x="50" y="50"/><position x="-235" y="-315"/><rotation angle="90"/></to></lerp></image><!-- Стрелка справа --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><persist /><lerp loop="true" reverse="true" speed="2" start="0" wait="0.2"><from><scale x="40" y="40"/><position x="230" y="-315"/><rotation angle="-90"/></from><to><scale x="50" y="50"/><position x="190" y="-315"/><rotation angle="-90"/></to></lerp></image><!-- Подложка -->. . .</step>
Шаг 4. Проносящийся текст
Теперь для WarriorFirstQuest.xml – «Маленькая Проблемка» добавим проносящийся через весь экран текст: «АРАГОГ УЖЕ ЗДЕСЬ!»
<step id="battle_aragog_step"><info><label>Убей [FF0000]Арагога[-]</label><kill enemy="GiantSpider" count="1"/></info><label location="HUD">[FF0000]АРАГОГ УЖЕ ЗДЕСЬ![-]<persist /><lerp loop="false" reverse="false" speed="2" start="0" wait="0"><from><scale x="50" y="50"/><position x="-1300" y="0"/><rotation angle="0"/></from><to><scale x="50" y="50"/><position x="1300" y="0"/><rotation angle="0"/></to></lerp></label>. . .</step>
И тут же образовалась проблема: <label/> с <persist/> конфликтует с нашим самозацикленной авто-опцией которая через <existmobs/> с <inverse/> проверяет наличие Арагога. Конфликт происходит из-за того, что Арагог спавниться не мгновенно, а с небольшой задержкой. Соответственно, наша авто-опция начинает пытаться перезапустить наш шаг, чтобы Арагог вновь появился. Проблема в том, что перезапуск авто-опцией своего же шага приводит к уничтожению всех <label/> с <persist/>. Чтобы этого избежать, добавим в нашу авто-опцию небольшой таймер в 3 секунды, который сделает задержку перед началом проверок наличия Арагога в мире у авто-опции.
Получается так: Игрок переходит на battle_aragog_step, игра начинает спавнить Арагога, в это же время у авто-опции <existmobs/> с <inverse/> идет задержка в 3 секунды перед началом работы. За эти 3 секунды задержки успевает заспавнится Арагог. Всё, проблема решена
И на будущее: Если делаете шаги, которые перезапускают сами себя, то обязательно добавьте им небольшую задержку
Задержка добавляется элементарно. Для это в саму авто-опцию добавляем атрибут start="ЧислоСекунд", а внутрь авто-опции в самый верх вкладываем тег <delay sec="ЧислоСекунд"/>. При этом важно, чтобы число секунд у start совпадало с delay, иначе отсчет пойдет по минимальному из этих двух значений. Обязательно используйте их в тандеме, один без другого работать не будет.
<step id="battle_aragog_step">. . .<option auto="battle_aragog_step" start="3"><delay sec="3" /><existmobs name="GiantSpider"><inverse/></existmobs></option></step>
Проверяем:
Если вы считаете, что текст прллетает слишком быстро, то можете или умеьшить расстояние между точками А и Б, или уменьшить скорость. Я оставлю текущую
Задания на закрепление
- Сделайтетак, чтобы после убийства Арагога снизу всплывал текст: «Арагог повержен. \nВернись к Аарону и расскжаи об этом». Оформите это на свой вкус. Всплывать текст может хоть откуда: сверху падать, снизу выезжать и т.д.
- Сделайте оформление с label и image, как на скриншоте ниже. При этом спрайт руки должен покачиваться на месте влево-вправо

Заключение
В этой Главе мы научились работать с анимациями. Насамом деле, мы разобрали лишь малую часть того,что можно делать с помощью этих анимаций. В сочетании с текстур-паками, анимации заиграют еще более яркими красками. По сути, всё ограничивается лишь вашей фантазией
Итоговый код Главы
MyQuest.xml – «Основной Квест»
<?xml version="1.0" encoding="UTF-8"?><quest npc="Wizard" title="Основной Квест"><step id="#start"><intro>Привет, [FFAA00]как дела[-]?</intro><desc>[FFAA00]Рад[-] тебя снова видеть! Как дела?</desc><option to="bad_mood_step" text="[FF0000]Плохо[-]"/><option to="good_mood_step" text="[00FF00]Норм[-]"><journal/></option></step><!-- Хорошее настроение --><step id="good_mood_step"><intro>Это хорошо, что хорошо.У меня есть одно плёвое заданьеце для тебя.Готов ли его выполнить?</intro><desc>Снова ты?\nМоё предложение всё ещё актуально.\nГотов приступить?</desc><option to="spider_hunt_step" text="Да"><journal/></option><option to="#start" text="Назад"/></step><step id="spider_hunt_step"><desc>Хорошо. Попробуй убить парочку пауков для разминки. Также принеси мне яблочко, а то я уж больно проголодался</desc><row><label><scalex="310"y="3"/></label><image atlas="GUI" sprite="RadarArrowGreen"><scale x="50" y="50"/></image></row><row><label><scalex="300"y="3"/></label><item name="Apple" count="1"/></row><!-- Стрелка слева --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><persist /><lerp loop="true" reverse="true" speed="2" start="0" wait="0.2"><from><scale x="40" y="40"/><position x="-265" y="-315"/><rotation angle="90"/></from><to><scale x="50" y="50"/><position x="-235" y="-315"/><rotation angle="90"/></to></lerp></image><!-- Стрелка справа --><image atlas="GUI" sprite="RadarArrowGreen" location="HUD"><persist /><lerp loop="true" reverse="true" speed="2" start="0" wait="0.2"><from><scale x="40" y="40"/><position x="230" y="-315"/><rotation angle="-90"/></from><to><scale x="50" y="50"/><position x="190" y="-315"/><rotation angle="-90"/></to></lerp></image><!-- Подложка --><image atlas="GUI" sprite="Button400x70" location="HUD"><scale x="385" y="50"/><position x="-25" y="-315"/><persist /></image><label location="HUD"><persist/><position x="-200" y="-300"/><scale x="24" y="24"/>[FFAA00]Цель:[-] 2 паука и яблоко</label><option to="#close" text="Ок"/><rewards type="Award"><reward exp="300"/><row><reward item="Axe" count="5"/><reward recipe="Sunshard Paula" type="Table"/></row></rewards><info><kill enemy="Spider" count="2"/><collect item="Apple" count="1"/><label>Яблоко: </label><item name="Apple" count="1"/></info><option auto="after_hunt_step"><kill enemy="Spider" count="2"/><collect item="Apple" count="1"/><notify>[00FF00]Задание выполнено![-] \nВернись к Волшебнику!</notify></option></step><step id="after_hunt_step"><desc>Отлично! Ты справился</desc><rewards type="Display"><reward exp="300"/><row><reward item="Axe" count="5"/><reward recipe="Sunshard Paula" type="Table"/></row></rewards><option to="spider_hunt_step" text="Уйти"/><option to="go_to_warrior_step" text="Ок"><remove item="Apple" count="1"/><reward exp="300"/><reward item="Axe" count="5"/><reward recipe="Sunshard Paula" type="Table"/></option><info><label>Вернись к Волшебнику и скажи, что ты убил всех пауков</label></info></step><step id="go_to_warrior_step"><desc>На этом пока что всё. Мне нечего тебе поручить. Сходи к Аарону, может быть, у него есть что-то для тебя</desc><option to="#close" text="Ок"/><info><label>Выполни поручение от Аарона</label></info><option auto="before_find_pig_spawner_step"><complete quest="Маленькая Проблемка" step="after_aragog_step" /></option></step><step id="before_find_pig_spawner_step"><desc>Всё таки у меня есть для тебя поручение</desc><option to="find_pig_spawner_step" text="Ок"/></step><removestep id="find_pig_spawner_step"><intro>Собери-ка мне спавнер Свиньи</intro><desc>Так ты будешь собирать?</desc><finish>О, а вот и мой спавнер</finish><remove item="Antique Spawner" data="56" count="1" /><rewards><reward exp="500"/></rewards><option to="after_pig_go_warrior" text="Ок"/></removestep><step id="after_pig_go_warrior"><desc>Спасибо, Наездник Дракона, а теперь сходи опять к Аарону. У него опять есть для тебя какое-то задание</desc><option to="#close" text="Ок"/><info><label>Иди к Аарону ([FFFF00]опять[-])</label></info></step><step id="#end"><desc>Прощай, Наездник Дракона! Рад был повидаться</desc><option to="#start" text="Назад"/><option to="#close" text="Пока"/></step><!-- Плохое настроение --><step id="bad_mood_step"><intro>Очень жаль! Крепись там</intro><desc>Понимаю, что тебе сейчас не хорошо, крепись там!</desc><option to="brew_health_potion_step" text=":("><journal/></option><option to="#start" text="Назад"><reward item="Health Potion" count="1000"/></option></step><step id="brew_health_potion_step"><desc>Могу предложить тебе сварить Зелье Здоровья. Это должно помочь поднять тебе настроение.</desc><option to="#close" text="Понял"/><label>Вот рецепт:</label><recipe name="Health Potion" type="Cauldron" /><label><scale x="1000" y="8"/>[FFFF00]_[-]</label><row><label>Скрафти Котел</label><item name="Cauldron" count="0"/><label>и помести:</label></row><row><item name="Lilia Flower" count="1"/><item name="water" count="1"/><item name="Energy" count="1"/></row><info><label>Свари Зелье Здоровья по рецепту: </label><recipe name="Health Potion" type="Cauldron" /><label>После приготовления, подойди к Волшебнику!</label> <!-- --><label></label><label>Для приготовления потребуются: </label><item name="Cauldron" count="1"/><item name="Lilia Flower" count="1"/><item name="water" count="1"/><item name="Energy" count="1"/></info><option auto="return_to_wizard_step"><collect item="Health Potion" count="1"/> <!-- --></option></step><step id="return_to_wizard_step"><desc>Отлично! Вижу, что ты справился</desc><option to="consume_health_potion_step" text="Ок"/><label location="HUD"><persist/><position x="-200" y="-300"/><scale x="24" y="24"/>[FFAA00]Цель:[-] Отнести зелья Волшебнику</label><info><label>Вернись к Волшебнику и скажи, что ты сварил Зелье</label></info></step><consumestep id="consume_health_potion_step"><intro>Теперь выпей это Зелье! Тебе сразу же полегчает</intro><desc>Давай пей. Чего ты ждёшь?</desc><finish>Вот видишь. Я прямо вижу, как ты стал здоровее выглядеть!</finish><consume target="Player" item="Health Potion" count="1"/><rewards><reward exp="50"/></rewards><option to="#end_dark_side" text="Ок"/></consumestep><step id="#end_dark_side"><desc>Пока, Наездник Дракона. Был рад повидаться!</desc><option to="#start" text="Назад"/><option to="#close" text=". . ."/></step></quest>
WarriorFirstQuest.xml – «Маленькая Проблемка»
<?xml version="1.0" encoding="UTF-8"?><quest npc="Warrior" title="Маленькая Проблемка"><complete quest="Основной Квест" step="go_to_warrior_step" /><step id="#start"><desc>Привет, Наездник Дракона. Полагаю, тебя послал Волшебник. \nЧтож у меня есть для тебя небольшое заданьеце</desc><option to="small_problem_step" text="?"><journal /></option></step><step id="small_problem_step"><desc>Видишь ли, у меня тут появилась небольшая проблемка. Говорят, что на дне земных провалов на тамошних деревьях растут самые вкусные яблочки. Я бы и рад туда спуститься, но там сидит огромный паук – Арагог. \nЯ, понимаешь ли, даже мелких паучков страшусь, а тут аж огромный Паучище\nВ общем, не мог бы ты мне немножко подсобить с этой маленькой проблемкой? Я готов немно...</desc><rewards type="Display"><reward exp="500"/><reward item="Gold Coin" count="30"/></rewards><option to="before_aragog_step" text="Да"/></step><step id="before_aragog_step"><desc></desc><label>[FF0000]А-А-А-А! ОН УЖЕ ЗДЕСЬ![-]\n\nКто не спрятался — я не виноват!</label><option to="battle_aragog_step" text="!!!"/></step><step id="battle_aragog_step"><info><label>Убей [FF0000]Арагога[-]</label><kill enemy="GiantSpider" count="1"/></info><label location="HUD">[FF0000]АРАГОГ УЖЕ ЗДЕСЬ![-]<persist /><lerp loop="false" reverse="false" speed="2" start="0" wait="0"><from><scale x="50" y="50"/><position x="-1300" y="0"/><rotation angle="0"/></from><to><scale x="50" y="50"/><position x="1300" y="0"/><rotation angle="0"/></to></lerp></label><option auto="#close"><clearmobs/><spawnmobs value="false"/><spawn mob="GiantSpider" distance="3"/></option><option auto="after_battle_aragog_step"><kill enemy="GiantSpider" count="1"/><spawn mob="Warrior" distance="5" /></option><option auto="battle_aragog_step" start="3"><delay sec="3" /><existmobs name="GiantSpider"><inverse/></existmobs></option></step><step id="after_battle_aragog_step"><desc>Да ты мне жизнь спас! Так ещё я смогу насладиться тамошними яблочками в провалах!\nДержи, ты заслужил</desc><option to="after_aragog_step" text="Ок"/><rewards type="Award"><reward exp="500"/><reward item="Gold Coin" count="30"/></rewards></step><step id="after_aragog_step"><desc>Пока, и ещё раз спасибо за помощь</desc><option to="#close" text="Ок"><spawnmobs value="true"/></option><info><label>Иди к Волшебнику и проверь, есть ли у него задания</label></info><option auto="before_find_bed_step"><complete quest="Основной Квест" step="after_pig_go_warrior"/></option></step><step id="before_find_bed_step"><desc>Слу-у-ушай...</desc><option to="find_bed_step" text="Ок"/></step><removestep id="find_bed_step"><intro>Можешь найти мне кровать? Да, обычную кровать. Моя что-то в совсем уж печально состоянии</intro><desc>Так ты найдешь кровать?</desc><finish>Спасибо, Наездник Дракона. Эта кровать мне очень пригодиться</finish><remove item="Bed" count="1"/><rewards><reward exp="777"/></rewards><option to="#end" text="Ок"/></removestep><step id="#end"><desc>Ну а теперь пока, Наездник Дракона. Был рад повидаться.\nИди к Волшебнику, может, у него есть какое-то задание для тебя</desc><option to="#close" text="Пока"/></step></quest>
Отличный гайд, но осталось несколько вопросов:
1) Куда делся раздел с кастомным магазином из прошлой версии руководства. В 13.5.0? Его больше нельзя кастомизировать?
2) У Фуза в 13.5.0 есть свой магазин, следовательно, они тоже стали квестовыми НПС и для них можно делать кастомные квесты. Или нельзя?
1) Кастомные магазины полностью убраны (даже для Алхимика, Фуза и Крестьян). Теперь игра не будет подгружать квесты, содержащие в коде тег <store>
В логах будет писать:
Skipped custom quest ‘ИмяКвеста’ from file [Путь до XML-файла квеста]: NPC stores are not allowed in custom quests
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:LogWarning(Object)
QuestManager:AddUserQuests(XmlSerializer, String)
d__77:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
2) Да, для Фуза можно теперь тоже делать квесты. Список Квестовых NPC только что обновил, просто изначально забыл добавить
Из нюансов: У них пока что нет своего портрета, поэтому будет видно просто имя