shape1
shape2
shape3
shape4
shape7
shape8

Оптимизация


Mirex

Руководитель проекта
Команда портала
Гл.Администратор
Золотой
21.12.2013
2 512
3
550
0
29
definehost.ru
Скриптер
Часть 1. Циклы

for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
SetPlayerArmour(i, 100.0);
}

В этом цикле всем игрокам сервера максимально быстро установится 100 единиц брони. А теперь давайте разберём интересные моменты по "кусочкам":

new i = GetMaxPlayers() - 1; - данная запись создаст переменную, величина которой будет равняться количеству слотов, записанных в конфиге сервера, но так как всего 800 игроков, то ID будут от 0 до 799, поэтому -1.

i != 0 - самое быстрое сравнение - не равно. В нашем случае мы ждём момента, пока уменьшаемая переменная не достигнет нуля.
--i - префиксный декремент, который сначала уменьшит переменную, а потом подставит её в цикл. (если писать i--, то нужно выполнять сравнение i != -1). Почему именно он? Потому что уменьшение переменной происходит быстрее, чем увеличение.
if(!IsPlayerConnected(i)) continue; - если игрок отсутствует на сервере, сразу переходим к следующей итерации, игнорируя дальнейшие условия.
if(IsPlayerNPC(i)) continue; - если игрок - бот, запускаем следующую итерацию.
 
В случае, когда нужно найти циклом игрока с определённым именем:
new PlayerName[MAX_PLAYER_NAME]; // вынесем из цикла, дабы не создавать бесполезные переменные
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
GetPlayerName(i, PlayerName, sizeof(PlayerName));
if(strcmp(PlayerName, "OKStyle", false, MAX_PLAYER_NAME) != 0) continue; // если не нашелся, переключаем итерацию
SetPlayerArmour(i, 100.0);
break; // обрываем цикл при найденном игроке
}

В данном цикле мы максимально быстро выдадим игроку с ником OKStyle 100 единиц брони.
 
Как вы видите, MAX_PLAYERS вообще отсутствует.
 
Часть 2. Имя игрока

До этого момента некоторые из вас могли знать только о двух вариантах получения имени игрока:

а) Каждый раз функцией GetPlayerName, вида:

new PlayerName[MAX_PLAYER_NAME];
GetPlayerName(playerid, PlayerName, sizeof(PlayerName)) 
, где приходилось каждый раз создавать переменную.

б) Каждый раз функцией GetPlayerNameEx (например), вида:
GetPlayerNameEx(playerid)
, которая возвращала string с именем игрока с указанным id.
 
Сейчас я расскажу о небольшом "трике", который позволит быстро обращаться к имени заданного игрока.
 
В ваш enum с данными игрока нужно внести переменную:
Код:
pName[MAX_PLAYER_NAME]
В OnPlayerConnect:

GetPlayerName(playerid, PlayerInfo[playerid][pName], MAX_PLAYER_NAME); // мы не используем sizeof, т.к. он не может получить размер двойного массива.

И всё! Теперь в любом месте кода вы можете использовать эту переменную. Например:

printf("%s был замечен на сервере", PlayerInfo[playerid][pName]);

Как вариант, вы можете использовать PVar'ы, но в этом случае вам всегда нужно будет создавать переменную для извлечения данных и PVar'а. Так что этот вариант - не вариант. Однако запомните, что PVar'ами, созданными в моде, можно оперировать в ФС, без их объявления, и наоборот - созданными PVar'ами из ФС можно оперировать в моде (например, в фс в PVar записывается имя, в моде можно извлекать из PVar'а имя).
 
Часть 3. Public vs Stock

Тут всё просто. Для public-функции нужен форвардинг (forward) и служит она для использования в таймерах или в Call*-образных функциях (CallLocal..., CallRemote...). Stock служит для вне'Call'овых и внетаймерных функций. Не использованный в коде stock компилятор не записывает в amx, в этом случае я просто рекомендую удалять stock'и, которые в моде не используются, чтобы не засорять сорсы.
 
Могу порекомендовать такой макрос, который позволяет не форвардить функции:
Код:
#define fpublic %1(%2) forward %1(%2); public %1(%2)
Использовать (например):

fpublic SaveAccounts()
{
//действие
}

Часть 4. Условие перебора

Я думаю, что уже все поняли, что switch куда быстрее, чем if/else if, а if/else if быстрее, чем if/if.

 
Давайте разберем OnDialogResponse на примере:

switch(dialogid)
{
case 0:
{
if(!response) return 1;
switch(listitem)
{
case 0: {} //действие
}
}
}

Важно: не забывайте после последнего case дописывать:

default: {} // тут стандартное действие для элементов, которые вы не указали в переборе (если его нет, пишу то же самое, что в case 0)

Полезно: в case можно использовать несколько значений сразу, если них существуют одинаковые действия:

case 1..3: {} // от 1 до 3х
case 1, 2, 5, 8: {} // для 1, 2, 5 и 8 элемента

Немного об inputtext. Будет полезно и для команд (в OnPlayerCommandText - cmdtext):

if(inputtext[0] == EOS) return SendClientMessage(playerid, 0xFF0000FF, "Поле пустое!"); // EOS - End of String/Конец строки

И запомните главное: ставить задефайненную константу или цифру - дело ваше, в игре это никак не отразится, т.к. компилятор при компилировании ставит цифру в тех местах, где вы использовали обозначенную константу. Обычно константы создают для удобства программера.

Автор: OKStyle 

 

SHOROOP

Освоившийся
Пользователь
30.01.2014
58
56
0
32
Скриптер
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
SetPlayerArmour(i, 100.0);
}
 Вот конкретно с таким методом нужно быть внимательнее, и особенно - с условием выхода из цикла.

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

Вот, к примеру:

Код:
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
   if (i == 0) i = -2;
}
Условие выхода не срабатывает при i = 0. Мы присваиваем ему новое значение i = -2, которое снова не попадает под условие выхода. Здравствуй, бесконечный цикл.