Сейчас 05:34:08 Пятница, 22 ноября, 2024 год
Главная ⇒ Форум ⇐ RSS Файлы Cтатьи Картинки В о й т и   или   з а р е г и с т р и р о в а т ь с я

Меню сайта

Категории

Наш опрос
Какой тип карт вам нравится больше всего?
Проголосовало: 145114

Сейчас на сайте
На сайте всего: 70
Гостей: 70
Пользователей: 0

Реклама

Главная » Статьи по WarCraft 3 » Создание карт » Jass
Создаем небольшой ИИ для нейтралов.
Доброго времени суток уважаемые посетители сайта, думаю вы уже поняли тему статьи. Цель статьи - сделать систему, чтобы NPC - в нашем случае Нейтрально-враждебыне юниты, атаковали ближайших врагов. Вы спросите: "А зачем все это, нейтралы и так на ура бьют ближайших?", у меня есть ответ, у нас своя "фича", вот, например, если у вас есть герой, который имеет очень большую мобильность, и за ним гонится орда "крипов", то они довольно таки длительное время будут гнатся за вашим героем, а тут, они поменяют приоритет на ближайшего врага. Что с некоторой стороны - весьма логично.

Полагаю, вы уже знакомы с GUI, или как называют еще, триггеры. В этой статье, мы сделаем систему, где нейтральные юниты, будут атаковать ближайших врагов. Сам я не являюсь знатоком JASS'а, так же не являюсь опытным писателем, так что попытаюсь максимально доступно пояснить все наши действия.
Думаю, у вас установлен JNGP, так что, писать код(если вы кончено не будете все тупо "копипастить", что я вам крайне не рекомендую) нам будет куда удобнее и приятнее.
Для начала, создаем пустой триггер, назовем его, "Sis12Attack", добавляем "Время - периодическое событие", ставим там 0.50 сек. Самый оптимальный вариант. Теперь "конвентируем триггер в текст", и получаем это:
Код
[code=jass]
function Trig_Sis12Attack_Actions takes nothing returns nothing

endfunction

//===========================================================================
function InitTrig_Sis12Attack takes nothing returns nothing
set gg_trg_Sis12Attack = CreateTrigger( )
call TriggerRegisterTimerEventPeriodic( gg_trg_Sis12Attack, 0.50 )
call TriggerAddAction( gg_trg_Sis12Attack, function Trig_Sis12Attack_Actions )
endfunction
[/code]

Думаю разбирать что да как тут смысла нет, т.к. я уже писал в предыдущей статье, как примерно состоит триггер в JASS'e.

Так же, надо создать глобальную переменную "отряд" для наших нейтралов, которые будут атаковать ближайших врагов. У меня будет "g", просто "g", а в JASS'e будет выглядить "udg_g" - Это для тех переменных, которые были созданны в специальном окне редакторов триггеров, в общем, по умолчанию.

Теперь же, пожалуй можно приступить к аналитике. Для начала, нам надо будет "прогнать" юнитов из нашей группы, потом по нашему "прогнонному" юниту делать главное действие, в действии будем "выбирать" всех врагов в радиусе 1000.0 и сверять дистанцию меж выбранным юнитом и нашим "прогнанным" юнитом. Довольнотаки сложно выглядит, согласен.

Сейчас нам надо будет написать "вспомогательные" функции, это те, без которых наша система не будет работать. Начнем с того, что напишем функцию которая определяла бы дистанцию меж координатами. За основу берем BJ-фнкцию "DistanceBetweenPoints(locationA,locationB)". Как вы видите, она берет аргументы типа "точки"(локации), а в нашем случае это очень не удобно, т.к. мы будем работать с Координатами(real). Теперь пишем код:
[code=jass]
function DistanceBetweenPointsX takes real x,real y,real xx,real yy returns real
local real dx = xx - x
local real dy = yy - y
return SquareRoot(dx * dx + dy * dy)
endfunction
[/code]

Вот и все она берет значения "x","y" - это как раз подойдет для нашего "прогоняемого" юнита, а "xx","yy" для нашей "выбранной" еденицы (то есть, с которым мы будем сверять дистанцию), а возвращает дистанцию меж координатами. Все просто.

Теперь наш триггер выглядит так:
[code=jass]
function DistanceBetweenPointsX takes real x,real y,real xx,real yy returns real
local real dx = xx - x
local real dy = yy - y
return SquareRoot(dx * dx + dy * dy)
endfunction

function Trig_Sis12Attack_Actions takes nothing returns nothing

endfunction

//===========================================================================
function InitTrig_Sis12Attack takes nothing returns nothing
set gg_trg_Sis12Attack = CreateTrigger( )
call TriggerRegisterTimerEventPeriodic( gg_trg_Sis12Attack, 0.50 )
call TriggerAddAction( gg_trg_Sis12Attack, function Trig_Sis12Attack_Actions )
endfunction
[/code]

Теперь нам осталась последняя "вспомогательная" функция, это фильтр, который будет проверять враждебность и живучесть для нашей цели, не будет же наш юнит бить союзника в конце концов.
Вот её код:
[code=jass]
function GetFilterEnemy12 takes nothing returns boolean
return IsPlayerEnemy(Player(12), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction
[/code]

Эта функция возвращает "true" тогда, когда выбранный юнит будет врагом для "Нейтрально Враждебного" и если у него здоровья больше 0.405. Дело в том, что юниты умирают не с нулевым значением здоровья, как полагают многие, а остаются в ~0.405 значением.
Обратите внимание на "Player(12)" - нет, это не игрок 12, это "Нейтрально враждебный". Дело в том, что в JASS все нумерация игроков начинается с нуля, например, игрок 1 в JASS будет Player(0), а игрок 12 будет Player(11).

Все, теперь наш триггер выглядит таким образом:
[code=jass]
function GetFilterEnemy12 takes nothing returns boolean
return IsPlayerEnemy(Player(12), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

function DistanceBetweenPointsX takes real x,real y,real xx,real yy returns real
local real dx = xx - x
local real dy = yy - y
return SquareRoot(dx * dx + dy * dy)
endfunction

function Trig_Sis12Attack_Actions takes nothing returns nothing

endfunction

//===========================================================================
function InitTrig_Sis12Attack takes nothing returns nothing
set gg_trg_Sis12Attack = CreateTrigger( )
call TriggerRegisterTimerEventPeriodic( gg_trg_Sis12Attack, 0.50 )
call TriggerAddAction( gg_trg_Sis12Attack, function Trig_Sis12Attack_Actions )
endfunction
[/code]

Учтите, что функции надо объявлять до того когда они будут вызваны. Пожалуй я просто приведу не сложный пример вместо тысячи букв. Например, если вызвать нашу недавно созданную функцию "DistanceBetweenPointsX" внутри функции "GetFilterEnemy12" то компилятор выдаст ошибку, что функция не существует, т.к. мы её еще не объявили.

Что ж, теперь пишем непосредственно основное "тело" системы, в нашем пустующей функции Trig_Sis12Attack_Actions:
[code=jass]
function Trig_Sis12Attack_Actions takes nothing returns nothing
call ForGroup(udg_g,function Sis12AttackX)
endfunction
[/code]

Как вы поняли, сейчас как раз мы сделали "прогон". И для этого нам надо будет создать функцию "Sis12AttackX". Естeственно, пишем её выше функции "Trig_Sis12Attack_Actions", тем самым, объявляя её.
____заметка, а вообще, правильнее называть такие функции - процедура, т.к. такие функции ничего не возвращают.
Собственно код функции Sis12AttackX:
[code=jass]
function Sis12AttackX takes nothing returns nothing
local unit u = GetEnumUnit()//наш "програнный" юнит
local real x = GetUnitX(u)//координаты нашего
local real y = GetUnitY(u)//юнита, Х и Y
local real xx//Это координаты для
local real yy//выбранного юнита
local real lastdis=1000.0//изначальный радиус
local boolexpr b = Condition(function GetFilterEnemy12)//Фильтр
local unit t//здесь будем хранить самого ближайшего "врага"
local group g = CreateGroup()//сразу же создаем группу
local unit f//для проверки цикала
call GroupEnumUnitsInRange(g, x, y, 1000.0, b)//Здесь мы добавляем всех юнитов в группу g, которые прошли через наш фильтр
loop
set f = FirstOfGroup(g)//Здесь мы прогоняем юнитов
exitwhen f == null//цикл прекращается как в нашей группе не будет юнитов
set xx = GetUnitX(f)//Тут то мы и придаем значения
set yy = GetUnitY(f)//координат выбраного юнита
if DistanceBetweenPointsX(x,y,xx,yy) < lastdis then
//проверяем дистанцию меж нашим юнитом и врагом, если
//дистанция меньше lastdis, то
set t=f//t = ближайшая цель
set lastdis=DistanceBetweenPointsX(x,y,xx,yy)//сохраняет дистанцию в lastdis
//так что не беспокоимся что первый попавшийся окажется нашей целью.
//таким хитрым образом мы находим ближайшего противника.
endif
call GroupRemoveUnit(g, f)//удаляем "програнного" юнита из группы, что бы цикл небыл бесконечным.
endloop
call IssueTargetOrder(u,"attack",t)//когда все закончилось, можем смело отдать приказ нашему юниту на атаку ближайшей цели.

//далее идет "обнуление"
call DestroyGroup(g)//перед тем как обнулить переменную "g", сначала надо уничтожить созданную группу.
call DestroyBoolExpr(b)//тоже самое с фильтором
set u = null//теперь спело обнуляем handle-переменные
set b = null
set t = null
set g = null
set f = null
endfunction
[/code]

ДЛЯ ТЕХ КОМУ ЛЕНЬ ЧИТАТЬ, ЛИБО ПРОСТО НУЖНЕН КОД, ПОЖАЛУЙСТА:
[code=jass]
function GetFilterEnemy12 takes nothing returns boolean
return IsPlayerEnemy(Player(12), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

function DistanceBetweenPointsX takes real x,real y,real xx,real yy returns real
local real dx = xx - x
local real dy = yy - y
return SquareRoot(dx * dx + dy * dy)
endfunction

function Sis12AttackX takes nothing returns nothing
local unit u = GetEnumUnit()//наш "програнный" юнит
local real x = GetUnitX(u)//координаты нашего
local real y = GetUnitY(u)//юнита, Х и Y
local real xx//Это координаты для
local real yy//выбранного юнита
local real lastdis=1000.0//изначальный радиус
local boolexpr b = Condition(function GetFilterEnemy12)//Фильтр
local unit t//здесь будем хранить самого ближайшего "врага"
local group g = CreateGroup()//сразу же создаем группу
local unit f//для проверки цикала
call GroupEnumUnitsInRange(g, x, y, 1000.0, b)//Здесь мы добавляем всех юнитов в группу g, которые прошли через наш фильтр
loop
set f = FirstOfGroup(g)//Здесь мы прогоняем юнитов
exitwhen f == null//цикл прекращается как в нашей группе не будет юнитов
set xx = GetUnitX(f)//Тут то мы и придаем значения
set yy = GetUnitY(f)//координат выбраного юнита
if DistanceBetweenPointsX(x,y,xx,yy) < lastdis then
//проверяем дистанцию меж нашим юнитом и врагом, если
//дистанция меньше lastdis, то
set t=f//t = ближайшая цель
set lastdis=DistanceBetweenPointsX(x,y,xx,yy)//сохраняет дистанцию в lastdis
//так что не беспокоимся что первый попавшийся окажется нашей целью.
//таким хитрым образом мы находим ближайшего противника.
endif
call GroupRemoveUnit(g, f)//удаляем "програнного" юнита из группы, что бы цикл небыл бесконечным.
endloop
call IssueTargetOrder(u,"attack",t)//когда все закончилось, можем смело отдать приказ нашему юниту на атаку ближайшей цели.

//далее идет "обнуление"
call DestroyGroup(g)//перед тем как обнулить переменную "g", сначала надо уничтожить созданную группу.
call DestroyBoolExpr(b)//тоже самое с фильтором
set u = null//теперь спело обнуляем handle-переменные
set b = null
set t = null
set g = null
set f = null
endfunction

function Trig_Sis12Attack_Actions takes nothing returns nothing
call ForGroup(udg_g,function Sis12AttackX)
endfunction

//===========================================================================
function InitTrig_Sis12Attack takes nothing returns nothing
set gg_trg_Sis12Attack = CreateTrigger( )
call TriggerRegisterTimerEventPeriodic( gg_trg_Sis12Attack, 0.50 )
call TriggerAddAction( gg_trg_Sis12Attack, function Trig_Sis12Attack_Actions )
endfunction
[/code]

Вопросы и ответы
>>Так как теперь это использовать?
Просто до безобразия, создаете триггер, не важно, инициализация или с событием. Добавляете нейтрально-враждебного юнита в "отряд" "g". А когда юнит умирает, удаляйте этого юнита из группы.

А если серьезно, то это просто попытка дать "мозги" ботам. С вами был Bornikkeny, до встречи.
Просмотров: 2401 Добавил: bornikkeny Добавлено: 04 Мая 2013 в 12:21:03
Комментариев: 10 |

Всего комментариев: 10
12 Апреля 2015
Я нечего не понял но красиво написал, лайк) ^_^

12 Марта 2014
9. Шохрух (Reckit) [Материал]
ДЛЯ ТЕХ КОМУ ЛЕНЬ ЧИТАТЬ, ЛИБО ПРОСТО НУЖНЕН КОД, ПОЖАЛУЙСТА:
а в чём разница до этого кода и после? zomba

17 Июня 2013
Joker, нет, что вы :)

12 Июня 2013
7. Красный Колпак (Jоker) [Материал]
Мн одному показалось, что на картинке php?

09 Мая 2013
Ах да, ребята, я тут ошибку допустил.
function Trig_Sis12Attack_Actions takes nothing returns nothing
ForGroup(udg_g,function Sis12AttackX)
endfunction
Тут я забыл Call. Замените это на
function Trig_Sis12Attack_Actions takes nothing returns nothing
call ForGroup(udg_g,function Sis12AttackX)
endfunction

08 Мая 2013
Bru, как получится. И спасибо, не часто в интернетах похвалу получишь..

08 Мая 2013
Порыв хорош, жаль пример плохой.
Да и я думаю такой простенький скрипт вряд ли достоин целой статьи.
Но молодец! Скоро я буду на статьях, дам тебе немного подсказок, и было бы очень хорошо, если бы ты наполнял каталог хорошими статьями!

08 Мая 2013
Bru, конечно я уважаю ваше мнение, но я бы код мог натереть до блеска, но как ни как, это просто "попытка дать мозги ботам" и попытка призвать ГУИШНИКОВ изучить в конце концов JASS. Такие дела, вот.

07 Мая 2013
Ну хотя бы такой код - http://pastebin.com/VVqF2h7G.
Был бы редактор, сократил бы ещё раза в два.

07 Мая 2013
Плохая статья. Зайду с компа - распишу.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Форма входа

Поиск

Случайная картинка

Случайный файл
[01 Ноября 2009]
[Карты · Melee]
Windy Barrens -
  • Автор карты: X-OMG-X
  • Жанр: Melee
  • Версия: 1.0 от 25.09.09
  • Язык: Неважен
  • Кол-во игроков: 1vs1
  • Поддержка компьютера: Есть

  • Новые карты
    [07 Февраля 2016]
    Переезжаем на другой сайт, господа![Dota]
    [18 Октября 2015]
    Duel of Gods PreV[Другое]
    [18 Октября 2015]
    Hero of The Empire v1.18g[RPG]
    [17 Октября 2015]
    Servant War v1.05[Другое]
    [17 Октября 2015]
    Age of Vikings Edited v1.6[Другое]
    [17 Октября 2015]
    Strife of the Champions Beta v1.2[Arena]
    [17 Октября 2015]
    VirusBoll (rus)[Другое]
    [17 Октября 2015]
    Exterminators v1[AoS]
    [17 Октября 2015]
    The Lord Heroes v1.2[Другое]
    [17 Октября 2015]
    Versus heroe Arena 1.0 AI[Arena]

    5 лучших по кол-ву добавленных статей
    [ Duosora ] [ 58 ]
    [ Messenger ] [ 52 ]
    [ Bru ] [ 39 ]
    [ Pand@ ] [ 35 ]
    [ OrcRider ] [ 27 ]

    Наша кнопка
    Warcraft3FT.info - Всё для Warcraft 3 и DotA

    Другие варианты

    Статистика

    Материалы:
    Новости: 1010
    Файлы: 8668
    Статьи: 680
    Картинки: 8256
    Форум: 30520/954989
    Комментарии: 58094
    Copyright © 2006 - 2024 Warcraft3FT.info При копировании материалов c сайта ставьте, пожалуйста, активную обратную ссылку на нас • Design by gReeB04ki ©
    Хостинг от uCoz