17.01.2013, 16:17
(
Last edited by anybox; 21/01/2013 at 06:46 AM.
)
Всем привет. Допустим, что по некоторым причинам вас не удовлетворяют средства проверки, что игрок находится в какой-либо точке, например с помощью пикапов или проверок IsPlayerInRangeOfPoint. Или же вы просто хотите сделать чекпоинты как дополнение к уже существующим пикапам(в том смысле, чтобы чекпоинт появлялся, когда подходишь к какому-либо пикапу). Я расскажу, как сделать систему динамических чекпоинтов, а вы уже определите, как будете использовать её в своих проектах.
1 шаг. Создание основы. Структуры и функции.
Во-первых, нам нужна структура, которая будет содержать информацию о чекпоинте: позиция, радиус чекпоинта и дистанция, начиная с которой чекпоинт будет отображаться.
Далее нужно будет объявить массив чекпоинтов. Давайте сделаем два чекпоинта. Так, просто для теста.
Мы сразу заполняем массив чекпоинтов dynamicCp нужной информацией(если вас не устраивает такой способ заполнения массива, вы можете сделать считывание основных параметров из файла). Давайте разберём строку {0.0, 0.0, 1.0, 3, 5}. Первые три цифры - это позиция чекпоинта, далее радиус и последнее - дистанция. По сути, эта строка указывает, что чекпоинт размера 3 будет создан в центре карты (0.0, 0.0, 1.0) и покажется игроку, только если расстояние между ними окажется меньше 5.
Объявите новый массив pDynCp[MAX_PLAYERS].
Назначение его в том, чтобы хранить индекс того чекпоинта, который в данный момент виден игроку. Мы будем использовать значения этого массива, чтобы определять в OnPlayerEnterCheckpoint, на какой чекпоинт зашёл игрок. Всё, что нужно сделать с данным массивом, это "обнулить" одну из его ячеек, когда какой-либо игрок присоединиться. (фактически, мы не обнуляем, а присваиваем -1, потому что индекс чекпоинта может быть и нулевым)
Далее. Создайте паблик, пока пустой.
Отлично, паблик создан. Последним на данном, подготовительном этапе шагом будет создание таймера. В OnGameModeInit перед return 1; поместите данный ниже код:
Мы объявили таймер, суть которого будет объяснена ниже.
Поздравляю, вы закончили первый этап, давайте перейдём ко второму.
2. Непосредственно обработка чекпоинтов.
Не самый сложный код, но в данном мануале, пожалуй, самый основной.
Вернёмся к нашему паблику checkDynCp. Пропишите в нём следующее:
Время разобраться, что же собственно говоря происходит в данном паблике.
В начале мы объявляем вспомогательные переменные, делаем их статическими (созданы они будут один раз и доступны только в пределах данного паблика).
Далее запускаем цикл по всем игрокам (если вы используете свои "оптимизированные" циклы только по подключенным игрокам, то можете изменить эту строку). Первая проверка - подключен ли игрок. Если нет, переходим к следующему игроку. Далее узнаём позицию игрока GetPlayerPos(j,x,y,z); В переменную ID заносим значение индекса чекпоинта, который виден в данный момент игроку. Далее проверяем, если индекс НЕ равен -1 (то есть игроку виден какой-либо чекпоинт в данный момент), то будем проверять, отошёл ли игрок от чекпоинта на достаточное расстояние(e_cpDist), чтобы скрыть чекпоинт. Если не отошёл, то чекпоинт так и будет виден, и нам не надо проверять другие чекпоинты (помните, что в GTA SAMP в один момент времени может существовать только ОДИН чекпоинт). Если же у нас id равен -1 (то есть игроку ещё не виден никакой чекпоинт), то мы проверим, какой чекпоинт ближе всего к игроку. Для этого пройдёмся по всем чекпоинтам и расчитаем расстояние от чекпоинта до игрока. Если расстояние меньше чем e_cpDist, то в массив pDynCp[playerid] заносим индекс найденного чекпоинта, устанавливаем на карте чекпоинт и прерываем цикл через break;.
Отлично! Мы написали основной код, перейдём теперь к следующему этапу.
3. Делаем что-либо, в зависимости от того, на каком чекпоинте игрок.
Как видите, проверки очень просты. Если сейчас игроку виден 0 чекпоинт, то соответственно он может на него встать, а когда встанет ему появится сообщение "Вы на первом чекпоинте". То же самое для второго. Вместо этих действий вы вправе прописать любой свод код, например показать игроку какое-нибудь меню, либо телепортировать его в другое место.
Помните, что всегда могут возникнуть ситуации, когда ваша система идёт вразрез с этой, поэтому тщательно переделывайте любой свой код связанный с чекпоинтами, если вы будете использовать эту систему. (проблемным будет импортировать данный код в gf-based моды)
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}
};
Объявите новый массив pDynCp[MAX_PLAYERS].
pawn Code:
new pDynCp[MAX_PLAYERS];
pawn Code:
public OnPlayerConnect(playerid)
{
// какой-то ваш код
pDynCp[playerid] = -1;
return 1;
}
pawn Code:
forward checkDynCp();
public checkDynCp()
{
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;
}
Помните, что всегда могут возникнуть ситуации, когда ваша система идёт вразрез с этой, поэтому тщательно переделывайте любой свой код связанный с чекпоинтами, если вы будете использовать эту систему. (проблемным будет импортировать данный код в gf-based моды)