fallout.ru

Скрипты NPC: Искусственный Интеллект (ИИ) и Движение

Как заставить NPC пойти куда-либо

AITravel, float_числ_x, float_числ_y, float_числ_z, [reset]

Чтобы заставить NPC проследовать из одного место в другое, Вам следует использовать функцию AITravel.

Аргументы x, y, z – это координаты на глобальной карте. Вы можете определить их в Конструкторе, подведя камеру к желаемой позиции и выделив объект рядом – координаты высветятся под окном объектов. Предназначение дополнительного флага неизвестно.

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

Begin Travel

AiTravel, 1359, 2700, 1045

End Travel

Он не будет работать, поскольку скрипт будет постоянно загружаться и загружаться, а NPC застынет на месте.

Begin Travel

Short do_once

If ( do_once==0 )
            AiTravel, 1359, 2700, 1045
            Set do_once to 1
endif

End Travel

Следующий пример будет работать – NPC отправится к желаемой позиции, как только его скрипт станет активным, то есть он будет находиться в одной локации с игроком.

Проверка на совершение необходимого движения

GetAIPackageDone (возвращает Boolean/short)

if ( GetAIPackageDone == 1 )
            [делать что-то]
endif

Функция GetAIPackageDone проверяет, появился ли NPC в необходимой локации. В тот самый единственный момент, когда движение заканчивается, функция возвращает 1. Следующий скрипт показывает, как использовать ряд функций AITravel и условных конструкций:

Begin TravelLoop

short state
float timer

if ( menumode == 1 ) ; если включено меню, скрипт не работает
            return
endif

;Начать движение

if ( state == 0 )
            if ( player -> GetDistance HB_adros_darani < 5000 )
                        set state to 5
            endif

;******************* Начинает свой путь
elseif ( state == 5 )
            SetHello 0
            AITravel -8144, -19409, 728 ;новые координаты точки 1
            set state to 10

elseif ( state == 10 )
            if ( GetAIPackageDone == 1 ) ;достиг точки 1
                        set state to 40
            endif

elseif ( State == 40 )
            AITravel -9147, -19459, 720 ;новые координаты точки 2
            set State to 50

elseif ( state == 50 )
            if ( GetAIPackageDone == 1 ) ;достиг точки 2
                        set state to 60
            endif

elseif ( state == 60 )
            AITravel -8144, -19409, 728 ;новые координаты точки 1
            set state to 70

elseif ( state == 70 )
            if ( GetAIPackageDone == 1 ) ;достиг точки 1
                        set state to 80
            endif

elseif ( state == 80 )
            AITravel -6640, -18496, 1040 ;новые координаты точки 0
            set state to 90

elseif ( state == 90 )
            if ( GetAIPackageDone == 1 ) ;достиг точки 0
                        set state to 0
            endif
endif

End TravelLoop

Хорошим примером использования AITravel является передвижение Фаргота к его тайнику и скрипт “CharGenWalkNPC”, управляющий тюремным стражем, проводящим Вас по кораблю в начале игру.

Если Вы планируете часто использовать подобные функции, то Вам стоит запомнить следующий момент. Если движение началось, а игрок решает покинуть локацию или поспать, то, когда он вновь вернётся, NPC застынет в той позиции, в которой был покинут. Следующая часть кода предотвратит подобные неприятности (следует добавить в прошлый скрипт):

; *************** Спасение замороженного скрипта – восстанавливает скрипт после возвращения или
; *************** пробуждения игрока
If ( Player -> GetDistance, HB_adros_darani < 5000 )
            if ( GetCurrentAIPackage == -1 ) ; проверка на замороженность
                        set timeout to ( timeout + GetSecondsPassed )
                        if ( timeout >= 3 ) ; немного подождать.
                                    set state to ( state - 10 ) ; движение вновь начинается сначала
                                    set timeout to 0
                        endif
            else
                        set timeout to 0
            endif
endif

Повернуть персонажа

Face x_float, y_float

Заставляет NPC повернуться лицом в заданном направлении. Вероятно, функция не прерывает выполнение активной анимации. То есть, если персонаж бродит, а на нём была использована эта функция, то он поворачивается в заданном направлении и продолжает своё движение, как ни в чём ни бывало.

Пример: скрипт заставляет NPC постоянно наблюдать за игроком:


set px to player->getpos x
set py to player->getpos y
face px, py

Случайное движение

AiWander, расстояние_числ, длительность_числ, время_числ, [idle2], [idle3], …[idle9], [reset]

"ID" -> AIWander, 512, 5, 0, 0,20,0,0,10,30,0,0

Это алгоритм случайного движения, который используется почти всеми NPC в игре – они ходят туда обратно, меняют направление движения и останавливаются, чтобы почесать голову или оглядеться.

  • Длина: определяет расстояние, на которое существо может удалиться от начального положения
  • Длительность: возможно, время (в часах), которое определяет, как долго NPC будет выполнять своё движение (перед тем, как оно обнуляется, что, возможно, происходит, когда игрок уходит или спит? Не уверен)
  • Время: вероятно, определяет начальное время, если у движения определена длительность.
  • [idle2], …[idle9]: возможные варианты прерывания движения:
    Idle2:            Осматривается
    Idle3:            Смотрит назад
    Idle4:            Чешет голову
    Idle5:            Поправляет одежду и броню на плече
    Idle6:            Сводит руки вместе, показывая товары
    Idle7:            Смотрит на пальцы и осматривается с опаской
    Idle8:            Глубоко задумывается
    Idle9:            Дотрагивается до оружия

Для того, чтобы заставить NPC просто стоять на месте, можно использовать просто: AIWander, 0, 0, 0

Активация объектов

AIActivate "Object ID"
AIActivate , ObjectID, [reset]

По словам Bethesda, «эта функция заставляет NPC активировать какой-либо объект. Мощный инструмент, возможности которого трудно переоценить».

В стандартном Морровинде функция, похоже, не работает, единственная её возможность – заставить NPC что-либо выпить. В Трибунале, она, вроде бы, исправлена, по крайней мере, её возможности расширились. Я удачно использовал её, чтобы заставить NPC поднимать оружие, открывать обычные двери и проходить сквозь двери между локациями. Последний вариант работает только, если маркер двери находится в той же локации, что и NPC, или в активной локации, в которой находится герой. Иначе игра вылетает. Мне также удалось удачно использовать активатор – кнопку у Призрачных Ворот.

Мне не удалось обнаружить сигнала AIPackageDone, но поменять направление движения можно и с помощью других методов (смотри ниже).

Тип ОбъектаПри активации
NPCНачать разговор
КонтейнерОткрыть
ДверьОткрыть
Дверь между локациямиОткрыть/телепортировать (только в той же локации)
Оружие, броня и прочееПоднять
Книга/свитокПрочесть (что бы это означало для NPC?)
АктиваторыВыполнить скрипт активатора

Примеры: вот некоторые из моих тестовых скриптов. Они показывают, как определить, закончилось ли движение:

Begin TT_opendoor

short doonce
short AIState

if ( doonce == 0 )
            if ( GetDistance, Player < 400 )
                        AIActivate TT_door
                        set doonce to 1
            endif
elseif ( doonce == 1 )
            set AIState to GetCurrentAIPackage
            MessageBox "Package = %g", AIState
            if ( TT_door->GetAngle, z != 180 ); Как только дверь начинает вращение
                        MessageBox "Готово"
                        AIWander 30, 5, 0, 0,20,0,0,10,30,0,0
                        set doonce to 2
            endif
endif

end


Begin TT_pickmace

short doonce
short AIState

if ( doonce == 0 )
            if ( GetDistance, Player < 400 )
                        AIActivate TT_daedric_mace
                        set doonce to 1
            endif
elseif ( doonce == 1 )
            set AIState to GetCurrentAIPackage
            MessageBox "Package = %g", AIState
            if ( GetItemCount, TT_daedric_mace >= 1 ); как только в инвентаре NPC появился предмет
                        MessageBox "Готово"
                        AIWander 512, 5, 0, 0,20,0,0,10,30,0,0
                        set doonce to 2
            endif
endif

end


Begin TT_openloaddoor

short doonce
short AIState

if ( doonce == 0 )
            if ( GetDistance, Player < 400 )
                        AIActivate TT_door
                        set doonce to 1
            endif
elseif ( doonce == 1 )
            set AIState to GetCurrentAIPackage
            MessageBox "Package = %g", AIState
            if ( GetPos, y > 2000 ); маркер был в той же локации
            ;Другие условия, например, GetPCCell также могут быть использованы.
                        MessageBox "Готово"
                        AIWander 30, 5, 0, 0, 20, 0, 0, 10, 30, 0, 0
                        set doonce to 2
            endif
endif

end

Следование

AIFollow, "NPC ID", длительность_f_числ, x_f_числ, y_f_числ, z_f_числ, [reset]
AIFollowCell, "NPC ID", "ID локации", длительность_f_числ, x_f_числ, y_f_числ, z_f_числ, [reset]

"MobID" -> AIFollow, "Mob2ID", 0,0,0,0

Функция заставляет одного персонажа следовать за другим. Можно использовать эту возможность, как для следования за игроком, так и для формирования каравана. Пример из моей практики, показывающий использование функции без всяких проверок:

elseif ( state == 20 )
            HB_guar_pack_adros_ -> AIFollow, HB_adros_darani, 0, 0, 0, 0
            AITravel -8144, -19409, 728 ;новые координаты точки 1
            set state to 30

Поскольку здесь нет ни длительности движения, ни определённых координат, гуар будет следовать за NPC, пока не получит другой команды. Поэтому позаботьтесь о том, чтобы функции движения выполнялись только 1 раз, а не несколько десятков раз за секунду.

Длительность, ID локации, координаты x, y, z – условия, по выполнению которых функция будет остановлена. Функция AIFollowCell позволяет пунктом назначение указать некий интерьер. Предназначение дополнительного флага “reset” в данный момент мне неизвестно.

AIEscort, "NPC ID", длительность, x, y, z, [reset]
AIEscortCell, "NPC ID", "ID локации", длительность, x, y, z, [reset]

Эти функции позволяют заставить NPC вести куда-либо игрока. Персонаж остановится и подождёт, если расстояние между ним и игроком увеличится слишком сильно. Пример использования этой функции можно увидеть в самом начале игры, когда стражник, проводящий Вас по кораблю останавливается, чтобы подождать, если Вы отстали от него.

Проверка значения выполняемого движения

Для написания сложных скриптов полезно знать, какой режим движения совершается в данный момент, и в зависимости от этого выполнять определённые действия.

GetCurrentAIPackage (возвращает short)

If ( GetCurrentAIPackage == 2 )
            [делать что-то]
endif

Возвращаемые значения:

ДействиеЗначение
Ничего
-1
Брожение
0
Путешествие
1
Эскорт
2
Следование
3
Активация
4
Преследование
5

Как заставить NPC красться

ForceSneak
ClearForceSneak
GetForceSneak (возвращает Boolean/short)

Команда “ForceSneak” заставляет NPC перейти в крадущийся режим, “ClearForceSneak” выключает его. К сожалению, похоже, не существует функции, способной включить режим бега (она была добавлена лишь в Трибунале). “GetForceSneak” возвращает 1, если NPC крадётся. Хорошим примером тому является скрипт “LookoutScript”. Вот отрывок из него:

elseif ( walkstate == 2 )
            Fargoth->ForceSneak ; включение крадущегося режима
            Fargoth->AiTravel -11468.595,-71511.531,173.728
;идёт к дереву             set walkstate to 3

elseif ( walkstate == 3 )
            if ( Fargoth->GetAiPackageDone == 1 )
                        ;Fargoth->Equip "torch_infinite_time_unique"
                        set walkstate to 4
                        ;MessageBox "SHOULD BE AT TREE"
            endif

elseif ( walkstate == 4 )

            set timer to timer + GetSecondsPassed

            Fargoth->ClearForceSneak ; выключение крадущегося режима
            Fargoth->AiWander 0 0 0 0 0 0 0 0 0

                        if ( timer > 3 )
                                    Fargoth->ForceSneak ; реинициализация крадущегося режима
                                    Fargoth->AiTravel -11410.590,-72057.188,133.644 ;идёт к стене
                                    set walkstate to 5
                        endif

Заставить кого-либо упасть

Fall

Заставляет NPC упасть. Может служить дополнительным толчком вниз, когда из под ног персонажа уходит почва. Также заставляет летающие существа свалиться с небес. Эта функция заставляет алхимика разбиться насмерть неподалёку от Сейда Нин.