[13.5] Создание Своих Квестов для Игры

Reading Mode

Глава 19. Анимации

В прошлых Главах мы научились выводить картинки и текст на экран. Все бы хорошо, но статичные картинки и текст – это скучно. Они просто стоят на месте, пока игрок не перейдет на следующий шаг, убрав их с экрана

Было бы интересней, например, добавить анимаций для какой-никакой динамики. Например, текст, который проносится через весь экран. Благо, в игре для этого есть отдельный тег <lerp/>, он же Линейная Интерполяция

Шаг 1. Основа анимации <lerp/>

Тег <lerp/> помещается внутрь image/label и заставляет игру плавно менять их параметры от параметров А к парметрам Б. Например, двигать стрелку из точки А в точку Б и постепенно наращивать ее размер по мере продвижения.

<lerp loop="true" reverse="true" speed="2" start="0" wait="0.5">
  <!-- Тут параметры А и Б -->
</lerp>

 

Где:

  1. loop (true/false) – Зациклить анимацию (да/нет). Если стоит false, то анимация проиграется один раз и остановится
  2. reverse (true/false) – Проиграть ли анимацию туда-обратно (да/нет). Пришел из А в Б, а потом вернлся обратно в А
  3. speed – Скорость анимации
  4. start – Задержка в секундах перед первым стартом анимации
  5. wait – Пауза в секундах в крайних точках. Условно, когда объект дошел до точки Б, он подождет это время перед тем, как вернуться обратно в А

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

Шаг 2. Контейнеры <from> и <to>

Чтобы игра вообще понимала, что мы собрались анимировать, внутрь <lerp/> нужно поместить два контейнера:

  1. <from> – Откуда начинаем (точка А). Базовое расположение объекта
  2. <to> – Куда идем (точка Б)

В этих контейнерах мы указываем теги форматирования, при этом теги, которые будут указаны вне этих контейнеров будут игнорироваться. Исключение: тег <persist/> и текст у <label/>

Теги форматирования:

  1. <persist/> – Остававить на экране после закрытия Диалога.
  2. <rotation/> – Поворот в градусах. Против часовой стрелки – положительные координаты (90, 30…), По часовой – отрицательные (-90, -180…)
  3. <position/> – Позиция по XY, куда поставить image/label. Отсчитывается по левому верхнему краю блока image/label
  4. <scale/> – Определяет растягивание по XY
  5. <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>

 

Проверяем:

Если вы считаете, что текст прллетает слишком быстро, то можете или умеьшить расстояние между точками А и Б, или уменьшить скорость. Я оставлю текущую

Задания на закрепление

  1. Сделайтетак, чтобы после убийства Арагога снизу всплывал текст: «Арагог повержен. \nВернись к Аарону и расскжаи об этом». Оформите это на свой вкус. Всплывать текст может хоть откуда: сверху падать, снизу выезжать и т.д.
  2. Сделайте оформление с 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>

 

2
0

Comments

  1. Northern_77
    Northern_77

    Отличный гайд, но осталось несколько вопросов:
    1) Куда делся раздел с кастомным магазином из прошлой версии руководства. В 13.5.0? Его больше нельзя кастомизировать?
    2) У Фуза в 13.5.0 есть свой магазин, следовательно, они тоже стали квестовыми НПС и для них можно делать кастомные квесты. Или нельзя?

    1
    0
    • Da-Damych
      Da-Damych

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

      1
      0

Leave a Reply