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

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

 
Doc

offline
Опыт: 63,163
Активность:
[Конкурсная] Основы Galaxy.
Приветствую тебя, читатель! Сегодня я расскажу тебе об основах Galaxy - скриптового языка игры Starcraft II.
Предполагается, что ты уже знаком с основами программирования в редакторе триггеров, а если нет - вперед изучать остальные статьи!


Итак, пойдем по порядку:

Содержание:

  1. Функции
  2. Переменные
  3. Массивы
  4. Циклы
  5. Условия
  6. Практикум


Функции:


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

Синтаксис функции определяется следующим образом:


Код:
<ТИП ВОЗВРАЩАЕМОЕГО ЗНАЧЕНИЯ> <ИМЯ ФУНКЦИИ>(<АРГУМЕНТЫ ЧЕРЕЗ ЗАПЯТУЮ>){
...
<ТЕЛО ФУНКЦИИ>
...
}


Например:



Код:
void CreatePylon(){
UnitCreate(1, "Pylon", 0, 0, Point(0.0, 0.0), 0);
}


Вызывается она так:



Код:
CreatePylon();


Пожалуй, объясню что означает весь этот бред выше. Во-первых, все функции делятся на кастомные и нативные. Нативные функции - это те функции, которые непосредственно влияют на игровой процесс, совершая действия. Кастомные функции состоят из нативных чуть менее чем полностью и созданы они лишь для удобства, чтобы не повторять множество раз одни и те же действия. Тогда очевидно, что функция UnitCreate является нативной, а CreatePylon - кастомной.
Давайте же рассмотрим, что делает эта наша суперфункция! Когда мы вызываем ее, в левом нижнем углу карты создается пилон, принад лежащий нейтральному игроку. Итак, первый аргумент отвечает за количество создаваемых юнитов. Второй - за тип самого юнита, пишется в кавычках. Третий - за то, создастся ли юнит сразу же или будет произведена его постройка. Четвертый - за номер игрока, которому будет принадлежать юнит. Пятый - за точку создания юнита, как видите тут я использовал нативную функцию Point создающую точку в левом нижнем углу карты. Шестой - за угол поворота юнита.
Итак, мы разобрались в устройстве нашей функции. Теперь давайте дополним ее, а заодно и разберемся с принимаемыми и возвращаемыми значениями.
А что если сделать так, чтобы можно было задавать игрока-владельца созданного пилона? Давайте попробуем!

Код:
void CreatePylon(int PlayerOwner){
UnitCreate(1, "Pylon", 0, PlayerOwner, Point(0.0, 0.0), 0);
}


Теперь мы можем одной функцией создать несколько пилонов для разных игроков.

Вызываться она будет так:



Код:
CreatePylon(1);

Или


Код:
CreatePylon(2);


А давайте еще добавим выбор той точки, в которой будет создаваться наш пилон:

Код:
void CreatePylon(int PlayerOwner, point CreationPoint){
UnitCreate(1, "Pylon", 0, PlayerOwner, CreationPoint, 0);
}


А она вызываться будет уже так:


Код:
CreatePylon(1, Point(20, 20));

Или


Код:
CreatePylon(2, Point(15, 40));


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

Код:
unit CreatePylon(int PlayerOwner, point CreationPoint){
UnitCreate(1, "Pylon", 0, PlayerOwner, CreationPoint, 0);
return UnitLastCreated();
}


Использовать это можно так:



Код:
UnitKill(CreatePylon(1, Point(20, 20)));


Правда, результата это не принесет, ведь созданный юнит сразу же уничтожится функцией UnitKill. Но это лишь одно из множества применений. Заметьте, что return прерывает действия функции, то есть его можно использовать как искусственный прерыватель.
Вот я и поведал вам о функциях все, что хотел. Далее у нас переменные. Итак...

Переменные:



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

Разберем синтаксис объявления переменных:



Код:
<ТИП ПЕРЕМЕННОЙ> <ИМЯ ПЕРЕМЕННОЙ> = <ЗНАЧЕНИЕ ПЕРЕМЕННОЙ>;


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

Вот и пример:



Код:
unit u = CreatePylon(1, Point(0, 0))

Или


Код:
int i = 5 * 20 - 100


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

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

Массивы:


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

Объявляются массивы так:



Код:
<ТИП ПЕРЕМЕННОЙ>[<КОЛИЧЕСТВО ЯЧЕЕК В МАССИВЕ> - 1] <ИМЯ ПЕРЕМЕННОЙ>


Заметьте, что задать начальное значение нельзя.

Вот простенький пример использования массивов:



Код:
int[10] i;
int j = 0;
j = RandomInt(0, 10);
i[j] = j + 5;


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

Циклы:


Предположим, что нам нужно создать 10 пилонов функцией CreatePylon так, чтобы каждому пилону соответствовал свой игрок. Как бы вы сделали это?

Код:
CreatePylon(1, Point(0, 0));
CreatePylon(2, Point(0, 0));
CreatePylon(3, Point(0, 0));
CreatePylon(4, Point(0, 0));
CreatePylon(5, Point(0, 0));
... и т.д.


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

Синтаксис цикла выглядит так:



Код:
while (<УСЛОВИЕ>){
...
<ДЕЙСТВИЯ ЦКИЛА>
...
}


Вот как нужно решать вышеописанную задачку:



Код:
int i = 0;
while (i < 10){
CreatePylon(i, Point(0, 0));
i = i + 1;
}


Удобно, не правда ли? Приступим к условиям.

Условия:


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

Синтаксис условий выглядит примерно так:



Код:
if (<УСЛОВИЕ>) {
...
<ТЕЛО УСЛОВИЯ>
...
}


Хочу заметить, что внутри условий могут быть использованы такие операторы как:

Код:
== - равенство.
!= - неравенство.
> < - больше, меньше.
>= <= - больше или равно, меньше или равно.


Для соединения нескольких условий в одной строке могут быть использованы скобки и нижеследующие операторы:

Код:
|| - или.
&& - и.


Мы можем сравнить значения с true и false - истинностью и ложью.

Вот пожалуй и все, перейдем к последней части нашей статьи: практикуму.

Практикум:


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

Вроде бы просто, не так ли? Итак, приступим:

Для начала создадим в редакторе триггеров функцию и назовем ее, например, Custom code. Это будет наш аналог Кастом кода из Warcraft III. Создадим действие "Общее - Пользовательский скрипт". Щелкните на него и в открывшемся поле для ввода пишите:

Код:
return true;
}
            
void End(){


Это некоторое ухищрение(thx to ScorpioT1000) для того, чтобы создать пустое поле для кода. Все действия далее производятся между

Код:
return true;
}
            
void End(){


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

Код:
void InitCircle(int count, fixed dist){
    
}


count - количество создаваемых пилонов, dist - действительная переменная диаметра круга пилонов.

Внутри функции нам понадобится цикл, добавим его.

Код:
void InitCircle(int count, fixed dist){
            int i = 0;
            while (i < count) {
            
            i = i + 1;
            }
        }


Мы хотим чтобы пилоны создавались ровно по кругу, не так ли? Зададим рассчет угла, добавив еще одну переменную.

Код:
void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
            
            i = i + 1;
            }
        }


Теперь добавим еще одну переменную для задания точки создания.

Код:
void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);

            i = i + 1;
            }
        }


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

Код:
void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                i = i + 1;
            }
        }


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

Код:
unit[100] Pylon;
        int PylonCount;
        fixed PylonDist;
        trigger PylonMove

        void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                Pylon[i] = UnitLastCreated();
                i = i + 1;
            }
        }


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

Код:
unit[100] Pylon;
        int PylonCount;
        fixed PylonDist;
        trigger PylonMove

        bool PylonMoveFunc(bool testConds, bool runActions){
        if (!runActions) {
            return true;
        }
        
        return true;
        }

        void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                Pylon[i] = UnitLastCreated();
                i = i + 1;
            }
            PylonCount = count;
            PylonDist = dist;
        }


Поясню: любая функция, привязанная к триггеру выглядит именно таким образом как и PylonMoveFunc. Действия пишутся непосредственно после первого условия. Итак, повесим функцию на триггер и зарегистрируем на триггер периодическое событие.

Код:
unit[100] Pylon;
        int PylonCount;
        fixed PylonDist;
        trigger PylonMove = TriggerCreate("PylonMoveFunc");

        bool PylonMoveFunc(bool testConds, bool runActions){
        if (!runActions) {
            return true;
        }
        
        return true;
        }

        void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                Pylon[i] = UnitLastCreated();
                i = i + 1;
            }
            PylonCount = count;
            PylonDist = dist;
            TriggerAddEventTimePeriodic (PylonMove, 0.04, c_timeGame);
        }


Поясню: c_timeGame - константа игрового времени. Далее нам осталось лишь дописать тело двигающей функции.

Код:
unit[100] Pylon;
        int PylonCount;
        fixed PylonDist;
        trigger PylonMove = TriggerCreate("PylonMoveFunc");

        bool PylonMoveFunc(bool testConds, bool runActions){
        int i = 0;
        fixed ang;
        point p;
        if (!runActions) {
            return true;
        }
        while (i < PylonCount) {
            ang = AngleBetweenPoints(RegionGetCenter(RegionPlayableMap()), UnitGetPosition(Pylon[i])) + 2.0;
            p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), PylonDist, ang);
            UnitSetPosition(Pylon[i], p, true);
            i = i + 1;
        }
        return true;
        }

        void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                Pylon[i] = UnitLastCreated();
                i = i + 1;
            }
            PylonCount = count;
            PylonDist = dist;
            TriggerAddEventTimePeriodic (PylonMove, 0.04, c_timeGame);
        }


Готово! Итого мы получаем:

Код:
return true;
        }
            
        unit[100] Pylon;
        int PylonCount;
        fixed PylonDist;
        trigger PylonMove = TriggerCreate("PylonMoveFunc");

        bool PylonMoveFunc(bool testConds, bool runActions){
        int i = 0;
        fixed ang;
        point p;
        if (!runActions) {
            return true;
        }
        while (i < PylonCount) {
            ang = AngleBetweenPoints(RegionGetCenter(RegionPlayableMap()), UnitGetPosition(Pylon[i])) + 2.0;
            p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), PylonDist, ang);
            UnitSetPosition(Pylon[i], p, true);
            i = i + 1;
        }
        return true;
        }

        void InitCircle(int count, fixed dist){
            int i = 0;
            fixed ang;
            point p;
            while (i < count) {
                ang = (360.0 / IntToFixed(count)) * IntToFixed(i);
                p = PointWithOffsetPolar(RegionGetCenter(RegionPlayableMap()), dist, ang);
                UnitCreate(1, "Pylon", 0, 0, p, 0);
                Pylon[i] = UnitLastCreated();
                i = i + 1;
            }
            PylonCount = count;
            PylonDist = dist;
            TriggerAddEventTimePeriodic (PylonMove, 0.04, c_timeGame);
        }
        
        void End(){


Поздравляю тебя, читатель, и благодарю за прочтение моей статьи! Карту с вышеописанным кодом ты можешь найти чуть ниже.
Прикрепленные файлы
Тип файла: sc2map К статье.SC2Map (169.0 Кбайт, 33 просмотров )

Отредактировано Doc, 03.11.2010 в 22:35.
Старый 17.10.2010, 12:18
reALien

offline
Опыт: 29,211
Активность:
Из всех двух претендентов ты пока что победитор =P
Старый 17.10.2010, 13:30
H
hello world
offline
Опыт: 130,675
Активность:
хорошая годная статья
плохие ужасные примеры ^_^
В конце более менее, но все примеры можно сделать не использовав и строки кода.
Старый 23.10.2010, 01:57
Sergey
Старейший
offline
Опыт: 44,363
Активность:
Ahelhot
А другие статьи прокоментируешь?
Старый 23.10.2010, 18:59
H
hello world
offline
Опыт: 130,675
Активность:
Другие не интересные. омфг одни обзорные статьи =\
Старый 23.10.2010, 19:36
Doc

offline
Опыт: 63,163
Активность:
Ahelhot, лично мне кодом гораздо удобнее, так что кому как =) спасибо ок.
Старый 23.10.2010, 20:46
bee
vjass.optimizer
offline
Опыт: 16,615
Активность:
Давайте, доктор, жгите дальше. +1
Старый 23.10.2010, 20:53
H
hello world
offline
Опыт: 130,675
Активность:
лично мне кодом гораздо удобнее, так что кому как =) спасибо ок.
Ну статьи нужны только новичкам, а им как известно писать кодом не очень нравится =)
все равно годная статья, мне понравилась.
Старый 24.10.2010, 14:20
Mihahail
๏̯͡๏
offline
Опыт: 17,766
Активность:
Статья гут, единственное замечание: не по канонам ) Но получилось весьма не плохо
Про каноны. Если брать любой учебник, или самоучитель, или школьную(и вузовскую тоже) программу по программированию, то везде порядок следующий:
Переменные
Условия
Циклы
Массивы
Функции
Практикум
Кагбэ по увеличению сложности.
ps: <КОЛИЧЕСТВО ЯЧЕЕК В МАССИВЕ> неверно, нужно <КОЛИЧЕСТВО ЯЧЕЕК В МАССИВЕ МИНУС ОДИН> ибо счёт идёт с нуля
Старый 26.10.2010, 23:02
H
hello world
offline
Опыт: 130,675
Активность:
((цитата
<КОЛИЧЕСТВО ЯЧЕЕК В МАССИВЕ> неверно, нужно <КОЛИЧЕСТВО ЯЧЕЕК В МАССИВЕ МИНУС ОДИН> ибо счёт идёт с нуля
))
Шас на 70 годы что бы так горевать из за одной ячейки. Я лично никогда не делаю массив в 9 ячеек (0-9 т.е 10 переменных).
Просто оставляю 0 как свободную ячейку и счет массивов начинаю с 1. Так гораздо удобнее и понятнее.
Старый 26.10.2010, 23:15
Mihahail
๏̯͡๏
offline
Опыт: 17,766
Активность:
Ahelhot, это плохой стиль. Не будем дискутировать об этом.
И если человек изучает си на примере гкалакси, то зачем сразу приучать его к плохому стилю?
Вот и программируйте на дельфи.
Старый 26.10.2010, 23:37
Doc

offline
Опыт: 63,163
Активность:
Ок, это забыл. О канонах не знал. Статьи никогда не писал. Спасибо.
Старый 27.10.2010, 20:37
H
hello world
offline
Опыт: 130,675
Активность:
это плохой стиль. Не будем дискутировать об этом.
я как бы и сказал что мне пофиг, тут каждый делает как ему удобно, от одной переменной никому хуже не будет. Я кстати заметил что нулевая переменная почти всегда пригодится в другом деле, хотя и не было запланировано её юзать =)
Вообще можно в трактире на эту тему пофлудить. )
Старый 27.10.2010, 23:46
Mihahail
๏̯͡๏
offline
Опыт: 17,766
Активность:
Doc:
Ок, это забыл. О канонах не знал. Статьи никогда не писал. Спасибо.
Фикс будет?)
Старый 03.11.2010, 21:43
Doc

offline
Опыт: 63,163
Активность:
Mihahail, про колво ячеек fixed, спасибо.
Старый 03.11.2010, 22:36

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

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

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

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



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