XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
Sargeras
Лидер "Двух Королевств"
offline
Опыт: 22,163
Активность:
Создание простого (Melee) AI
Руководство по AI-скрипту: Создание простого (Melee) AI

Автор: Vidstige
Ссылка на оригинал
{Примечание: мои собственные пометки начинаются с символа “{” и заканчиваются символом “}”. Чтобы статья была похожа на статью, из неё были убраны некоторые авторские слова, не относящиеся к теме статьи, а также те, которые в какой-то мере были проявлением рекламы сайта, где расположен оригинал. Кроме того, в статье не были переведены те моменты, которые касались того, что уже есть на нашем сайте, в частности, теория Jass’a, лучше описанная юзером Сергеем в его соответствующей статье, однако всё то, что непосредственно связано с написанием AI-скриптов, например, рассмотрение понятия «циклы» в Jass’e с примерами из «реальной жизни», осталось. Благодарю Remal’a за предоставленную информацию для перевода и Злую Тысячу (ZlaYa1000) за предложение перевести её.}

Это руководство расскажет, как шаг за шагом построить Melee AI-скрипт через файл .ai. Если вы думаете, что это руководство научит вас, как сделать AI лучше, чем AMAI, то вам не следует продолжать его читать. Взамен оно попытается научить вас основам Jass, стандартным встроенным функциям и понятиям, которые вам понадобятся, чтобы попробовать свои силы в качестве AI-скриптера.

Это руководство (к несчастью) не будет всё о триггерах.
------------------------------------------
Содержание:
1. Hello World!
2. Сбор ресурсов
3. Подготовка большего количества рабочих
4. Строительство ферм для поддержки большего количества рабочих
5. Создание барака и подготовка нескольких солдат
6. Больше теории Jass’a: управление действиями, условия и циклы
7. Функции: те, которые мы использовали до этого
8. Когда нам следует что-то построить [код для добавления ]
9. Функции: Создание и использование
1. Hello World!


Давайте начнём с уверенности, что вы знаете, как получить простой скрипт “Hello World!” и запустим его.
Код:
function main takes nothing returns nothing  
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello World!" )  // F12 вам не поможет
endfunction

Вставьте вышеуказанный код в файл, который называется human.ai .
Запустите Word Editor.
Откройте вашу любимую melee - карту и сохраните её под другим именем или (предпочтительней) в другой директории.
Нажмите F12 (или откройте меню модулей и выберите менеджер импорта) .
Select File --> Import file in the Import manager.
Выберите созданный файл human.ai.
Щёлкните правой кнопкой мыши на импортированном файле и выберите Modify file properties.
Измените путь к файлу на “\Scripts\human.ai”.
Сохраните карту (вам, вероятно, также нужно закрыть карту).
Запустите WarCraft III, выберите ''Single game -> Custom Game -> Ваша милая карта’’.
Убедитесь, что Вы играете против компьютера расы человека.
Позже Вы захотите выбрать, чтобы вся карта стала видимой.
Начните игру и ищите “Hello World!” на экране (она всё же быстро исчезает).

Если Вы считаете, что надпись очень быстро исчезла, или Вы не уверены, что она вообще была там, Вы можете попробовать эту версию вместо той:
Код:
function main takes nothing returns nothing
    call Sleep( 2 )  // <--- Чтобы удостовериться, что игра готова записать приветствие.
    call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, "Hello World!" )
endfunction

Если Вы далеко от экрана более чем один час (3600 сек), Вы можете посмотреть на приветствие, нажав F12.

2. Сбор ресурсов


Каждый знает, что сбор ресурсов является важной аспектом во время игры в Warcraft. Так почему бы не посмотреть на то, как должен быть сделан AI-скрипт для сбора:
Код:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // ждать одну секунду
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // начало вечного цикла
        call ClearHarvestAI()   // Сначала восстанавливаем то, что было сделано менеджером сбора и 
        call HarvestGold( 0, 4 )   // затем говорим ему иместь 4 работника, собирающих золото
        call HarvestWood( 0, 1 )   // и 1 работника, собирающего дерево.
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )
        call Sleep( 1.0 )  // Если мы никогда не будем спать, то движок убьёт нас.
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

Опробуйте этот скрипт тем же способом, с каким опробовали вышеуказанный “Hello World!”. Вместо удаления более раннего импортированного файла, и дальнейшего добавления новой версии, Вы можете щёлкнуть правой кнопкой мыши (в менеджере импорта) на добавленный до этого файл и выбрать замену.

Не забывайте всегда устанавливать полную видимость в дополнительных настройках игры, иначе вам будет нелегко увидеть, что делает AI-игрок. { Хотя можно воспользоваться чит-кодом “iseedeadpeople”}

Почему мы восстанавливаем и переназначаем менеджеру сбора время? Что случится, если рабочий, рубящий дерево, умрёт? Если мы не восстановим то, что было мделано менеджером сбора, работник больше никогда не сможет быть заменён.
.
3. Подготовка большего количества рабочих


Если у нас есть только 5 работников, то их недостаточно, чтобы создать армию, поэтому давайте создадим гораздо больше рабочих.
Код:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // Ждать 1 секунду
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // начало вечного цикла
        call ClearHarvestAI()   // Сначала восстанавливаем то, что было сделано менеджером сбора и
        call HarvestGold( 0, 4 )   // затем говорим ему иметь 4 работника, собирающих золото
        call HarvestWood( 0, GetUnitCountDone('hpea')-4 )   // а остальным собирать дерево.
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )
        call SetProduce(1, 'hpea', 0)  // Попытаться начать тренировку крестьянина
        call Sleep( 3.0 )  // Если мы никогда не будем спать, то движок убьёт нас. 
    endloop
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction


GetUnitCountDone('hpea') считает, как много людских крестьян, которых контролирует игрок. Это используется для того, чтобы высчитать, как много крестьян, которые не собирают золото и послать их рубить дерево. Почему бы вам не понаблюдать за компьютером, строящим тысячи рабочих?

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

Вы попытались? Построил ли он тысячи юнитов? Нет? Ему нужно больше пищи? Мы скоро сделаем это, построив несколько ферм…

4. Строительство ферм для поддержки большего количества рабочих


Давайте построим несколько ферм, из-за чего мы сможем подготовить большее количество рабочих и получить больше золота. Если мы производим менее чем 3 дополнительных единицы пищи, тогда мы начинаем строить новую ферму.

Код:
function main takes nothing returns nothing
    call Sleep( 1.0 )  // ????? ???? ???????
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )
    loop   
        call ClearHarvestAI()   
        call HarvestGold( 0, 4 )   
        call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )   
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )
        call SetProduce( 1, 'hpea', 0 )  // ??????????? ???????????

        // Наша база производит еду для 12 людей, и каждая база для 6 людей                           
        // Сравните производимую еду с количеством людей и решите, 
        // стоит ли строить ферму (дом людей).

        if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') < 3 ) then
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
            call SetProduce( 1, 'hhou', 0 )   // Строительство фермы
        endif

        call Sleep( 3.0 )
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

Благодаря этому получим множество рабочих и множество ресурсов, но не следует ли нам попытаться потратить ресурсы на что-то забавное? Если мы будем создавать больше строений, возможно, эти здания помогут нам тренировать армию. Почему бы нет?

5. Создание барака и подготовка нескольких солдат


Настало время, чтобы предпринять наши первые меры к подготовке армии, которая разрушит всё сопротивление. Давайте начнем с создания барака и подготовим какого-нибудь пехотинца.

Код:
function main takes nothing returns nothing
    call Sleep( 1.0 )
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop
        call ClearHarvestAI()   
        call HarvestGold( 0, 4 )
        call HarvestWood( 0, GetUnitCountDone( 'hpea' )-4 )
        call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Harvesting" )

        call SetProduce( 1, 'hpea', 0 )  // ??????????? ???????????

        // Наша база производит еду для 12 людей, и каждая база для 6 людей                           
        // Сравните производимую еду с количеством людей и решите, 
        // стоит ли строить ферму (дом людей).

        if ( 12 + 6 * GetUnitCount('hhou') - GetUnitCount('hpea') < 3 ) then
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a house" )
            call SetProduce( 1, 'hhou', 0 )   // Строительство фермы

        endif

        if ( GetUnitCount( 'hbar' ) < 1 ) then  // Есть ли у нас казармы?
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to build a barrack" )
            call SetProduce( 1, 'hbar', 0 )   // Построить казармы (в главном городе).
        else
            call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Trying to train a footman" )
            call SetProduce( 1, 'hfoo', 0 )   // Создать пехотинца (в главном городе).
        endif

        call Sleep( 3.0 )  // Если мы никогда не спим, такой движок может убить нас. 
    endloop
    
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction

Возможно, Вы обратили внимание, что AI через некоторое время одновременно остановило конструкцию ферм и строительство юнитов. Можете ли Вы вычислить, почему мы не строим больше ферм, когда мы вот-вот начнём испытывать недостаток пищи? Может быть мы запутались с тем, как много еды мы используем? Может быть, Вы могли бы попытаться уладить эту проблему сами, прежде чем я покажу решение её? (НАМЕК: Есть проблема в одном из условий).
Прежде, чем мы сделаем код больше, подобно созданию других типов зданий, готовящих другие типы военных подразделений, может быть мы могли бы даже позволить подразделениям напасть на что-то? Почему бы нет, но это, вероятно, тяжело и выходит из области этого основного руководства.
Прежде чем попытаться узнать, если это - трудно, самое время познать немного больше о Jass. Пора узнать полезность от присутствия некоторых функций. Это, может быть, даже время, чтобы узнать, где все те странные функции, которые мы использовали до этого, и как они действительно работают. Не уходите.

6. Больше теории Jass’a: управление действиями, условия и циклы

Управление действиями - о том, как сделать возможным ходить разными путями в программе, {например, как устанавливать порядок действий. Можно создать множество действий, а потом сделать их последовательными благодаря условиям и циклам, а можно использовать это и в других целях...} Jass поддерживает это со своим if и циклическими конструкциями. Так давайте бегло ознакомимся с ними.
{Примечание: данная часть статьи была переведена лишь потому, что здесь можно найти некоторые интересные примеры использования циклов при создании AI-скриптов.}
if
Существует несколько вариантов того, как может быть использован if. Но основная идея в том, что если что-то - истина, тогда мы сделаем какие-то действия, в противном случае мы сделаем нечто другое (или может быть вообще ничего не сделаем).
Код:
if condition then
    doSomething
    doSomethingMore
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у нас меньше 12 крестьян,
    call setProduce( 1, 'hpea', 0)       // Строить больше одного крестьянина (в нашей главной базе)
endif


Эй, а если мы захотим сделать что-то другое, если будем иметь всех крестьян?

Код:
if condition then
    doSomething
else
    doSomethingElse
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call setProduce( 1, 'hpea', 0) Строить больше одного крестьянина (в нашей главной базе)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
endif


Это хорошо, если есть только две возможности, но что если нам нужно выбрать между тремя отдельными действиями?

Код:
if condition then
    doSomething
elseif condition2
    doSomethingElse
else
    doSomeOtherThing
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Построить одного крестьянина (в начальном городе)
elseif GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif


Путём добавления большего количества elseif’ов Вы можете сделать программу выбирающую между несколькими различными действиями. Просто убедитесь, что Вы поместили ваши условия в соответствующее место. Следующий скрипт неудачен:

Код:
if GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
elseif GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Построить одного крестьянина (в начальном городе)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif


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

Loop

Циклы - это повторяющаяся много раз вещь. Если вы использовали циклы в других языках, Вы можете ощутить неудобство с циклами Jass'а. Всё же есть способ реализации циклов while и for в Jass.

Давайте сначала посмотрим на цикл, который будет запускаться вечно.

Код:
loop
    doSomethingForever
endloop

loop
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user forever")
    call Sleep( 1.0 )    // Он не будет вечным без спячки, потому что движок
// убьёт нас за ненадёжность
Endloop


Циклы, которые запускаются навсегда, используются, но они не очень гибкие. Так давайте введем строки, чтобы выйти из цикла.
Код:
loop
    doSomething
    exitwhen condition
    doSomethingMore
endloop

local integer i = 1
loop
    call Sleep( 1.0 )    // Он не будет вечным без спячки, потому что движок
// убьёт нас за ненадёжность
    exitwhen i > 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user 10 times")
    set i = i + 1
endloop


for
Мы не имеем циклы for в Jass, но мы можем сделать цикл, который будет вести себя подобно циклу for.
Код:
for ( initStatement; condition; endOfIterationStatement ) {
    stuff;    
}

может быть сделан на Jass как
Код:
initStatement
loop
    exitwhen not (condition) 
    stuff  
    endOfIterationStatement  
endloop

А теперь давайте посмотрим на более конкретный пример.
Код:
for (int i = 0; i < 10; i++) {
    printf("We need to say it 10 times");
}

На Jass’e станет
Код:
local integer i = 0
loop
    exitwhen not ( i < 10 )   // выход из цикла, если i >= 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need to say it 10 times")
    set i = i + 1
endloop


while
Может быть, вам следует самому попытаться сделать это. Если вы не имеете обыкновение в использовать цикл while, просто проигнорируйте иго, Jass, во всяком случае, ничего не знает о нём.
7. Функции, которые мы использовали до этого

Эта часть о тех функциях, которые мы использовали до этого, о том, как они работают, что они делают, и почему мы хотим, чтобы они делали только это.
Для каждых функций, которые мы использовали, я укажу, как она объявлена в sourcefiles, объясню аргументы, что она делает и что возвращает.

native Sleep takes real seconds returns nothing
call Sleep( 3.5 ) // заставляет наш скрипт заснуть на 3,5 секунд
Если мы никогда не спим, движок получается анормальным и выводит скрипт из строя после некоторого времени.

constant native GetLocalPlayer takes nothing returns player
local Player p = GetLocalPlayer() // {создаёт локального игрока p, которого мы используем, чтобы выдать текст именно ему (смотрите ниже)}

native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello local player!" )
Нам нужно сообщить функции, что писать и кому. Изменяя нули на нечто другое, мы можем также контролировать, где текст появится на экране. Почувствуйте себя свободным для эксперимента, нули обычно работают всё же лучше. Это сообщение уйдет после некоторого времени. Время зависит от длины сообщения.

native DisplayTimedTextToPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 60, "Hello for 60 secs!" )
Также, как и выше, но теперь мы контролируем, как долго будет показываться текст.

native ClearHarvestAI takes nothing returns nothing
call ClearHarvestAI() // приказывает искусственному интеллекту, отвечающему за сбор, остановить сбор.
Это используется с двумя функциями ниже, чтобы контролировать, как много рабочих собирают золото и дерево.

native HarvestGold takes integer town, integer peons returns nothing
call HarvestGold( 0, 5 ) // Сообщает 5 рабочим начать сбор золота в городе 0, который является главным городом.
Если этот вызов сделан дважды, без ClearHarvestAI, 10 рабочих вместо 5 будут собирать золото.

native HarvestWood takes integer town, integer peons returns nothing
call HarvestWood( 0, 10 )
Работает точно также, как и выше, только вместо золота дерево.

native SetProduce takes integer qty, integer id, integer town returns boolean
local boolean success = SetProduce( 1, 'hpea', 0 ) // Попытается создать одного крестьянина в городе 0 (главный город).

call SetProduce( 1, 'hpea', 0 )
Я не полностью верю обратной величине, но это, может быть, из-за того, что иногда я немного скептичен.

native GetUnitCount takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Считает количество, крестьян, которое имеет AI-игрок.
Это величина включает и тех крестьян, кто в то время создаётся.

native GetUnitCountDone takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Считает количество, крестьян, которое имеет AI-игрок.
Это величина не включает тех крестьян, кто в то время создаётся.

Вы, вероятно, скажете, что всё это очень хорошо, но как я могу построить или сосчитать другие вещи? Мы скоро рассмотрим, какие секретные величины использованы, чтобы создавать другие вещи.

8. Когда нам следует что-то построить [код для добавления ]


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

Самый простой путь заставить AI создать здания будет только лишь позволить ему попытаться это сделать, игнорируя все зависимости. Если мы поместили бы все приказы строительства в цикле, это в конечном счете построит всё, что мы приказали бы. Но он всё же не будет работать очень хорошо, мы получим несколько зданий, подобно 3 кузнецам. Как затем эта проблема может быть решена?

Если мы снова просмотрим часть четвёртую (Строительство ферм для поддержки большего количества рабочих) и пятую (Создание барака и подготовка нескольких солдат), то мы увидим, что есть несколько if'ов, условий, которые решают, что должно быть сделано. Мы должны быть способными создать несколько правил, которым должен следовать AI, чтобы построить хотя бы одно здание. Так, что мы ждём, давайте попробуем сделать это!

Правило 1: Если у нас мало крестьян, нам нужно тренировать больше крестьян.
Правило 2: Если у нас мало еды, нам нужно строить больше ферм.
Правило 3: Если у нас нет казарм, мы должны построить казармы.
Правило 4: Если у нас есть казармы, то мы должны тренировать пехотинцев.
Правило 5: Если у нас есть казармы, мы должны построить кузницу.
Правило 6: Если у нас нет алтаря, то мы должны построить алтарь.
Правило 7: Если у нас есть алтарь, но нет героя, мы должны создать героя.

Давайте посмотрим на эти правила, которые, надо надеяться, дадут нам армию пехотинцев и стрелков, лидируемых героем. Но почему Вы не подумали, как будете преобразовывать их в Jass, прежде, чем Вы прочитали? (Часть шестая, содержащая if-условия, может помочь.)

Эх, ленитесь попытаться сделать что-то своё? Хорошо, я проведу вас через несколько из них.
Давайте посмотрим, как мы можем реализовать правило первое на Jass, Оно будет похожим на это в условии if:
Код:
if "we have to few peasant" then 
  "train more peasants"
endif

Что делает few mean, и как много more? Давайте изменим правила вот так:
Rule 1b: Если у нас меньше 10 крестьян, начинаем тренировать одного крестьянина.
И попробуем преобразовать новое правило в Jass:
Код:
if GetUnitCount('hpea') < 10 then 
  call SetProduce( 1, 'hpea', 0 ) // 0 - это наш стартовый город
Endif

Мило!

Так давайте сделаем то же самое с другими правилами. Я укажу преобразованные правила и код. Попробуйте это сами, перед тем, как я.

Правило 1b: Если мы имеем менее чем 10 крестьян, тогда начинаем тренировать 1 крестьянина.
Правило 2b: Если мы имеем менее чем 6 дополнительных единиц пищи, тогда начинаем строить 1 ферму.
Правило 3b: Если мы имеем менее чем 1 казарму, тогда начинаем строить ещё 1 казарму.
Правило 4b: Если у нас есть более, чем 0 казарм, тогда начинаем тренировать одного пехотинца.
Правило 5b: Если у нас есть более, чем 0 казарм и мы имеем меньше чем 1 кузнецу, тогда начинаем строить одну кузнецу.
Правило 6b: Если мы имеем менее чем 1 алтарь, тогда начинаем строить 1 алтарь.
Правило 7b: Если у нас есть более, чем 0 алтарей и мы имеем менее 1 героя, тогда начинаем готовить 1 героя.
Правило 8b: Если у нас есть более 0 правил, не конвертированных в Jass, тогда начните преобразовывать первое правило на Jass.

9. Функции: Создание и использование


Функции... чем они хороши, для чего, почему мы используем их? Хорошо... мы хотим, чтобы кто-нибудь еще написал много хороших функций для нас, чтобы вызывать их, так что мы не должны создавать их сами. И мы хотим создать несколько их самим себе, так что мы можем использовать свой код, отличный от других. Разделяй и властвуй . Поделите ваш код в несколько функциий, и затем завоевывайте каждую функцию.

Давайте посмотрим на самую маленькую функцию:
Код:
function myFunc takes nothing returns nothing
endfunction

Эта функция совсем ничего не делает и ничего не даёт. Тем не менее, она содержит несколько частей, которые каждая функция должна иметь. Она начинается со слова function и заканчивается словом endfunction. После слова function идёт название функции, myFunc в данном случае. Следующим идёт слово takes и список всех аргументов, которые функция принимает. Эта функция ничего не принимает, {говоря иными словами, ей не нужно что-либо сообщать}. Она также ничего не возвращает. Что все это дает нам? Это дает нам функцию с именем myFunc, которое не берет никакие входные аргументы/параметры и не имеет обратную значение.

Давайте теперь попытаемся сделать функцию, которая делает что-то, принимает какие-либо аргументы и возвращает значение.
Код:
function add takes integer a, integer b returns // sum of a и b
  return a + b
endfunction


Сразу видно, что она делает, да? И не очень умно, мы могли бы просто использовать "+" в начале без функции. Давайте сделаем более полезную функцию, ту, которая убеждается, что все крестьяне убирают что-то.

Код:
function resourceManager takes goldPeasants returns nothing
  call ClearHarvestAI()   // Сначала перезапускаем менеджер сбора, {то есть останавливаем сбор и начинаем новый} и
  call HarvestGold( 0, goldPeasants )   // затем сообщаем имеющимся работникам собирать золото.
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )   // и остальным собирать дерево.
Endfunctions

Эта функция берёт, как аргумент, как много крестьян, которым следует собирать золото (остальные собирают дерево). Она может быть вызвана из другой функции, похожей на это:
Код:
call resourceManager( 5 )

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

Код:
function resourceManager takes goldPeasants returns nothing
  call ClearHarvestAI()   // Сначала перезапускаем менеджер сбора, {то есть останавливаем сбор и начинаем новый} и
  call HarvestGold( 0, goldPeasants )   // затем сообщаем имеющимся работникам собирать золото.
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )   // и остальным собирать дерево.
endfunctions

function main takes nothing returns nothing
    call Sleep( 1.0 )  // Ждать одну секунду
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script started" )

    loop   // начало вечного цикла
        call resourceManager( 4 )  // 4 крестьян в шахте, а остальные в лесу.
        call Sleep( 1.0 )  // Если мы никогда не спим, такой движок может убить нас.
    endloop

    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Script exiting" )
endfunction


Убедитесь, что полезная функция, та, которая быть названа, находится перед той, которая вызывает её. В противном случае WarCraft III думает вроде этого: “Что это за странная функция? Никогда не слышал об этом. Я лучше убью этот сценарий, он выглядит, как повреждённый.”

Другая полезная функция:
Код:
function print takes string message returns nothing
  call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 3600, message )
endfunction


Хорошо, хорошо, скажете Вы... но эта функция не создаст для нас армию. Хорошо... скоро мы создадим несколько функций, которые помогут нам создать большую армию. Терпение, пожалуйста.
Сценарий для борьбы

Давайте сначала перезапишем код из части пятой - создание барака и подготовка нескольких солдат.

Чтобы увеличивать удобность для чтения и сделать управление кодом легче, сценарий разбит в другие разделы, которые оперируют ресурсами, подготовкой юнитов и строительством зданий.

Код:
function resourceManager takes nothing returns nothing 
  local integer goldPeasants = 5
  local integer peasantsDone = GetUnitCountDone('hpea')
  if peasantsDone < 6 then
    set goldPeasants = peasantsDone - 1
  endif

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In manager")
  call ClearHarvestAI() // Сначала перезапускаем менеджер сбора, {то есть останавливаем сбор и начинаем новый} и
  call HarvestGold( 0, goldPeasants ) // сообщаем
                           // рабочим группы goldPeasants собирать золото   
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
                      // и остальным собирать дерево. 
endfunction 

function unitManager takes nothing returns nothing 
  if GetUnitCount('hpea') < 12 then
    call SetProduce(1, 'hpea', -1) // Тренировать одного крестьянина, где возможно
  endif
  call SetProduce(1, 'hfoo', -1) // Тренировать одного пехотинца, где возможно
endfunction 

function buildingManager takes nothing returns nothing 
  local integer produced = 12 + 6 * GetUnitCount('hhou') 
  local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
  local integer foodSpace = produced - used 
  local integer barrack = GetUnitCount('hbar')

  if foodSpace < 6 then 
    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"Building house") 
    call SetProduce(1,'hhou',-1) 
  endif

  if barrack < 1 then
    call SetProduce(1,'hbar',-1)     
  endif
endfunction 

function main takes nothing returns nothing 
  call Sleep( 1.0 )  // Sleep 1 second 
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started") 

  loop   // начало вечного цикла
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "in loop") 
    call resourceManager() 
    call unitManager() 
    call buildingManager() 
    call Sleep( 1.0 ) //Если мы никогда не спим, такой движок может убить нас.      
  endloop 

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting") 
endfunction


Этот код произведет отряд пехотинцев, который будет просто стоять и ничего не делать. Но об этом скоро позаботимся... Давайте начнём с создания AI, что произвольно бродит карте со своими пехотинцами. Надо надеяться, он найдет что-то, чтобы убить... Мы должны скоро также рассмотреть как встроенный в AI скрипт решает проблемы обманом...

Код:
globals  // variables that can be reached from all functions in the AI script.
  unit scoutUnit = null
  real scoutX = 0
  real scoutY = 0
  real scoutHealth
endglobals
//==================================================  =======
//  resourceManager
//
//  Сбор ресурсов путём подачи команд крестьянам
//==================================================  =======
function resourceManager takes nothing returns nothing 
  local integer goldPeasants = 5
  local integer peasantsDone = GetUnitCountDone('hpea')
  if peasantsDone < 6 then
    set goldPeasants = peasantsDone - 1
  endif

//  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In resourceManager")
  call ClearHarvestAI() // Сначала перезапускаем менеджер сбора, {то есть останавливаем сбор и начинаем новый} и
  call HarvestGold( 0, goldPeasants ) // сообщаем
                           // рабочим группы goldPeasants собирать золото   
  call HarvestWood( 0, GetUnitCountDone('hpea') - goldPeasants )
                      // и остальным собирать дерево. 
endfunction 
//==================================================  =======
//  unitManager
//
//  Тренировка юнитов
//==================================================  =======
function unitManager takes nothing returns nothing 
  if GetUnitCount('hpea') < 12 then
    call SetProduce(1, 'hpea', -1) // Тренировать одного крестьянина, где возможно
  endif
  if GetUnitCount('hfoo') < 15 then
    call SetProduce(1, 'hfoo', -1) // Тренировать одного пехотинца, где возможно
  endif
endfunction 
//==================================================  =======
//  buildingManager
//
//  Строительство зданий
//==================================================  =======
function buildingManager takes nothing returns nothing 
  local integer produced = 12 + 6 * GetUnitCount('hhou') 
  local integer used = 1 * GetUnitCount('hpea') + 2 * GetUnitCount('hfoo')
  local integer foodSpace = produced - used 
  local integer barrack = GetUnitCount('hbar')

  if foodSpace < 6 then 
//    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"Building house") 
    call SetProduce(1,'hhou',-1) 
  endif

  if barrack < 2 then
    call SetProduce(1,'hbar',-1)     
  endif
endfunction 
//==================================================  =======
//  allocateScout
//
//  Выбор какого-либо юнита разведчиком, чтобы он разведывал тропы
//==================================================  =======
function allocateScout takes nothing returns nothing
  local group grp = CreateGroup()
  call GroupEnumUnitsOfType(grp, "footman", null)
  set scoutUnit = FirstOfGroup(grp)
  call RemoveGuardPosition(scoutUnit)
  set scoutX = GetUnitX(scoutUnit)
  set scoutY = GetUnitY(scoutUnit)
  set scoutHealth = GetUnitState(scoutUnit, UNIT_STATE_LIFE)
endfunction
//==================================================  =======
//  init
//
//  Инициализация скрипта
//==================================================  =======
function init takes nothing returns nothing
endfunction
//==================================================  =======
//  walk
//
//  Приказывает юниту u двигаться в какое-то место.
//==================================================  =======
function walk takes unit u, real distance returns nothing
  local real dir = GetUnitFacing(u) * 3.14 / 180
  local real x = GetUnitX(u) + distance * Cos(dir)
  local real y = GetUnitY(u) + distance * Sin(dir)
  call IssuePointOrder( u, "move", x, y)
endfunction
//==================================================  =======
//  walk
//
//  Приказывает юниту u двигаться в каком-либо направлении.
//==================================================  =======
function walkDir takes unit u, real distance, real direction returns nothing
  local real dir = direction * 3.14 / 180
  local real x = GetUnitX(u) + distance * Cos(dir)
  local real y = GetUnitY(u) + distance * Sin(dir)
  call IssuePointOrder( u, "move", x, y)
endfunction
//==================================================  =======
//  helpScout
//
//  Посылает юнитов, которые будут следовать по дорогам разведчиков
//==================================================  =======
function helpScout takes nothing returns nothing
  local group grp = CreateGroup()
  local unit u
  call GroupEnumUnitsOfType(grp, "footman", null)
  set u = FirstOfGroup(grp)
  loop
    exitwhen u == null
    if u != scoutUnit then
      call IssuePointOrder( u, "attack", scoutX, scoutY)    
    endif
    call GroupRemoveUnit(grp, u)
    set u = FirstOfGroup(grp)
  endloop
  call DestroyGroup(grp)
endfunction
//==================================================  =======
//  scout
//
//  Контролирование скаута, которые бродит по карте.
//==================================================  =======
function scout takes nothing returns nothing
  local real R = 1024
  local real x = GetUnitX(scoutUnit)
  local real y = GetUnitY(scoutUnit)

  if scoutX == x и scoutY == y then
//    call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,"== Bump ==")   
    call walkDir(scoutUnit, R, GetUnitFacing(scoutUnit) + GetRиomReal(90, 270))
  else
    set scoutX = x
    set scoutY = y
    call walk(scoutUnit, R)
  endif
endfunction
//==================================================  =======
//  combatManager
//
//  Контролирование армии, которая будет следовать за скаутом
//==================================================  =======
function combatManager takes nothing returns nothing
//  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "In combatManager")
  if scoutUnit == null or not UnitAlive(scoutUnit) then
    call allocateScout()
  endif
  if GetUnitCountDone('hfoo') > 4 и scoutUnit != null then
    call scout()
    call helpScout()
  endif
endfunction
//==================================================  =======
//  main
//
//  Главная функция, запускающая AI
//==================================================  =======
function main takes nothing returns nothing 
  call Sleep( 1.0 )  // Sleep 1 second 
  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script started") 
  call init()

  loop   // начало вечного цикла
//    call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "in loop") 
    call resourceManager() 
    call unitManager() 
    call buildingManager() 
    call combatManager()
    call Sleep( 5.0 ) //Если мы никогда не спим, такой движок может убить нас.      
  endloop 

  call DisplayTextToPlayer(GetLocalPlayer(), 0.0, 0.0, "Script exiting") 
endfunction


Попытайтесь создать несколько людей, борющихся друг с другом при помощи этого сценария…
Следующий код будет связан со сценарием AI, действующем на игрока 2.
Код:
local integer command = 42
local integer data = 6 * 9
call CommиAI(Player(1), commи, data)

Чтобы получать хранимую информацию из сценария AI, используйте следующее:
Код:
function ReadAICommи takes nothing returns nothing
  local integer commи
  local integer data
                                                                                            
  loop
    exitwhen commandsWaiting() > 0
    call Sleep(0.5)  // Если мы не спим, игровой движок может быть разрушен...
  endloop
                                                                                            
  set command = GetLastcommand()
  set data = GetLastData()
  call PopLastcommand()

  // do whatever with command и data. 
Endfunction


Это работает только для целых чисел, конечно...
Другие способы создать связь между Картой и сценарием AI включают в себя установку золота и лесоматериалов для некоторого игрока или установку собственной величины некоторому специфическому юниту. Это возможно также при использовании игрового кеша… не уверен, что это доступно из сценариев AI. Вот, собственно говоря, и всё.

Отредактировано Sargeras, 19.06.2007 в 09:13.
Старый 19.06.2007, 09:07
S

offline
Опыт: 43,833
Активность:
Труъ! Тянет на 2000 опыта :)
Слава Саргерасу, создателю первого внятного АИ! +)

Отредактировано Sasha, 19.06.2007 в 11:09.
Старый 19.06.2007, 09:38
Sargeras
Лидер "Двух Королевств"
offline
Опыт: 22,163
Активность:
Цитата:
Слава Саргерасу, создателю первого внятного АИ! +)

Спасибо за тёплые слова.)
Старый 19.06.2007, 09:44
FausT

offline
Опыт: 1,451
Активность:
Долго строчил? Зачет ето за размер, Мегазачет - статья рулезз (хотя еще и не чытал) сейчас скину на флешку, вдому прочитаю. Еще раз зачет!

Да у меня заказ - АИ для футменов. Если нету времени или лень написать, можешь кинуть мне в приват скороченой вариант.

Отредактировано Dead_knight, 20.06.2007 в 04:13.
Старый 19.06.2007, 13:11
Sargeras
Лидер "Двух Королевств"
offline
Опыт: 22,163
Активность:
FausT, на мой взгляд, в статье прекрасно написано и рассказано, как можно реализовать данный вид AI. набросайте условий, добавьте побольше юнитов и т.д. Сам я не смогу Вам помочь, потому что сейчас катастрофически не хватает времени из-за моего проекта. Спасибо за комментарии.
Старый 20.06.2007, 08:38
tysch_tysch
Работаем
offline
Опыт: отключен
Sargeras разместишь на сайте?
Старый 21.06.2007, 23:07
Sargeras
Лидер "Двух Королевств"
offline
Опыт: 22,163
Активность:
ZlaYa1000, да, конечно, считайте, что уже разместил)
Старый 22.06.2007, 08:30
PlayerDark
Coraline
offline
Опыт: 10,569
Активность:
Sargeras AI - шник ?
Старый 22.06.2007, 09:37

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 18:05.