PDA

Просмотр полной версии : [Мануал] Использование динамической зоны



red.inc
30.07.2019, 13:52
Всех приветствую дорогие читатели Pro-Pawn.Ru
Недавно приступил к помощи игрокам и созданиям мануалов. Просьба не судить строго, буду рад каждой помощи

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

Это лишь мои предположение, проверку она не проходило.

Теперь приступаем к коду изложенный мною.


enum AREAS
{
gExitHospital, // выход из больницы
// и тд
}
new g_areas[AREAS]; // переменная, которая в будущим будет присваивать нашу зону

new player_zone[MAX_PLAYERS char]; // переменная которая будет принимать ид территория на игрока

После переходим к пабликам как OnGameModeInit и OnPlayerEnterDynamic/OnPlayerLeaveDynamic

public OnGameModeInit()
{
g_areas[gExitHospital] = CreateDynamicSphere(/*Ваши координаты X,Y,Z, радиус, виртуальный мир, интерьер, -1 (ид игрока (то есть любой))*/);

return true;
}

public OnPlayerEnterDynamicArea(playerid,areaid)
{
if(areaid == g_areas[gExitHospital]) player_zone{playerid} = 1; // присваем
return true;
}
public OnPlayerLeaveDynamicArea(playerid,areaid)
{
player_zone{playerid} = 0; // очищаем
}

Теперь отправляемся к нашему паблику OnPlayerKeyStateChange
public OnPlayerKeyStateChange
{
switch(newkeys)
{
case KEY_WALK:
{
switch(player_zone{playerid})
{
case 1:
{
SetPlayerPos(playerid,/*устанавливаем свои координаты*/);
/*
Дальше убираем виртуальный мир и Т.Д
*/
}
default: return 0;
}
}
}
return true;
}


Код готов, если появятся вопросы либо исправить можно будет как то лучше, то напишите в теме, каждое предложение очень много стоит для меня
Благодарю всех за внимание

DeimoS
30.07.2019, 20:24
Во-первых, слабоватый мануал. Я бы сказал, очень слабый :) Информации мало, она не структурирована, оформления нормального нет.

Во-вторых, идея подобной оптимизации - такая себе. Стример ведь не каким-то волшебным образом узнаёт о том, зашёл ли игрок в динамическую зону или нет. Он постоянно получает позицию каждого игрока и сверяет его с ближайшими объектами/пикапами/зонами и т.п. И подобной "оптимизацией" ты вместо временной нагрузки от вызова IsPlayerInRangeOfPoint получаешь постоянную нагрузку от стримера. Сомнительная оптимизация :)

Не говоря уже о том, что в коде не учитывается возможность того, что две или более динамические зоны будут накладываться друг на друга. В таком случае твой код просто будет работать некорректно :)

red.inc
30.07.2019, 22:23
Насчёт твоих слов. В некоторых событиях согласен, в оформление я не специалист, важна простота в чтение.
Хорошо, теперь давай насчёт зоны которую я указал, для начала это не так нагружает ЦП вашего компьютера, потом - не нужно устанавливать для кнопки ALT 100 функций, ведь можно просто через одно перечисление работать. К тому же и банкоматы, так же можно создать у каждого динамическую зону и не проводить проверки по нажатию ALT, где именно игрок находится, легче просто подходя к банкомату задать свой ИД и пользоваться им. Если уже говорить о оптимизации, тут уже можно спорить.

DeimoS
30.07.2019, 22:58
важна простота в чтение.

В том и дело, что сейчас довольно трудно читать сей мануал :)
Зайди в подраздел "Прочее (http://pro-pawn.ru/forumdisplay.php?224-%D0%9F%D1%80%D0%BE%D1%87%D0%B5%D0%B5)", походи по темам и сравни их со своей.


для начала это не так нагружает ЦП вашего компьютера,

Замеры в студию)
Хотя спойлер: с твоим вариантом нагрузка будет выше. Достаточно лишь знать как работает стример, чтоб понять это ;)

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


потом - не нужно устанавливать для кнопки ALT 100 функций, ведь можно просто через одно перечисление работать

Сути особо не меняется, кроме того, что вместо "IsPlayerInRangeOfPoint(...)" будет "case ...:"



Если уже говорить о оптимизации, тут уже можно спорить.

Существует такая штука, как "тестирование", которая помогает понять какой из вариантов ОБЪЕКТИВНО оптимизированнее. Сделаешь её и спорить сразу будет бессмысленно :)

Kovshevoy
31.07.2019, 00:12
Во-первых, слабоватый мануал. Я бы сказал, очень слабый :) Информации мало, она не структурирована, оформления нормального нет.

Во-вторых, идея подобной оптимизации - такая себе. Стример ведь не каким-то волшебным образом узнаёт о том, зашёл ли игрок в динамическую зону или нет. Он постоянно получает позицию каждого игрока и сверяет его с ближайшими объектами/пикапами/зонами и т.п. И подобной "оптимизацией" ты вместо временной нагрузки от вызова IsPlayerInRangeOfPoint получаешь постоянную нагрузку от стримера. Сомнительная оптимизация :)

Не говоря уже о том, что в коде не учитывается возможность того, что две или более динамические зоны будут накладываться друг на друга. В таком случае твой код просто будет работать некорректно :)
Стример разве не обрабатывает информацию в отдельном потоке (хотя я знаю шо Самп не многопоточен)? Посвятите нуфов, Владислав хД

red.inc
31.07.2019, 10:21
В том и дело, что сейчас довольно трудно читать сей мануал :)
Зайди в подраздел "Прочее (http://pro-pawn.ru/forumdisplay.php?224-%D0%9F%D1%80%D0%BE%D1%87%D0%B5%D0%B5)", походи по темам и сравни их со своей.



Замеры в студию)
Хотя спойлер: с твоим вариантом нагрузка будет выше. Достаточно лишь знать как работает стример, чтоб понять это ;)

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



Сути особо не меняется, кроме того, что вместо "IsPlayerInRangeOfPoint(...)" будет "case ...:"




Существует такая штука, как "тестирование", которая помогает понять какой из вариантов ОБЪЕКТИВНО оптимизированнее. Сделаешь её и спорить сразу будет бессмысленно :)

Хорошо. Я не умею проводить тестирование, у меня нету кода, который смог бы мне это проверить. Теперь переходим к моему коду, да происходит проверка ежесекунда, понимаю что можно сделать функцию, которая будет проверять местоположение по нажатию кнопки, к примеру ALT ,просто поставив через тот же самый areaid. Не знаю, попробую сейчас найти Cortez функцию, для тестирование. Потом отпишу

DeimoS
31.07.2019, 11:32
Стример разве не обрабатывает информацию в отдельном потоке (хотя я знаю шо Самп не многопоточен)? Посвятите нуфов, Владислав хД

Нет, не в отдельном (исходники открыты, так что каждый может самостоятельно убедиться).
Скажу даже больше: стример обрабатывает данные не тупо по таймеру, а каждые n тиков. И если ваш мод имеет много "тяжеловесного" кода, который забивает поток - функции стримера, по типу динамических зон, будут срабатывать с гораздо большей задержкой, чем, например, на пустом моде с тем же количеством объектов/зон и т.п.

Стример - не панацея. Он несомненно является очень удобным инструментом, помогающим решать кучу проблем SA-MP, но использовать его нужно с умом :)



Хорошо. Я не умею проводить тестирование, у меня нету кода, который смог бы мне это проверить.

При этом, начал рассуждать на тему того, как твой код нагружает процессор и т.п. =)


Теперь переходим к моему коду, да происходит проверка ежесекунда, понимаю что можно сделать функцию, которая будет проверять местоположение по нажатию кнопки, к примеру ALT ,просто поставив через тот же самый areaid. Не знаю, попробую сейчас найти Cortez функцию, для тестирование. Потом отпишу

Тем скриптом ты ничего не проверишь. Тебе не скорость работы конкретного кода проверять нужно, а нагрузку от стримера посмотреть. Но для достаточно мощного процессора нагрузка там будет несущественна, поэтому можно просто посмотреть как сам стример начнёт тормозить.

Возьми какой-нибудь крупный мод, в котором хотя бы 1000-2000 домов (важно, чтоб пикапы были достаточно сгруппированы хотя бы в одном каком-то месте), и вставь вот этот код сразу после стримера:

stock t_CreateDynamicPickup(modelid, type, Float:x, Float:y, Float:z, worldid = -1, interiorid = -1, playerid = -1, Float:streamdistance = STREAMER_PICKUP_SD, STREAMER_TAG_AREA:areaid = STREAMER_TAG_AREA:-1, priority = 0)
{
CreateDynamicCircle(x, y, 5.0);
return CreateDynamicPickup(modelid, type, x, y, z, worldid, interiorid, playerid, streamdistance, areaid, priority);
}
#if defined _ALS_CreateDynamicPickup
#undef CreateDynamicPickup
#else
#define _ALS_CreateDynamicPickup
#endif

#define CreateDynamicPickup t_CreateDynamicPickup

А в OnPlayerEnterDynamicArea вставь что-нибудь типа такого
new test_string[144];
format(test_string, sizeof(test_string), "areaid: %d", areaid);
SendClientMessageToAll(-1, test_string);

После зайди на сервер и начни бегать от пикапа к пикапу, следя за тем, как стример поспевает определять твоё нахождение в той или иной динамической зоне.

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

red.inc
31.07.2019, 16:28
Проведя несколько опытов, я решил немного по другому свой код сделать.
Показатель дал такой разрыв - что лучше использовать просто IsPlayerDynamicArea.

(IsPlayerInRangeOfPoint)
Проверка по нажатию кнопки ALT - результат 8900 мс | 0 FPS

(IsPlayerDynamicArea)
Проверка по нажатию кнопки Y - результат 6048 мс | 0 FPS

(OnPlayerDynamicArea)
Проверка по нажатию кнопки N - результат 8365 мс | 0 FPS

На кнопку была проверено 1000 игроками и 22 координаты (X,Y,Z), и результаты дали своё.
В данном случае, я опустил голову, и считаю что мой способ, в данном случае был лучше конечно IsPlayerInRangeOfPoint, но вот показатель IsPlayerDynamicArea тоже показал себя отлично, на данный момент, пока нельзя сделать выводы.

DeimoS
31.07.2019, 16:47
IsPlayerInDynamicArea
И эта функция в твоём случае вообще не применима, ибо динамические зоны могут вообще не относится к пикапам.

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

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

Я уже писал, что схожий результат можно получить без стримера, сделав как-то так:
public OnGameModeInit()
{
SetTimer("@__SomeTimer", 1000, true);
}

new PlayerInAreaID[MAX_PLAYERS];

public OnPlayerConnect(playerid)
{
PlayerInAreaID[playerid] = -1;
return 1;
}

@__SomeTimer();
@__SomeTimer()
{
foreach(new i: Player)
{
if(IsPlayerInRangeOfPoint(playerid, /*Радиус*/, /*координата x*/, /*координата y*/, /*координата z*/))
{
PlayerInAreaID[playerid] = 1;
}
else if(IsPlayerInRangeOfPoint(playerid, /*Радиус*/, /*координата x*/, /*координата y*/, /*координата z*/))
{
PlayerInAreaID[playerid] = 2;
}
else if(IsPlayerInRangeOfPoint(playerid, /*Радиус*/, /*координата x*/, /*координата y*/, /*координата z*/))
{
PlayerInAreaID[playerid] = 3;
}
// И так далее. Тут перечисляются координаты для всех мест, где должна срабатывать кнопка
else
{
PlayerInAreaID[playerid] = -1;
}
}
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_YES)
{
if(PlayerInAreaID[playerid] != -1)
{
switch(PlayerInAreaID[playerid])
{
case 1:
{
// Действие для зоны под ID 1
}
case 2:
{
// Действие для зоны под ID 2
}
case 3:
{
// Действие для зоны под ID 3
}
// И так далее
}
}
}
return 1;
}
Такой способ при замерах срабатывания кнопки будет даже быстрее, чем динамические зоны (ибо работа идёт с переменной, а не с функцией плагина). Но будет ли он оптимизированнее? Нет, нет и ещё раз нет. Ибо вместо того, чтоб один раз вызвать 10 IsPlayerInRangeOfPoint, ты будешь каждую секунду вызывать эти самые 10 IsPlayerInRangeOfPoint. Примерно по схожему принципу работает и стример.


Если ты действительно хочешь избавиться от кучи проверок координат при нажатии клавиш, лучше реализовать всё через пикапы: создать на каждой точке пикап и когда игрок будет его подбирать, записывать его ID и уже в OnPlayerKeyStateChange проверять этот самый ID + получать координаты пикапа через Streamer_GetFloatData, проверяя их с позицией игрока через IsPlayerInRangeOfPoint, дабы читеры не могли спокойно телепортироваться, передавая фейковые ID пикапов.

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

red.inc
01.08.2019, 09:49
Хорошо, проверить идею с пикапом, интересная задумка. После замеров, я напишу, как раз таки может и поменяю свой выбор.

DeimoS
01.08.2019, 10:41
Такие системы уже давно есть в паблике. Например, вот (http://pro-pawn.ru/showthread.php?16127-key_pickupfix-inc). Это первое.

Второе - такие системы, в целом, довольно бесполезны, ибо, по факту, куча проверок с IsPlayerInRangeOfPoint в OnPlayerKeyStateChange совсем уж негативно никак не повлияет на сервер. Чтоб это понять, достаточно изучить моды периода 2006-2012 годов, в которых спокойно использовали IsPlayerInRangeOfPoint и сервера как-то продолжали работать, не зависая каждую секунду и не испытывая каких-либо других проблем. И с тех пор компьютерное железо на хостингах стало только лучше.

Смысл есть пытаться оптимизировать подобные пикапы только тогда, когда ты заранее, перед написанием мода, продумываешь все системы и та система, с помощью которой ты оптимизируешь пикапы, будет использоваться ещё в каких-то системах. Например, можно воспользоваться принципом работы стримера: поделить всю карту на квадраты определённого размера и отслеживать то, в каком квадрате находится игрок. И после при запуске сервера и создании пикапов/машин и т.п., сверять их координаты с координатами квадратов и распределять их по этим квадратам. И когда, например, нужно будет узнать какой автомобиль находится ближе всего к игроку, не нужно будет проверять все автомобили сервера, а можно будет выгрузить список автомобилей из конкретного квадрата и проверять только среди них.
В этой ситуации ты действительно можешь существенно сэкономить процессорного времени, так как сразу оптимизируешь большую часть проверок, связанных со сверкой координат. А то, что ты избавишься от десятка IsPlayerInRangeOfPoint - не даст практически никакого профита в том плане, что эти проверки изначально не создавали каких-то существенных проблем.