-
-
-
-
-
Yaguar
- Генерал-полковник
-
-
- Сообщений: 78
- Зарегистрирован: 03 ноя 2011, 22:40
- Cпасибо сказано: 19
- Спасибо получено: 59 раз в 42 сообщениях
- Медали: 10
-
-
- Очков репутации: 20
-
-
Yaguar » 13 фев 2012, 14:12
Уроки от DarthVader:
Урок первый."Написание функций и их запуск".
На карте имеется автомобиль которому мы присвоили скриптовый номер 100 (как присваивать надеюсь не надо объяснять). Наша задача - заставить его перемещаться в определенную точку в определенное время. Простейший скрипт данного перемещения (да и вообще просто простейший скрипт):
function Init()
RunScript("MoveCar", 3000);
end;
function MoveCar()
Cmd(0, 100, 916, 1516);
Suicide();
end;
Начнем по порядку. Функция - это небольшой блок/подпрограмма выполняющая определенное действие. В данном скрипте изображено две функции: Init и MoveCar. Функция всегда начинается с ее объявления словом function, и заканчивается оператором End. Все что между ними - является телом функции. Правописание как вы уже догадались следующее:
function <имя ф-ции>() - после function обязательный пробел, перед () пробела нет, после () точка с запятой не ставится. Точка с запятой ставится после End, при чем обязательно.
Точка с запятой обычно обозначает окончание чего то, будь то функция, или оператор, или команда. Так же для удобства (хотя это не обязательно) функции еще отделяют друг от друга пустой строкой, что бы при проверке было легче читать и текст не слипался в бесконечный непрерывный поток строк.
Рассмотрим теперь обе функции поочередно. Функция Init - это функция запуска. Она неотемлемая часть любого скрипта, так как все остальные функции скрипта необходимо вызывать. Для этого и служит Init. Она запускается автоматически в самом начале, и приводит в действие (вызывает) остальные функции скрипта. Без Инита скрипт работать не будет. Ф-ция Init срабатывает только один раз. Она как стартер в автомобиле.
Как мы видим, тело функции содержит только одну команду/оператор (в данном примере):
RunScript("MoveCar", 3000);
Операторы, в отличии от функций заканчиваются точкой с запятой, без End-а; функция может содержать в себе любое кол-во операторов.
В ворд-файле 18_Lua есть подробное описание всех команд/операторов, используемых при написании скриптов к блицу. Как видим:
RunScript (“Name”, iPeriodicity , [iNumberOfRepetitions])
Запускает сценарную функцию с именем Name каждые iPeriodicity миллисекунд. Необязательным параметром iNumberOfRepetitions можно указать число повторений вызовов.
То есть, применительно к нашему случаю оператор RunScript должен запускать функцию MoveCar каждые 3 секунды. Однако сдесь есть нюанс. Оператор будет срабатывать каждые 3 секунды только в том случае, если функция (в которой он находится) будет постоянно активна (включена). А так как функция Init срабатывает только один раз (только в первый цикл) и сразу отключается, то следовательно оператор RunScript сработает только один раз (через 3 секунды после запуска ф-ции Init).
Сразу оговорюсь, что один цикл - это полное прочтение программой (игрой) содержания скрипта от верха до низа (программа читает скрипт построчно). Игра повторяет цикл постоянно в течении всей миссии. В зависимости от "длинны скрипта" время цикла может немного колебаться, однако это все равно будут миллисекунды.
Что то я немного ушел в сторону (хотя информация тоже нужная). Итак, мы видим, что при запуске миссии ф-ция Init по средству оператора RunScript запустила ф-цию MoveCar с задержкой 3 секунды. Все. Ф-ция Init выполнила свое предназначение, и пока ее можно больше не вспоминать.
Перейдем ко второй функции - MoveCar:
function MoveCar()
Cmd(0, 100, 916, 1516);
Suicide();
end;
Как видим, она содержит два оператора. Каждый из операторов обязательно заканчивается точкой с запятой и начинается с новой строки! Операторы выполняются в порядке очередности один за другим. Синтаксис операторов можно посмотреть в ворд-файле 18_Lua.
Оператор Cmd:
GiveCommand (iAction, iScriptID [, params ...])
Дает команду iAction юниту со сценарной группой iScriptID. Необходимые параметры params должны быть перечислены через запятую. Cmd — синоним GiveCommand. Команда отдается юниту сразу же. См. значения команд в разделе «Команды юнитов» (в ворд файле 18_Lua). Все координаты выдаются в сценарных (SCRIPT) точках.
То есть в момент срабатывания ф-ции MoveCar, а следовательно и оператора Cmd, будет дана команда 0-перемещаться юниту (или юнитам) со скриптовым номером 100 в точку с координатами х=916, у=1516.
Далее идет оператор Suicide(); Если Suicide вызывается в теле функции, вызванной через RunScript, то после её выполнения функция больше не вызовется. То есть после срабатывания этого оператора в последующих циклах функция будет уже отключена. Она останется отключенной либо до окончания миссии, либо до момента, когда эта функция вновь будет вызвана с помощью любой другой активной функции по средству оператора RunScript.
В основном Suicide нужна для того, что бы юнит получил команду только один раз, а не получал ее с каждым новым циклом. То есть что бы ф-ция сработала, и сразу же отключилась.
Ну вот собственно и все. Теперь, когда мы все надеюсь поняли, опишем вышесказанное в трех строчках:
1) При запуске миссии срабатывает Init, который запускает ф-цию MoveCar через 3 сек.
2) По истечению 3 секунд запускается ф-ция MoveCar которая отдает команду машинке "100" двигаться в точку 916-1516.
3) Машинка отправилась в путь, а функция завершила свою работу и более не активна.
Пока все. Можете самостоятельно попробовать разобраться в нескольких следующих уроках.
Урок второй. Добавление второй функции с последовательным набором команд.
Как и в первом уроке, у нас есть авто со скриптовым номером 100. Но теперь на карту добавлен грузовик под номером 101. Авто действует по тому же принципу, что и в уроке-1. Грузовик немного иначе. Ниже приведен скрипт второго урока:
function Init()
RunScript("MoveCar", 3000);
RunScript("MoveTruck", 7000);
end;
function MoveCar()
Cmd(0, 100, 916, 1516);
Suicide();
end;
function MoveTruck()
Cmd(0, 101, 1583, 223);
QCmd(0, 101, 1890, 1570);
QCmd(0, 101, 1100, 1890);
Suicide();
end;
Как видим, это тот же скрипт, что был рассмотрен в первом уроке, но в него добавлена новая функция – MoveTruck. Эта функция, как и ф-ция MoveCar, запускается через Init. С момента старта миссии ф-ция MoveCar запускается через 3 секунды, а MoveTruck – через 7 секунд. Как видим, обе функции запускаются не зависимо друг от друга (только с разной временной задержкой), и поэтому будут работать параллельно.
Рассмотрим новую ф-цию MoveTruck. Она очень похожа на ф-цию MoveCar, но имеет больше операторов. С оператором Cmd мы уже знакомы, а вот QCmd – необходимо рассмотреть.
Из справочника 18_Lua:
GiveQCommand (iAction, iScriptID ,[ params ...])
Добавляет команду iAction в список команд юниту со сценарной группой iScriptID. Необходимые параметры params должны быть перечислены через запятую. QCmd — синоним GiveQCommand. Функция GiveQCommand может вызываться «пачкой», например, для составления промежуточных точек пути, по которому будет перемещаться юнит. В начале такой пачки должна быть функция GiveCommand, чтобы команда отдалась юниту сразу же. См. значения команд в разделе «Команды юнитов». Все координаты выдаются в сценарных (SCRIPT) точках. С помощью GiveQCommand (QCmd) можно добавлять команды в пул стартовых команд юнитов.
Говоря другими словами, с помощью оператора QCmd, юниту можно добавить любое число (любых доступных этому юниту) последовательных команд, но только после того, как был использован оператор Cmd. Эти команды будут выполняться подобно тому, как если бы вы отдавали приказы в игре, зажав клавишу Шифт. То есть – последующая команда начнет выполняться только после того, как будет полностью выполнена предыдущая.
Сдесь я сразу же оговорюсь о некоторых тонкостях выполнения последовательных команд:
1) Если предыдущая команда невыполнима в принципе, то юнит переходит к выполнению следующей команды, не нарушая таким образом правильной работы скрипта.
Например:
Cmd(7, 200); - пехоте с номером 200 выдана команда «7»-покинуть здание
QCmd(0, 200, 1890, 1570); - пехоте добавлена команда «0»-перемещаться в точку (1890, 1570).
Может возникнуть ситуация, когда здание будет разрушено раньше времени, но пехота успеет выскочить из него и выжить. В таком случае, когда сработает соответствующая ф-ция с выше указанным набором команд, возникнет ситуация, при которой пехоте 200 уже неоткуда будет выходить (пехота уже на «улице»). В подобных случаях игра просто переходит к следующей команде в списке.
2) Предыдущая команда невыполнима физически, но выполнима в принципе. Такая ситуация приводит к «подвисанию» юнита (не скрипта) и прерыванию всей последовательности команд. Сейчас поясню, что это значит.
Например:
Cmd(0,200,1000,2000);
QCmd(0, 200, 3000, 4000);
В этом кусочке скрипта юниту 200 дана стартовая команда двигаться в точку (1000,2000). При этом юнит, к примеру, обязательно пересечет реку через деревянный мост. Но мост, оказался взорванным раньше времени. Юнит, не находя пути к заданной точке следования, практически неизбежно застопорится где-нибудь на карте. И даже если мост в дальнейшем будет починен, юнит уже не будет продолжать выполнять поставленную последовательность команд. Я думаю у всех случалось подобное в игре, когда юнит (например вследствии пробки на мосту) уезжал куда то в сторону вдоль реки (пытаясь найти обходной путь), а потом просто замирал у какого то края карты. И разшевелить его можно было, только выдав новую команду. Так же и сдесь.
3) Если в процессе выполнения последовательности команд, юниту из какой либо другой ф-ции будет выдана новая команда по средству оператора Cmd, то эта старая последовательность прервется и немедленно начнется выполнение новой команды. Другими словами оператор Cmd более приоритетный чем оператор QCmd.
4) Не забывайте «отключать» ф-цию с помощью оператора Suicide по окончанию ее работы, так как это может привести к ее зацикливанию. Применительно к ф-ции MoveTruck – можно сказать, что если бы у нее в конце не стоял оператор Suicide, то эта ф-ция запускалась бы каждые 7 секунд (см. Init), и каждые 7 секунд юнит 101 получал бы одну и ту же команду: Cmd(0, 101, 1583, 223);
Итак, разобравшись с принципом работы операторов Cmd и QCmd мы теперь видим, что при запуске ф-ции MoveTruck, грузовик 101 получает последовательную команду – двигаться по трем точкам.
Теперь давайте рассмотрим немного другой вариант скрипта урока-2, в котором ф-ции MoveCar и MoveTruck остаются практически теми же, но запускаются не параллельно, а последовательно. Текст скрипта приведен ниже:
function Init()
RunScript("MoveCar", 3000);
end;
function MoveCar()
Cmd(0, 100, 916, 1516);
RunScript("MoveTruck", 4000);
Suicide();
end;
function MoveTruck()
Cmd(0, 101, 1583, 223);
QCmd(0, 101, 1890, 1570);
QCmd(0, 101, 1100, 1890);
Suicide();
end;
Как видим сдесь Init запускает только одну ф-цию – MoveCar. Ф-ция MoveTruck запускается из тела ф-ции MoveCar. То есть, пока полностью! не сработает ф-ция MoveCar (так как оператор RunScript стоит в конце), ф-ция MoveTruck не будет запущена. Как видим, MoveTruck в итоге так же запускается через 3+4=7 секунд (в действительности на пару миллисекунд позже).
Данный порядок вызова функций более удобный при составлении скриптов, так как позволяет точно согласовывать по времени запуск очередных функций с происходящим на карте. А так же знание данного порядка вызова будет необходимо при изучении следующего (и самого незаменимого!) оператора if then else, проверяющего выполнение некоего заданного условия. Но уже в следующем уроке.
Разбор полетов Basic Lua Sessions.
Урок третий. Проверка выполнения заданного условия.
Откройте в редакторе карт учебную карту Lesson3.bzm, которая хранится в папке Basic Lua Sessions. В принципе, для полного понимания каждого из учебных скриптов, вы должны открывать в редакторе карту, соответствующую уроку. Если вы не делали этого на прошлых уроках, то советую вам это сделать.
На карте Lesson3.bzm (как и на предыдущей Lesson2) размещены - легковое авто с номером 100, грузовик с номером 101, дом (ему присвоен номер 10), и несколько мелких объектов. Как видим, на карте появилось два новых юнита – пехота с номером 103, и пехота с номеом 104. Оба этих взвода объединены в скриптовое подкрепление номер 103. Список подкреплений можно увидеть в свитке Reinforcement Group (на рабочей панели слева) в окошке Groups. Если выделить интересующее подкрепление, то в окошке Group Propertie появится список всех скриптовых групп, входящих в скриптовое подкрепление. При составлении скриптов не путайте скриптовые номера групп юнитов со скриптовыми номерами подкреплений! Так же на карте появилась скриптовая зона - Farm, обозначенная зеленой рамкой. Список скриптовых зон можно увидеть в свитке Map Tools.
Ниже мы рассмотрим скрипт урока-3. Он выполняет следующие действия:
1) Авто 100, и грузовик 101 двигаются к дому (урок 1-2);
2) Как только авто и грузовик оба окажутся возле дома, при чем внутри скриптовой зоны Farm, на карту высаживается подкрепление 103;
3) Пехота движется к дому. Один взвод размещается возле дома, а второй заходит в дом.
Текст скрипта:
function Init()
RunScript("MoveCar", 3000);
RunScript("FarmCheck", 2000);
end;
function MoveCar()
Cmd(0, 100, 916, 1516);
RunScript("MoveTruck", 4000);
Suicide();
end;
function MoveTruck()
Cmd(0, 101, 1583, 223);
QCmd(0, 101, 1890, 1570);
QCmd(0, 101, 1100, 1890);
Suicide();
end;
function FarmCheck()
if GetNUnitsInArea(0, "Farm") == 2 then
RunScript("SquadArrive", 1000);
Suicide();
end;
end;
function SquadArrive()
LandReinforcement(103);
RunScript("SquadMove", 2000);
Suicide();
end;
function SquadMove()
Cmd(0, 103, 1530, 1320);
Cmd(6, 104, 10);
Suicide();
end;
Как видим часть скрипта идентична уроку-2. Рассмотрим новые функции. Начнем по порядку. В Init у нас запускается ф-ция FarmCheck через 2 секунды. Эта функция содержит новый (и очень полезный оператор if). Для дальнейшей работы со скриптами его обязательно необходимо изучить.
Оператор IF THEN ELSE – оператор проверяющий выполнение заранее заданного условия. Он действует по следующему принципу:
IF (если выполняется) «УСЛОВИЕ»
THEN (тогда делай) «ДЕЙСТВИЕ-1»
ELSE (в противном случае делай) «ДЕЙСТВИЕ-2»
Если в операторе отсутствует часть ELSE, тогда в случае невыполнения условия, оператор IF бездействует до тех пор, пока условие не выполнится. Рассмотрим его на примере ф-ции FarmCheck.
function FarmCheck()
if GetNUnitsInArea(0, "Farm") == 2 then
RunScript("SquadArrive", 1000);
Suicide();
end;
end;
Следует читать оператор так: if (если) GetNUnitsInArea(0, "Farm") == 2 (количество юнитов игрока «0» в зоне "Farm" = 2) then (тогда) RunScript("SquadArrive", 1000) (запустить ф-цию "SquadArrive" через 1 сек.). Suicide – отвечает за то, что ф-ция FarmCheck отключится сразу же после срабатывания строки RunScript("SquadArrive", 1000);
Оператор IF THEN ELSE по сути является небольшой вспомогательной функцией внутри основной функции, поэтому его окончание, так же необходимо обозначать словом end; В ф-ции FarmCheck первый end обозначает окончание оператора IF, а второй end – окончание самой ф-ции FarmCheck.
Внимание! Если вы забудете закрыть оператор IF эндом, весь ваш скрипт будет нерабочим!
Синтаксис оператора IF THEN ELSE чем то похож на синтаксис ф-ций, за исключением отсутствия названия и наличия условия:
if (пробел) («условие», если большое то в скобках) (пробел) then (далее с новой строки)
(«действие» - набор операторов или команд, составляющие тело вспомогательной функции if) (далее с новой строки)
end; - окончание вспомогательной функции (оператора) if.
Синтаксис знаков, позволяющих задать условие:
А==B (А равно B, пишется двойной знак равенства!);
А>B, A<B, A>=B, A<=B – стандартные знаки неравенства
A~=B (A не равен В, перед равно стоит знак «тильда»)
Итак, разобравшись с оператором if, мы так же разобрались и с ф-цией FarmCheck. Мы видим, что как только в зоне Farm, количество юнитов игрока «0» будет равно 2, оператор RunScript запустит ф-цию SquadArrive. Ну а эти «два» юнита, как не сложно увидеть из скрипта, - легковой автомобиль и грузовик.
Ф-ция SquadArrive:
function SquadArrive()
LandReinforcement(103);
RunScript("SquadMove", 2000);
Suicide();
end;
Как видим она предельно проста, и содержит два оператора. Первый LandReinforcement(103) – мгновенно высаживает на карту скриптовое подкрепление под номером 103.
(Внимание! Как только вы назначите в редакторе карт скриптовое подкрепление и придатите ему определенное количество скриптовых групп, то все эти скриптовые группы будут отсутствовать на игровой карте до тех пор, пока вы не вызовите их с помощью оператора LandReinforcement в составе назначенного скриптового подкрепления! В тоже время в окне редактора карт они будут отображаться!).
Второй оператор RunScript("SquadMove", 2000) запускает ф-цию SquadMove через 2 секунды. Что делает ф-ция SquadMove, вы уже должны уметь разобраться сами.
Остановлюсь только на небольшом нюансе. Если вызываемое подкрепление достаточно велико, к примеру 50-100 юнитов, то игра может не успеть их высадить за несколько секунд. По этому, в случае, если вам необходимо сразу же после высадки выдать команды юнитам этого подкрепления, рекомендую запускать ф-цию с набором команд для этих юнитов не через 1-2 секунды, а не менее чем через 5-10 секунд. У вас просто может возникнуть ситуация, когда подкрепление еще будет «высаживаться», а ф-ция с командами уже сработает. В этом случае команду получит только та часть подкрепления, которая успела высадиться, остальная же будет бездействовать.
Добавление служебной функции для облегчения контроля за работой миссии (используется только на стадии тестирования).
function DebugView()
Password("Panzerklein");
DisplayTrace("Amazing! This really works");
ShowActiveScripts();
--God(0, 2);
Suicide();
end;
ф-ция DebugView содержит набор следующих служебных команд:
Password("Panzerklein") – активирует режим паролей;
DisplayTrace("Amazing! This really works") – стандартный оператор вывода текста на экран. В данном случае он нужен для того, что бы понять, работает скрипт или нет. Если в тексте скрипта нет синтаксических и/или орфографических ошибок, то при запуске миссии на экране появится строка Amazing! This really works. В противном случае – скрипт не работает, и требует тщательной проверки на ошибки (самая нудная часть работы картодела!!!!!!!!!!!)
ShowActiveScripts() – выводит через диалоговое окно список работающих на данный момент ф-ций скрипта. Диалоговое окно (или как оно там называется?) открывается клавишей «тильда» во время игры.
--God(0, 2); - режим «бога». Значение параметров в скобках смотрите в справочнике.
Два тире подряд в начале строки обозначают, что вся эта строка – всего лишь комментарий к программе (режим бога не включится). Комментарии никуда не выводится, и нужны только для пояснений или подписей внутри самого скрипта. Подобным образом, поставив в начале строки два тире, вы можете написать любую информацию, используя при этом любые символы. Программа автоматически пропускает (не читает) все строки, которые начинаются с двух тире. Если потребуется включить режим «бога», то для того что бы вышеуказанная строка --God(0, 2) стала активной, уберите оба тире.
При необходимости, можно ввести в ф-цию DebugView еще одну строку:
ChangeWarFog(N); - показывает зону видимости и юниты игрока N.
Соответственно, что бы вся ф-ция DebugView заработала, ее нужно запустить, к примеру через Init, добавив в него строку:
RunScript(“DebugView”,1000);
Примечание. В принципе, ф-цию DebugView можно и не вводить в состав скрипта при проверке. Но тогда все необходимые пароли вам прийдется вводить непосредственно в игре при каждом запуске вашей миссии.
Сценарная область — механизм, позволяющий обращаться к поименованной области карты из сценария. В частности, сценарий может проверять соблюдение или несоблюдение определенных условий в границах сценарной области.
Чтобы задать сценарную область:
откройте свиток Map Tools (Инструменты карты) на панели Workspace (Режим);
щелкните по радиокнопке Script Area Tool (Инструмент сценарной области);
выберите форму создаваемой сценарной области с помощью радиокнопок Rectangle (Прямоугольник) и Circle (Круг);
растяните области на карте, перемещая мышь при нажатой левой кнопке;
задайте имя сценарной области в появившемся окне.
Имя новой сценарной области появится в списке на панели Workspace (Режим).
Для удаления сценарной области выделите ее имя в списке на панели Workspace (Режим) и нажмите кнопку Delete (Удалить) над списком.
К примеру- мне надо вызвать штурмовики или же переместить группу юнитов (технику) при выполнения определенного условия в определенное место»
- «Придумываешь сам. Можешь назвать GitlersSturmovik, например.
Только функции заданий должны называться Objective0, Objective1,...Objective10 и т.д. Эти менять не стоит.»