PDA

Просмотр полной версии : [Вопрос] Проверка на координаты входа



Saibot
07.11.2020, 14:07
Делаю вход в бизнес через KEY_WALK в OnPlayerKeyStateChange и задался таким вопросом.
Если игрок будет нажимать на L.Alt в любом месте, ему в OnPlayerKeyStateChange, будет циклом прогонять все координаты входа в бизнес, дабы проверить находится ли он возле бизнеса.
Пример:
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(IsPlayerInRangeOfPoint(playerid, 2.0, BusinessInfo[b][bEnterX], BusinessInfo[b][bEnterY], BusinessInfo[b][bEnterZ]))
{
//Вход
return 1;
}
}
return 1;
}
}

Будет ли лучше, если создать динамическую зону и проверять в ней?
public OnPlayerEnterDynamicArea(playerid, areaid)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(areaid == ar_BusinessEnter[b])
{
gPlayerBusinessID[playerid] = b;
return 1;
}
}
return 1;
}

public OnPlayerLeaveDynamicArea(playerid, areaid)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(areaid == ar_BusinessEnter[b])
{
gPlayerBusinessID[playerid] = 0;
return 1;
}
}
return 1;
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
if(gPlayerBusinessID[playerid] != 0)
{
SetPlayerPos(playerid,
BusinessInfo[gPlayerBusinessID[playerid]][bEnterX],
BusinessInfo[gPlayerBusinessID[playerid]][bEnterY],
BusinessInfo[gPlayerBusinessID[playerid]][bEnterZ]
);
return 1;
}
return 1;
}
return 1;
}

punkochel
07.11.2020, 15:09
Лично я повсеместно использую динамические зоны, и советовал бы именно их, ибо проверка нахождения в зоне происходит быстрее чем проверка на нахождение в радиусе по координатам.
Единственное что я бы сделал, то не стал бы создавать массив (gPlayerBusinessID[MAX_PLAYERS]) для этой реализации:

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(IsPlayerInDynamicArea(playerid, areaid))
{
//Вход
return 1;
}
}
return 1;
}
}

Saibot
07.11.2020, 15:35
Лично я повсеместно использую динамические зоны, и советовал бы именно их, ибо проверка нахождения в зоне происходит быстрее чем проверка на нахождение в радиусе по координатам.
Единственное что я бы сделал, то не стал бы создавать массив (gPlayerBusinessID[MAX_PLAYERS]) для этой реализации:

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(IsPlayerInDynamicArea(playerid, areaid))
{
//Вход
return 1;
}
}
return 1;
}
}

Но проблема в том, что цикл остается в OnPlayerKeyStateChange и при нажатие L.Alt (в любом месте), он будет каждый раз проверять все зоны.

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

whale
07.11.2020, 16:27
Дабы априори избежать циклов, можно, при создании динамической зоны, записывать некоторую дополнительную информацию о ней в отдельный массив данных (в данном случае - закреплена ли зона за каким-либо бизнесом и, если да - то является ли эта зона входом, либо выходом).

Примерный вариант (не тестировал):


/*
Definitions
*/

#define INVALID_BUSINESS_ID (-1)
#define AREA_BUSINESS_STATUS_ENTRANCE (0)
#define AREA_BUSINESS_STATUS_EXIT (1)

/*
Global variables
*/

enum E_DYNAMIC_AREA_DATA
{
area_bussiness_id,
area_bussiness_status
}

new
gDynamicAreaExtraData[/*кол-во зон*/][E_DYNAMIC_AREA_DATA]; // заполнять информацию при создании зон

new
gPlayerCurrentDynamicAreaId[MAX_PLAYERS] = {INVALID_DYNAMIC_AREA_ID, ...};


/*
Publics
*/

public OnPlayerEnterDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = areaid;
return 1;
}

public OnPlayerLeaveDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = INVALID_DYNAMIC_AREA_ID;
return 1;
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
new
player_dynamic_area_id = gPlayerCurrentDynamicAreaId[playerid];

if(player_dynamic_area_id != INVALID_DYNAMIC_AREA_ID)
{
if(gDynamicAreaExtraData[player_dynamic_area_id][area_bussiness_id] != INVALID_BUSINESS_ID)
{
new
dynamic_area_business_status = gDynamicAreaExtraData[player_dynamic_area_id][area_bussiness_status];

// ID бизнеса указан в структуре динамических зон, так что получать координаты входа/выхода не сосавит труда

if(dynamic_area_business_status == AREA_BUSINESS_STATUS_ENTRANCE)
{
// вход

return 1;
}
else if(dynamic_area_business_status == AREA_BUSINESS_STATUS_EXIT)
{
// elise if дабы избежать возможных ошибок в коде (присвоение значения отличного от 0 или 1)
// выход

return 1;
}
}
}
}
return 1;
}

Saibot
07.11.2020, 16:46
public OnPlayerEnterDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = areaid;
return 1;
}

public OnPlayerLeaveDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = INVALID_DYNAMIC_AREA_ID;
return 1;
}
А разве оно запишет в gPlayerCurrentDynamicAreaId нужный ID зоны в которой находится игрок?

whale
07.11.2020, 16:55
public OnPlayerEnterDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = areaid;
return 1;
}

public OnPlayerLeaveDynamicArea(playerid, areaid)
{
gPlayerCurrentDynamicAreaId[playerid] = INVALID_DYNAMIC_AREA_ID;
return 1;
}
А разве оно запишет в gPlayerCurrentDynamicAreaId нужный ID зоны в которой находится игрок?

Запишется ID последней динамической зоны, в которую вошёл игрок.

Pro_Coder
07.11.2020, 17:19
Так правильно, стример каждый раз прогоняет все динамические зоны, что дает постоянную нагрузку, а не один раз когда нажали на клавишу. Но динамический зон может быть на сервере много, домов, бизнесов и т.п.

Я предпочитаю использовать то, что выполняется один раз

execution
07.11.2020, 17:27
На самом деле, было-бы неплохо сделать сделать вход через пикапы с помощью нажатия клавиши и всё

Saibot
07.11.2020, 17:35
На самом деле, было-бы неплохо сделать сделать вход через пикапы с помощью нажатия клавиши и всё
В первом варианте. сделано через пикап по нажатию на Alt.
Или, как ты имел введу?

execution
07.11.2020, 17:40
В первом варианты показано нажатие на АЛЬТ по координатам входа

Saibot
07.11.2020, 17:48
В первом варианты показано нажатие на АЛЬТ по координатам входа
Я имею в виду, что там стоит пикап, что бы игрок видел где нажимать.
Но, я так понял ты, имел ввиду, что нужно сделать так, что бы игрок ставал на пикап, присваивать ID бизнеса в переменную и потом делать проверку в KEY_WALK, как в другом моем варианте.

Так?

punkochel
07.11.2020, 18:04
Честно говоря, я не понимаю боязни использовать цикл при нажатии на клавишу. Если проблема в микро-нагрузке, то страшно представить как бы ты написал систему секундного таймера который обрабатывает игроков, если не брать в счет индивидуальные таймеры.
Ладно когда у тебя несколько сотен тысяч динамических зон...
Или быть может когда игрок встает на пикап, то его ID возвращается каким-то чудесным образом, без цикла?

Saibot
07.11.2020, 18:17
Или быть может когда игрок встает на пикап, то его ID возвращается каким-то чудесным образом, без цикла?
В том то прикол, что без цикла ни как.
Это я ответил execution.

- - - Добавлено - - -


Запишется ID последней динамической зоны, в которую вошёл игрок.
Твой варинт не практичен.
if(gDynamicAreaExtraData[player_dynamic_area_id][area_bussiness_id] != INVALID_BUSINESS_ID)

ID бизнеса указан в структуре динамических зон

player_dynamic_area_id не всегда будет равен ID бизнесу.
Можно сделать так, что бы динамические зоны для бизнаса создавать в OnGameModeInit первыми, да бы у них ID был равен бизнесу, т.е. с 1.
Но, за этим нужно следить, потому что, если создать динам. зону перед зоною с бизнесами то, areaid вернет уже не первый ID, а второй.
А, если создать 10 динам. зон перед, то вернем 11 ID.

Pro_Coder
07.11.2020, 18:54
Покажу пример из моего кода, делал для системы домов

#define TYPE_HOUSE_PICKUP_ENTER 1 // Пикапы входа
#define TYPE_HOUSE_PICKUP_EXIT 2 // Пикапы выхода
// и т.д. для бизнеса и все начиная с 3


enum e_Data_House {
h_STP,
h_House
};

Когда создаешь пикап, в данном случае у меня загрузка с БД

data[e_Data_House]


House[j][h_PickupID] = CreateDynamicPickup(1272, 23, ...
data[h_STP] = TYPE_HOUSE_PICKUP_ENTER;
data[h_House] = Ид дома;
Streamer_SetArrayData(STREAMER_TYPE_PICKUP, House[j][h_PickupID], E_STREAMER_EXTRA_ID, data);


Когда встаешь на пикап

public OnPlayerPickUpDynamicPickup(playerid, pickupid)
{

new
data[e_Data_House];
Streamer_GetArrayData(STREAMER_TYPE_PICKUP, pickupid, E_STREAMER_EXTRA_ID, data);
if (data[h_STP] == TYPE_HOUSE_PICKUP_ENTER) {
// data[h_House] - ид дома
return 1;
}

return 1;
}


И естественно удаляем данные перед удалением пикапа

new
data[e_Data_House];
data[h_STP] = TYPE_HOUSE_PICKUP_ENTER;
data[h_House] = Ид дома;
Streamer_RemoveArrayData(STREAMER_TYPE_PICKUP, House[id][h_PickupID], E_STREAMER_EXTRA_ID, data);
DestroyDynamicPickup(House[id][h_PickupID]);

Pa4enka
08.11.2020, 02:11
...
Тоже хотел предложить этот вариант, но в этом случае под капотом у стримера все равно будет цикл)

taichi
08.11.2020, 14:19
Помнится я уже создавал тему с подобным вопросом, и Deimos ответил мне, что всяко лучше использовать стандартный подход со связкой цикла for и IsPlayerInRangeOfPoint, так как это создаёт лишь временную нагрузку, в то время как стример каждые N миллисекунд проходится по массиву с Dynamic Area и сверяет их координаты с координатами игрока, что создаёт постоянную нагрузку, даже когда игрок не нажимает левый ALT.

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

DeimoS
08.11.2020, 14:27
Лично я повсеместно использую динамические зоны, и советовал бы именно их, ибо проверка нахождения в зоне происходит быстрее чем проверка на нахождение в радиусе по координатам.[/pawn]

А ты не задавался вопросом о том, как стример определяет то, в какой динамической зоне находится игрок? :)


Или быть может когда игрок встает на пикап, то его ID возвращается каким-то чудесным образом, без цикла?

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





Делаю вход в бизнес через KEY_WALK в OnPlayerKeyStateChange и задался таким вопросом.
Если игрок будет нажимать на L.Alt в любом месте, ему в OnPlayerKeyStateChange, будет циклом прогонять все координаты входа в бизнес, дабы проверить находится ли он возле бизнеса.
Пример:
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(IsPlayerInRangeOfPoint(playerid, 2.0, BusinessInfo[b][bEnterX], BusinessInfo[b][bEnterY], BusinessInfo[b][bEnterZ]))
{
//Вход
return 1;
}
}
return 1;
}
}

Будет ли лучше, если создать динамическую зону и проверять в ней?
public OnPlayerEnterDynamicArea(playerid, areaid)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(areaid == ar_BusinessEnter[b])
{
gPlayerBusinessID[playerid] = b;
return 1;
}
}
return 1;
}

public OnPlayerLeaveDynamicArea(playerid, areaid)
{
for(new b = 1; b <= gNumberBusiness; b++)
{
if(areaid == ar_BusinessEnter[b])
{
gPlayerBusinessID[playerid] = 0;
return 1;
}
}
return 1;
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_WALK)
{
if(gPlayerBusinessID[playerid] != 0)
{
SetPlayerPos(playerid,
BusinessInfo[gPlayerBusinessID[playerid]][bEnterX],
BusinessInfo[gPlayerBusinessID[playerid]][bEnterY],
BusinessInfo[gPlayerBusinessID[playerid]][bEnterZ]
);
return 1;
}
return 1;
}
return 1;
}


У тебя есть два варианта: либо оставить вариант с проверкой координат, так как каких-то критических нагрузок это не создаёт и особо переживать тут не о чем, либо можешь сделать вариант, при котором ты в OnPlayerPickUpDynamicPickup запоминаешь ID пикапа, на который встал игрок + время, когда он встал на него, а в OnPlayerKeyStateChange проверяешь, давно ли последний раз игрок вставал на пикап и если недавно - запускаешь цикл и проверяешь, равен ли ID сохранённого в OnPlayerPickUpDynamicPickup пикапа с одним из пикапов бизнесов. Такой цикл будет меньше загружать сервер, но для игроков кнопка, при взятии пикапа, может срабатывать с некоторой задержкой (так что лично я выбрал бы первый вариант).

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

punkochel
08.11.2020, 15:00
А ты не задавался вопросом о том, как стример определяет то, в какой динамической зоне находится игрок? :)
Понятное дело что цикл, просто мне на порядок лучше работать с зонами, нежели с пикапами, обусловлено это тем, что не нужно изобретать какие-то анти-флуды пикапов, есть возможность работать с зонами в любой точке мода, а не только в колбэке, ну, и конечно, сама динамичность создания различного рода форм и размеров.


Не поверишь, но отлавливанием пикапов занимается движок игры. Дальше SAMP перехватывает это событие и игрок отправляет серверу информацию о том, какой пикап он взял. Никаких циклов или дополнительных проверок для сервера в этот момент не происходит.
Не могу конечно утверждать, но интересно, как это движок идентифицирует пикапы, созданные мультиплеером? Ладно бы игроки качали ГТА, в которой уже созданы все пикапы по дефолту, там можно было бы перехватить его ID.

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

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


Не могу конечно утверждать, но интересно, как это движок идентифицирует пикапы, созданные мультиплеером? Ладно бы игроки качали ГТА, в которой уже созданы все пикапы по дефолту, там можно было бы перехватить его ID.

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

Для тебя это, возможно, будет открытием, но даже другие игроки в поле твоего зрения - это на деле не игроки, а обычные NPC, которые так же управляются движком игры. Сервер лишь отправляет данные о том, в каком состоянии находится игрок, а уже твой SAMP-клиент вызывает функции движка, заставляя NPC выполнять действия. Например, когда игрок бежит, сервер лишь передаёт направление и скорость, а не фактическое местоположение. Из-за этого у тебя игроки на деле находятся не в тех точках, где они находятся на самом деле, хоть эти отличия и не будут особо существенными (собственно, можно поискать скрины с каких-нибудь эвентов на том или ином проекте и найти скрины от разных игроков, сделаенные в одно время. Или вообще через песочницу зайти в игру за двух персонажей и поиграться с этим. Особенно заметно это, если один из персонажей будет на короткие расстояния перемещаться).

punkochel
08.11.2020, 15:23
Для тебя это, возможно, будет открытием, но даже другие игроки в поле твоего зрения - это на деле не игроки, а обычные NPC, которые так же управляются движком игры. Сервер лишь отправляет данные о том, в каком состоянии находится игрок, а уже твой SAMP-клиент вызывает функции движка, заставляя NPC выполнять действия. Например, когда игрок бежит, сервер лишь передаёт направление и скорость, а не фактическое местоположение. Из-за этого у тебя игроки на деле находятся не в тех точках, где они находятся на самом деле, хоть эти отличия и не будут особо существенными (собственно, можно поискать скрины с каких-нибудь эвентов на том или ином проекте и найти скрины от разных игроков, сделаенные в одно время. Или вообще через песочницу зайти в игру за двух персонажей и поиграться с этим. Особенно заметно это, если один из персонажей будет на короткие расстояния перемещаться).
Про синхронизацию я читал, знаю) Просто никогда не задумывался что движок сам определяет какие-либо значения, созданные из вне.

DeimoS
08.11.2020, 16:49
Про синхронизацию я читал, знаю) Просто никогда не задумывался что движок сам определяет какие-либо значения, созданные из вне.

Он ничего не определяет. Ему их сообщает сервер, отправляя данные, которые он сохранил в память в момент, когда ты вызываешь CreatePickup.
Ты можешь просто погуглить то, как мододелы для сингла создают пикапы. Вот, например, опкод, создающий обычный пикап: https://gtamods.com/wiki/0213
Есть отдельный опкод, который создаёт пикап оружия и т.п.