[Tutorial] Система динамических чекпоинтов
#1

Всем привет. Допустим, что по некоторым причинам вас не удовлетворяют средства проверки, что игрок находится в какой-либо точке, например с помощью пикапов или проверок IsPlayerInRangeOfPoint. Или же вы просто хотите сделать чекпоинты как дополнение к уже существующим пикапам(в том смысле, чтобы чекпоинт появлялся, когда подходишь к какому-либо пикапу). Я расскажу, как сделать систему динамических чекпоинтов, а вы уже определите, как будете использовать её в своих проектах.

1 шаг. Создание основы. Структуры и функции.
Во-первых, нам нужна структура, которая будет содержать информацию о чекпоинте: позиция, радиус чекпоинта и дистанция, начиная с которой чекпоинт будет отображаться.
pawn Code:
enum CHECKPOINTINFO {
    Float: e_cpX, // позиция чекпоинта по X
    Float: e_cpY, // позиция чекпоинта по Y
    Float: e_cpZ, // по Z
    e_cpRad, // радиус(размер) чекпоинта
    e_cpDist // дистанция, начиная с которой чекпоинт будет отображаться
}
Далее нужно будет объявить массив чекпоинтов. Давайте сделаем два чекпоинта. Так, просто для теста.
pawn Code:
new dynamicCp[][CHECKPOINTINFO] = {
    {0.0, 0.0, 1.0, 3, 5},
    {2.0, 0.0, 1.0, 3, 5}
};
Мы сразу заполняем массив чекпоинтов dynamicCp нужной информацией(если вас не устраивает такой способ заполнения массива, вы можете сделать считывание основных параметров из файла). Давайте разберём строку {0.0, 0.0, 1.0, 3, 5}. Первые три цифры - это позиция чекпоинта, далее радиус и последнее - дистанция. По сути, эта строка указывает, что чекпоинт размера 3 будет создан в центре карты (0.0, 0.0, 1.0) и покажется игроку, только если расстояние между ними окажется меньше 5.
Объявите новый массив pDynCp[MAX_PLAYERS].
pawn Code:
new pDynCp[MAX_PLAYERS];
Назначение его в том, чтобы хранить индекс того чекпоинта, который в данный момент виден игроку. Мы будем использовать значения этого массива, чтобы определять в OnPlayerEnterCheckpoint, на какой чекпоинт зашёл игрок. Всё, что нужно сделать с данным массивом, это "обнулить" одну из его ячеек, когда какой-либо игрок присоединиться. (фактически, мы не обнуляем, а присваиваем -1, потому что индекс чекпоинта может быть и нулевым)
pawn Code:
public OnPlayerConnect(playerid)
{
    // какой-то ваш код
    pDynCp[playerid] = -1;
    return 1;
}
Далее. Создайте паблик, пока пустой.
pawn Code:
forward checkDynCp();
public checkDynCp()
{
    return 1;
}
Отлично, паблик создан. Последним на данном, подготовительном этапе шагом будет создание таймера. В OnGameModeInit перед return 1; поместите данный ниже код:
pawn Code:
SetTimer("checkDynCp", 1000, true);
Мы объявили таймер, суть которого будет объяснена ниже.
Поздравляю, вы закончили первый этап, давайте перейдём ко второму.

2. Непосредственно обработка чекпоинтов.

Не самый сложный код, но в данном мануале, пожалуй, самый основной.
Вернёмся к нашему паблику checkDynCp. Пропишите в нём следующее:
pawn Code:
forward checkDynCp();
public checkDynCp()
{
    static k, Float: x, Float: y, Float: z, dist, id;
       
    for (new j = 0; j < MAX_PLAYERS; j++) {
        if (!IsPlayerConnected(j)) continue;
       
        id = pDynCp[j];
        if (id != -1) {
            dist = dynamicCp[id][e_cpDist];
            if (!IsPlayerInRangeOfPoint(j, dist, dynamicCp[id][e_cpX], dynamicCp[id][e_cpY], dynamicCp[id][e_cpZ])) {
                pDynCp[j] = -1;
                DisablePlayerCheckpoint(j);
            }
        } else
            for (k = 0; k < sizeof(dynamicCp); k++) {
                dist = dynamicCp[k][e_cpDist];
                x = dynamicCp[k][e_cpX];
                y = dynamicCp[k][e_cpY];
                z = dynamicCp[k][e_cpZ];
                if (IsPlayerInRangeOfPoint(j, dist, x, y, z)) {
                    pDynCp[j] = k;
                    SetPlayerCheckpoint(j, x, y, z, dynamicCp[k][e_cpRad]);
                    break;
                }
            }
    }
}
Время разобраться, что же собственно говоря происходит в данном паблике.
В начале мы объявляем вспомогательные переменные, делаем их статическими (созданы они будут один раз и доступны только в пределах данного паблика).
Далее запускаем цикл по всем игрокам (если вы используете свои "оптимизированные" циклы только по подключенным игрокам, то можете изменить эту строку). Первая проверка - подключен ли игрок. Если нет, переходим к следующему игроку. Далее узнаём позицию игрока GetPlayerPos(j,x,y,z); В переменную ID заносим значение индекса чекпоинта, который виден в данный момент игроку. Далее проверяем, если индекс НЕ равен -1 (то есть игроку виден какой-либо чекпоинт в данный момент), то будем проверять, отошёл ли игрок от чекпоинта на достаточное расстояние(e_cpDist), чтобы скрыть чекпоинт. Если не отошёл, то чекпоинт так и будет виден, и нам не надо проверять другие чекпоинты (помните, что в GTA SAMP в один момент времени может существовать только ОДИН чекпоинт). Если же у нас id равен -1 (то есть игроку ещё не виден никакой чекпоинт), то мы проверим, какой чекпоинт ближе всего к игроку. Для этого пройдёмся по всем чекпоинтам и расчитаем расстояние от чекпоинта до игрока. Если расстояние меньше чем e_cpDist, то в массив pDynCp[playerid] заносим индекс найденного чекпоинта, устанавливаем на карте чекпоинт и прерываем цикл через break;.
Отлично! Мы написали основной код, перейдём теперь к следующему этапу.

3. Делаем что-либо, в зависимости от того, на каком чекпоинте игрок.

pawn Code:
public OnPlayerEnterCheckpoint(playerid)
{
    switch (pDynCp[playerid]) {
        case 0: SendClientMessage(playerid, 0xFFFFFFFF, "Вы на первом чекпоинте");
        case 1: SendClientMessage(playerid, 0xFFFFFFFF, "Вы на втором чекпоинте");
    }
    return 1;
}
Как видите, проверки очень просты. Если сейчас игроку виден 0 чекпоинт, то соответственно он может на него встать, а когда встанет ему появится сообщение "Вы на первом чекпоинте". То же самое для второго. Вместо этих действий вы вправе прописать любой свод код, например показать игроку какое-нибудь меню, либо телепортировать его в другое место.
Помните, что всегда могут возникнуть ситуации, когда ваша система идёт вразрез с этой, поэтому тщательно переделывайте любой свой код связанный с чекпоинтами, если вы будете использовать эту систему. (проблемным будет импортировать данный код в gf-based моды)
Reply
#2

Для определения расстояния используй лучше GetPlayerDistanceFromPoint(), быстрее будет.
Reply
#3

Хорошо, спасибо.
Reply
#4

  • избавляемся от #define MAX_CHECKPOINTS 2
  • во всех циклах ломимся в sizeof(dynamicCp)
  • сам массив объявляем так new dynamicCp[][CHECKPOINTINFO] = {...};, компилятор сам определит его размер
Преимущество: нам теперь не нужно держать в голове информацию о том что необходимо ещё и где-то константу менять, просто добавляем новый чекпоинт в массив и компилируем скрипт.
Недостаток: при загрузке массива из стороннего источника, нам все равно придется жестко указать размер данного массива, но опять же только в одном месте.

Quote:
Originally Posted by White_116
View Post
Для определения расстояния используй лучше GetPlayerDistanceFromPoint(), быстрее будет.
А ещё быстрее будет использовать IsPlayerInRangeOfPoint
pawn Code:
if (GetPlayerDistanceFromPoint(j, x0, y0, z0) <= dist) {
if (IsPlayerInRangeOfPoint(j, dist, x0, y0, z0)) {
pawn Code:
if (GetPlayerDistanceFromPoint(j, dynamicCp[id][e_cpX], dynamicCp[id][e_cpY], dynamicCp[id][e_cpZ]) > dist) {
if (!IsPlayerInRangeOfPoint(j, dist, dynamicCp[id][e_cpX], dynamicCp[id][e_cpY], dynamicCp[id][e_cpZ])) {
Reply
#5

Помню, [LTD]Luxury делал что-то подобное. Естественно, без этих модных функций расстояния =)
Reply
#6

Подправил. Вопрос в том, почему быстрей будет использовать эти функции? И почему среди этих функций IsPlayerInRangeOfPoint будет работать пошустрей, чем GetPlayerDistanceFromPoint? Это было выявлено на тестах или дело в чём-то ином?
Reply
#7

Quote:
Originally Posted by anybox
View Post
Подправил. Вопрос в том, почему быстрей будет использовать эти функции? И почему среди этих функций IsPlayerInRangeOfPoint будет работать пошустрей, чем GetPlayerDistanceFromPoint? Это было выявлено на тестах или дело в чём-то ином?
Все сразу. IsPlayerInRangeOfPoint изначально была создана для проверок, она возвращает булево значение. GetPlayerDistanceFromPoint была создана для получения расстояния от игрока до точки и она возвращает значение с плавающей запятой, а в виду того что архитектура всех десктопных процессоров такова что они основаны на работе с целочисленными переменными, работа с плавающей запятой занимает больше времени.
Reply
#8

И чем это лучше CreateDynamicCP, который дан в стримере от Инкогнито ?
Reply
#9

Quote:
Originally Posted by Richard_Gere
View Post
И чем это лучше CreateDynamicCP, который дан в стримере от Инкогнито ?
Ничем. Это Tutorial
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)