[SourcePawn] Урок 1 - Основы языка (Часть 2)

Zanoza

Команда форума
Арбитр
Сообщения
472
Реакции
81
Баллы
28
<- Часть 1
  1. Функции
    Функция
    - фрагмент программного кода (подпрограмма), выполняющий определенные действия, к которому можно обратиться из другого места программы. После выполнения функции управление возвращается обратно в точку программы, где данная функция была вызвана.
    Прототип функции - объявление функции, не содержащее тела функции, но указывающее имя функции, типы аргументов и возвращаемый тип данных. В то время как определение функции описывает, что именно делает функция, прототип функции может восприниматься как описание её интерфейса.
    Функция состоит из
Типа функции
  • native: Прототип внешней функции, которая создана другими плагинами или расширениями
Код:
native bool IsValidClient(int iClient);
/*
Сообщаем компилятору что существует внешняя функция с возвращаемым типом данных bool, которой необходимо передать 1 аргумент типа int
*/
public: Внешняя функции, которая вызывается другими плагинами или расширениями
Код:
public void OnMapStart()    // Внешняя функция OnMapStart вызывается ядром самого sourcemod`а
{
    // код
}
normal: Нормальная функция, которая может быть вызвана только внутри текущего плагина (при этом слово normal не пишется)
Код:
void MyFunction()    // Внутренняя функция MyFunction
{
    // код
}
  • static: Тоже что и normal но функция доступна только в пределах текущего документа
  • stock: Тоже что и normal но если функция не вызывается нигде в плагине - компилятор не включает её в исполняемый файл (smx)
  • forward: Глобальное событие, вызываемое другими плагинами или расширениями. Является обратным вызовом и записывается только как прототип.
Код:
forward void MyFunction();    // Сообщаем компилятору что существует внешняя функция MyFunction пустого типа без аргументов
  • Типа данных функции
    Аналогично типам данных переменных, но добавлен еще 1 тип:
    • void - Пустой тип. Означает что функция не возвращает никакого значения.
  • Имени функции
    К имени функции применяются те же требования что и к имени переменной
  • Параметров и аргументов функции
    Параметры функции - это значения, которые должна принимать функция.
    Аргументы функции - это те значения, которые передают в функцию при её вызове.
Код:
public void OnMapStart()    // Функция без параметров
{
    MyFunc(аргумент1, аргумент1);    // Вызов функции, передаем аргументы
}

int MyFunc(параметр1, параметр2)    // Объявляем функцию, принимаем параметры
{
   // код
}
Параметры и аргументы у функции могут отсутствовать.
Код:
public void OnMapStart()    // Функция без параметров
{
    int a = 5;
    int b = AddNumbers(a, 9);    // Вызываем нашу функцию передавая её 2 аргумента
    // b сейчас равно 14
}

int AddNumbers(int param1, int param2)    // Объявляем функцию, которая имеет 2 параметра типа int. При этом называть их можно любым удобным именем.
{
    // param1 сейчас равно 5
    // param2 сейчас равно 9
    
    int iResult = param1+ param2; // Прибавляем наши числа и записываем это в переменную iResult
    return iResult;    // Возвращаем значение iResult как значение функции
}

Синтаксис объявления функции:
[Тип функции] <Тип данных функции> <Имя функции> ([параметр 1], [параметр 2], ...)

Есть 2 способа вызова функции:
  • Прямой вызов - Вы специально вызываете функцию в вашем коде.
Код:
public void OnMapStart()
{
    MyFunction();    // Вызываем нашу функцию
}

void MyFunction()    // Объявляем функцию
{
    // код
}
Обратный вызов - В этом случае вы передаете в функцию1 имя другой функции (функцию2) и по завершению исполнения фукнции1 будет вызваная функция2
Код:
public void OnMapStart()
{
    MyFunction1(MyFunction2);    // Вызываем нашу функцию MyFunction1, передавая как аргумент функцию MyFunction2
}

void MyFunction1(Function MyFunctionCallBack)    // Объявляем функцию MyFunction1
{
    // код
    Call_StartFunction(null, MyFunctionCallBack);        // Вызываем нашу функцию которую передали
    Call_Finish();
}

void MyFunction2()    // Объявляем функцию MyFunction2
{
    // код
}
Возврат (return)
Оператор return может быть 2-х видов
Типы данных
  • return; - Просто завершает выполнение функции и передает управление в ту часть кода, откуда функция была вызвана.
  • return значение; - Завершает выполнение функции и передает управление в ту часть кода, откуда функция была вызвана, при этом возвращая значение как значение функции.
Код:
public void OnMapStart()
{
    int b;
    // b сейчас равно 0
    b = AddNumbers(3, 2);    // Вызываем нашу функцию
    // Функция примет значение 5 и оно будет присвоено переменной b
    // b сейчас равно 5
}

int AddNumbers(int arg1, int arg2)
{
    int iResult = arg1 + arg2;
    return iResult;    // Возвращаем значение iResult как значение функции
    /*
    Либо можно записать так:
    return (arg1 + arg2);
    */
}
Код:
public void OnMapStart()
{
    int a = 3;
    if(IsValidNumber(a))
    {
        // Код
    }
}

bool IsValidNumber(int iNum)
{
    if(iNum > 0 && iNum < 10)    // Если число, которое передали в функцию больше 0 и меньше 10
    {
        return true;    // Вернем true
    }
    // Если условия выше выполнилось, то функция завершится и вернет true, а код ниже выполнен не будет
    return false;    // Вернем false
}
Для функций типа void return в конце функции не нужен, но может применяться return без значения для выхода из функции в нужный момент.

Функции не могут возвращать массивы (строки тоже массивы).

Значения по умолчанию
В SourcePawn можно задать значение по умолчанию для любых параметров функции. Это значит, что если параметр не передан в функцию - он принимает значение по умолчанию.
Код:
public void OnMapStart()
{
    int a = 3,
        b = 6;

    int c = MyFunc(a, b, true);        // Прибавление
    int d = MyFunc(a, b, false);    // Вычитание
    int e = MyFunc(a, b);            // Прибавление
}
/*
Функция MyFunc прибавляет или вычитает числа
1-й параметр это число 1
1-й параметр это число 2
2-й параметр определяет действие:
    true - прибавляем
    false - вычитаем
*/
int MyFunc(int iNum1, int iNum2, bool bAdd = true) // Если параметр bAdd не передан он будет принят как true
{
    if(bAdd)    // Если bAdd равен true
    {
        return (iNum1 + iNum2);    // Вернем сумму
    }
    else    // Если bAdd не равен true значит он равен false, так что проверять это нет смысла.
    {
        return (iNum1 - iNum2);    // Вернем разность
    }

    return 0;
}
Когда пропускаемый параметр не последний заменяем его нижним подчеркиванием _
Код:
public void OnMapStart()
{
    int a = 3,
        b = 6;

    int c = MyFunc(a, true, b);        // Прибавление
    int d = MyFunc(a, false, b);    // Вычитание
    int e = MyFunc(a, _, b);            // Прибавление
}

int MyFunc(int iNum1, bool bAdd = true, int iNum2) // Если параметр bAdd не передан он будет принят как true
{
    if(bAdd)    // Если bAdd равен true
    {
        return (iNum1 + iNum2);    // Вернем сумму
    }
    else    // Если bAdd не равен true значит он равен false, так что проверять это нет смысла.
    {
        return (iNum1 - iNum2);    // Вернем разность
    }

    return 0;
}
Код:
public void OnMapStart()
{
    int a = 3,
        b = 6;

    int c = AddNumbers(a, b);    // Передаем значения переменных a и b
    /*
    При этом мы передаем не сами переменные, а то значение, которое в них находится.
    Это значит что мы передаем числа 3 и 6.
    */
}

int AddNumbers(int iNum1, int iNum2) // Здесь создаются новые локальные переменные и они принимают значения, которые им были переданы
{
    // Здесь iNum1 это новая локальная переменная, которая равна 3, следовательно равна a но она не является переменной a.
    // Если здесь мы изменим её, например, так:
    //    iNum1 = 7;
    //    Она станет равна 7, но переменная a в функции OnMapStart по прежнему останется равна 3
    return (iNum1 + iNum2);
}
Часто возникает необходимость вернуть 2 значения. Это можно сделать передав функции параметры не по значению, а по ссылке.
Код:
public void OnMapStart()
{
    int a = 3,
        b = 6;

    int c;
    //     c == 0
    AddNumbers(a, b, c);    // Передаем значения переменных a и b и адрес переменной с
    //     c == 9
}

void AddNumbers(int iNum1, int iNum2, int &iResult) // & означает, что функция получает не значение переменной, а её адрес
{
    // Здесь переменная iResult это та же переменная что и c в функции OnMapStart;
    // Если мы её изменим здесь, то изменится и переменная c
    iResult = iNum1 + iNum2;
}
Массивы всегда передаются по ссылке, но символ & не ставится.
Код:
public void OnMapStart()
{
    int iArray[4] = {4, 5, 8, 1};

    MyFunc(iArray, 4);
    MyFunc2(iArray);
}

void MyFunc(int[] iArray, int iSize)    // Такая запись используется, если функция не знает точного размера массива, поэтому часто её нужно передать еще и размер массива
{
    // Код
}

void MyFunc2(int iArray[4])    // Такая запись используется, если функция всегда должна принимать массив указанного размера
{
    // Код
}
 

Zanoza

Команда форума
Арбитр
Сообщения
472
Реакции
81
Баллы
28
  1. Методы
    Метод
    - это функция, принадлежащая какому-то классу или объекту.
    Например, в SourcePawn есть стандартный класс Panel, который является дочерним от Handle.
    Класс Panel имеет специальные функции для работы с ним.
    Несколько их них:
    • Panel - Конструктор класса, создает объект класса Panel (Создает панель)
    • SetTitle - Добавляет заглавие в панель
    • DrawItem - Добавляет пункт в панель
    • DrawText - Добавляет текст в панель
    • Send - Отправляет панель игроку.
    • Эти функции и есть его методами.
      Более подробно о конкретных классах и их методах будет изложено в следующих уроках.
  2. Константы
    Константы
    - переменные, которые нельзя изменить после инициализации.
    Константы бывают:
    • Литеральные - Значение, записанное непосредственно в коде
Код:
public void OnMapStart()
{
    int a = 3;

    int c = AddNumbers(a, 4);    // 4 это литеральная константа
}

int AddNumbers(int iNum1, int iNum2)
{
    return (iNum1 + iNum2);
}
Именованные - Присвоение имени значению и последующая замена при компиляции
Код:
#define NUMBER    4

public void OnMapStart()
{
    int a = 3;

    int b = a + NUMBER;    // NUMBER будет заменено на 4
}
Перечисления - Для использования целочисленных констант можно использовать перечисления, в котором целочисленному значению сопоставляется определенное имя. По умолчанию первой константе задается значение 0, а другим значение на 1 большее, чем предыдущая константа.
Код:
enum
{
    NONE = -1,    // Первое значение -1
    SELF,        // == 0
    TEAM,        // == 1
    ALL,        // == 2
};

public void OnMapStart()
{
    int iMode = TEAM;    // Значение равно 1
}
Константы переменных - Это тип переменных, значение которым присваивается на этапе инициализации и его больше нельзя изменить.
Код:
static const int g_iGlobalVar = 7;    // Переменная g_iGlobalVar всегда будет равно 7 и её нельзя изменить
// static нужен при создании глобальных констант т.к. этого требует синтаксис

public void OnMapStart()
{
    const int iLocalVar = 9;    // Переменная iLocalVar всегда будет равно 9 и её нельзя изменить
}
Код:
public void OnMapStart()
{
    char sVar[] = "string";
    MyFunc(sVar);
}

void MyFunc(const char[] sString)    // Функция не знает, какой реальный размер переданной строки, но переменную нельзя изменить
{
    // Код
}

void MyFunc2(int iArray[4])    // Такая запись используется, если функция всегда должна принимать массив указанного размера
{
    // Код
}
Циклы
Циклы
- инструменты, которые позволяют многоразово выполнять блок кода.
Цикл состоит из условия выполнения и тела цикла.
Итерация - единичное выполнение тела цикла.
  • Цикл for - Используется, когда известно точное количество итераций
Код:
for (действие до начала цикла; условие продолжения цикла; действия в конце каждой итерации цикла)
{
    // Тело цикла
}
Частный случай:
Код:
for (счетчик = значение; счетчик < значение; шаг цикла)
{
    // Тело цикла
}
Код:
for (int i = 0; i < 5; i++)    // Будет выполнятся до тех пор, пока i меньше 5
{
    PrintToServer("i = %i;", i);
}
/*
int i = 0; - Здесь мы создали счетчик (переменная, которая ведет подсчет итераций)
i < 5; - Это условие, которое проверяет перед каждой итерацией (Условие продолжения)
i++ - Это действие, которое выполняется после каждой итерации
*/
Результат выполнения:
Код:
i = 0;
i = 1;
i = 2;
i = 3;
i = 4;
Код:
Вот реальный пример. Цикл по всем игрокам:
for (int i = 1; i <= MaxClients; ++i)
{
    // Начинаем с 1 т.к. индексы игроков могут быть от 1 до MaxClients (включительно, поэтому и <=), которое равно количеству слотов на сервере.
    // Сам сервер имеет индекс 0
    if(IsClientInGame(i)) // Проверяем на сервере ли игрок (т.е. занят ли этот индекс (слот) игроком)
    {
        // Эта проверка всегда должна идти первой
        // Тут мы точно знает что игрок на сервере и можем получать любую информацию о нем.
        // Например:
        if(IsPlayerAlive(i)) // Жив ли игрок
        {
            //
        }
        
        if(GetClientTeam(i) == 2) // Игрок играет за террористов
        {
            // Возможные команды игроков
            // 0 - Игрок только вошел на сервер и еще не выбрал команду
            // 1 - Команда наблюдателей
            // 2 - Команда террористов
            // 3 - Команда контр-террористов
        }

        if(IsFakeClient(i)) // Является ли игрок ботом
        {
            //
        }
    }
}
Цикл while - Когда мы не знаем, сколько итераций должен произвести цикл. Данный цикл будет выполняться, пока условие, является истиной
Код:
while (условие продолжения цикла)
{
    // Тело цикла
}
Код:
int i = 5;
while (i > 0)
{
    PrintToServer("i = %i;", i);
    i--;
}
Результат выполнения:
i = 5;
i = 4;
i = 3;
i = 2;
i = 1;
Цикл do while - То же что и while, но проверка условия выполняется уже после выполнения итерации. Это гарантирует хотя бы одно выполнение цикла
Код:
do
{
    // Тело цикла
} while (условие продолжения цикла)
Код:
int i = 5;
do
{
    // Тело цикла
    PrintToServer("i = %i;", i);
    i--;
} while (i > 0)
  • Операторы для работы с циклами
    • break; - Прекращает выполнение цикла и возвращает управление в функцию.
    • return [значение]; - Прекращает выполнение цикла и выходит из функции.
    • continue; - Пропускает выполнение текущей итерации цикла.
  • Ключевые слова
    Ключевые слова
    - это слова, которые зарезервированы самим SourcePawn и их нельзя использовать, как имена переменных и фукнций.
    К ним относятся if, for, return и другие.
    Если вы попытаетесь использовать ключевые слова как имена переменных и функций то получите ошибку:
    error 173: 'слово' is a newly reserved keyword that may be used in the future; use a different name as an identifier
  • Инструкции компилятора
    Инструкции компилятора
    - это инструкции, которые нужны для компилятора. После компиляции их нет в исполняемом файле.
    Инструкции компиляторавсегда начинаются с символа #
    • #include - Подключает указанный файл/библиотеку
      Использование:
      • #include <sourcemod> - Подключает библиотеку sourcemod из папки include, относительно расположения компилятора
      • #include "myfile.sp" - Подключает файл myfile.sp по пути относительно текущего файла
    • #tryinclude - То же что и #include но если файла не существует то ошибки не будет.
    • #define имя_макроса последовательность_символов - Создает макрос. В результате, если компилятор обнаружит в тексте программы имя_макроса, то он заменит его на последовательность_символов.
      Макрос заканчивается переносом строки. Для экранирования переносов строк в макросе используется символ \
    • #if условие - Выполняет проверку условия. Требуется закрывающая директива #endif. Возможно ветвление используя директивы #else и #elseif
    • #if defined имя_макроса - Проверяет объявлен ли макрос.
    • #error "Ошибка" - Выдает ошибку компиляции.
    • #pragma - Директива используется для доступа к специфическим расширениям компилятора.
      Использование:
      • #pragma semicolon 1 - Сообщает компилятору о том, что в конце каждого выражения должен стоять символ ;
      • #pragma newdecls required - Сообщает компилятору о том, что синтаксис плагина исключительно новый
      • #pragma deprecated - Устанавливает для переменной/функции статус устаревшей
      • #pragma dynamic количество_байт - Устанавливает динамичную память плагина
      • #pragma unused - Отключает предупреждение о то что переменная не используется
      • #pragma tabsize размер - Устанавливает значение TAB (0 - отключает)
 
Верх