Доступ к готовым решениям

Переход в группу "Пользователь"

300.00 ₽
Одноразовый платёж
Быстрый переход в группу "Пользователи", без надобности написания постов и ожидания.

Покупка дает возможность:
Быть полноправным участником форума
Нормальное копирование кода
Создавать темы
Скачивать файлы
Доступ к архиву Pawno-Info

Урок Советы и трюки

pahan73rus

Умник
Приветствую друзья портала Pawno-Info.Ru
Я хотел бы поделиться несколькими вещами, которые я использую в SA:MP

Если у вас есть что можно ещё добавить, дайте мне знать!

Я буду обсуждать такие темы как:

  • Тернарный оператор
  • Int -> преобразование в Boolean
  • Самый быстрый перебор строки
  • Самый быстрый перебор игроков
  • Короткие функции
  • Несколько действия в одном stock'e
  • Как избавиться от глупых предупреждающих тегов
  • "Char-массивы"
  • Использование логических операторов для маленьких IF-условий
  • Эффективное управление памятью со stock const
  • Битовые флаги в enum


Тернарный оператор
Тернарный оператор является оператором, который принимает 3 аргумента; если первый аргумент верный, то аргумент стоящий за ним будет использоваться, если нет, то будет использоваться последний аргумент.

Структура оператора заключается в следующем:
состояние ? правда : ложь

Вот несколько примеров:
PHP:
if ( a == b )
    c = d;
else
    c = e;
С тернарным оператором:
PHP:
c = ( a == b ) ? d : e;
//  если-^  тогда-^   ^-иначе
Еще пару примеров
PHP:
SetPlayerColor( playerid, ( Team[ playerid ] == TEAM_ONE ) ? COLOR_RED : COLOR_BLUE );
// Установить цвет на красный, если игрок в TEAM_ONE, в противном случае цвет синий.

GivePlayerWeapon( playerid, ( IsMadnessEnabled() ) ? WEAPON_MINIGUN : WEAPON_FLOWER, 5000 );
// Если IsMadnessEnabled верно, дать игроку миниган

file = fopen( ( useSpecialFile ) ? ("special_file.txt") : ("normal_file.txt") );
// Строки должны быть в круглых скобках или компилятор PAWN будет выдавать ошибку.

// Вы также можете использовать тернарные операторы внутри тернарных операторов!
file = fopen( ( useFile == 1 ) ? ("file1.txt") : ( ( useFile == 2 ) ? ("file2.txt") : ( ( useFile == 3 ) ? ("file3.txt") : ("file0.txt") ) ) );

Int -> преобразование в Boolean
Если вам необходимо будет преобразовать Int в Boolen, то это делается вот так:
PHP:
new myInt = 50;
new bool:myBool = !!myInt;
myBool будет равняться 0, если myInt будет равняться 0, иначе myBool будет равняться 1


Самый быстрый перебор строки
По моему опыту, самый быстрый способ для перебора строки:
PHP:
for(new i, l = strlen( string ); i != l; i++)
{
    // ..
}

Самый быстрый перебор игроков
Обратите внимание, что Foreach быстрее, чем это.
PHP:
for(new slots = GetMaxPlayers(), i; i < slots; i++)
{
    if (!IsPlayerConnected(i))
        continue;
    
    // код для подключенных игроков
}

Короткие функции
Если функция имеет только один оператор - её можно объявить так:
PHP:
stock SomeFunction(someInput)
    return someArray[someInput / 2];

Несколько действия в одном stock'e
В Pawn, как вы могли заметить, все действия отделены точкой с запятой ( ; ) Если вы хотите, чтобы ваш код был только на одной строке, то вот как это сделать:
PHP:
stock KickEx(playerid, reason[])
    SendClientMessage(playerid, 0xC00000FF, "Вы были кикнуты! Причина:"), SendClientMessage(playerid, 0xC00000FF, reason), Kick(playerid);

stock DoStuff(playerid)
    return DoFirstThing(playerid), DoSecondThing(playerid), DoThirdThing(playerid), DoLastThing(playerid);

public OnPlayerRequestSpawn(playerid)
{
    if (!IsPlayerLoggedIn(playerid)) return SendClientMessage(playerid, 0xC00000FF, "Вы не авторизовались!"), 0;
    // Отправит сообщение и возвратит 0
    
    return 1;
}

Как избавиться от глупых предупреждающих тегов
Вставляя 3DText, и прочее внутри функций, таких как Printf, Format, SetTimerEx, CallLocalFunction, CallRemoteFunction вы могли заметить, что вы получаете предупреждающие теги.
Чтобы избавиться от них вам нужно сделать очистку тега, поставив знак подчеркивания в качестве тега.
Пример:
PHP:
new Text3D:t3dTest = Create3DTextLabel( .. ), Text:txTest = TextDrawCreate( .. );

printf("DEBUG: %d, %d", _:t3dTest, _:txTest);

Char-массивы
PAWN имеет функцию для доступа одиночных байтов в массивах, предназначенных для использования с упакованными строками. Большинство программистов SA-MP не используют упакованные строки.
Однако, вы можете использовать эти массивы для оптимизации памяти. Нормальный массив может хранить значения между -2147483648 и 2147483647. Вам не всегда это нужно, не так ли?
В упакованных строках можно хранить значения между 0-255. (Нет отрицательных значений) Когда вы знаете, что вам не нужны отрицательные значения и никогда не будете превышать 255, почему бы не сэкономить немного памяти?
PHP:
new bool:g_IsPlayerSomething[MAX_PLAYERS]; // 500 клеток * 4 байта на 1 клетку = 2000 байтов!
new bool:g_IsPlayerSomething[MAX_PLAYERS char]; // 500 клеток = .. 500 байтов!
Примечание!
При использовании этих массивов, вам нужно использовать фигурные скобки, в отличие от нормальных квадратных скобках.
Пример:
PHP:
public OnPlayerConnect( playerid )
{
    g_IsPlayerSomething{playerid} = false;
}

public OnPlayerSpawn( playerid )
{
    if (g_IsPlayerSomething{playerid})
    {
        // ..
    }
}

Использование логических операторов для маленьких IF-условий
Вы можете использовать логические операторы, как будто if-условие, но делать все это внутри одного условия!
Например:
PHP:
stock FIXES_valstr(dest[], value, bool:pack = false)
{
    static const cellmin_value[] = !"-2147483648";

    if (value == cellmin)
        pack && strpack(dest, cellmin_value, 12) || strunpack(dest, cellmin_value, 12);
    else
        format(dest, 12, "%d", value), pack && strpack(dest, dest, 12);
}
Как вы видите, мы используем логические операторы (&& и ||) просто в открытую.
Код выше, без этой маленькой уловки, будет выглядеть примерно так:
PHP:
stock FIXES_valstr(dest[], value, bool:pack = false)
{
    static const cellmin_value[] = !"-2147483648";

    if (value == cellmin) {
        if (pack)
            strpack(dest, cellmin_value, 12)
        else
            strunpack(dest, cellmin_value, 12);
    } else {
        format(dest, 12, "%d", value);
        
        if (pack)
            strpack(dest, dest, 12);
    }
}
Еще примеры:
PHP:
a && b();              // если a, запускаем b.
a && b() || c();       // если a, запускаем b. в противном запускаем c.
a || b();              // если неверно a, запускаем b.
a && b() || c && d();  // если a, запускаем b. в противном случае, если c, запускаем d.
a && b() && c();       // если a, запускаем b. если b неверно, запускаем c.

Эффективное управление памятью со stock const
Автор Y_Less.

"stock const".

Это то, что я обнаружил некоторое время назад. Все пишут:
PHP:
#include <a_samp>

main()
{
    print("hi");
    print("hi");
}
Если мы компилируем это с "-a" мы получаем:
PHP:
CODE 0	; 0
;program exit point
	halt 0

	proc	; main
	; line 4
	; line 5
	push.c 0 // тут
	;$par
	push.c 4
	sysreq.c 0	; print
	stack 8
	;$exp
	; line 6
	push.c c // тут
	;$par
	push.c 4
	sysreq.c 0	; print
	stack 8
	;$exp
	zero.pri
	retn


DATA 0	; 0
dump 68 69 0 68 69 0 

STKSIZE 1000
Я пометил две линии. Первая толкает число "0", вторая толкает номер "12" ("C"). "print", поскольку это первый (и единственный) параметр, получает адрес в строку print. Все строковые литералы преобразуются в данные и хранятся в глобальной памяти (я не буду вдаваться в детализацию истинных последствиях этого, но достаточно сказать, чтобы вы изменили их). "dunp" является нынешней глобальной памятью для этой крошечной программы:
PHP:
dump 68 69 0 68 69 0
Если перевести эти цифры в ASCII мы вдруг видим:
PHP:
dump 'h' 'i' '\0' 'h' 'i' '\0'
Т.е. наши две "hi" строки - две копии одной и той же строки. Это не очень эффективно, так что давайте улучшать это управления памятью:
PHP:
#include <a_samp>

stock const
    C_HI[3] = "hi";

main()
{
    print(C_HI);
    print(C_HI);
}
PHP:
CODE 0	; 0
;program exit point
	halt 0


DATA 0	; 0
dump 68 69 0 

	proc	; main
	; line 7
	; line 8
	push.c 0
	;$par
	push.c 4
	sysreq.c 0	; print
	stack 8
	;$exp
	; line 9
	push.c 0
	;$par
	push.c 4
	sysreq.c 0	; print
	stack 8
	;$exp
	zero.pri
	retn


STKSIZE 1000
Расположение "dump" изменилось, но это не важно - большая программа будет иметь много "dump" по всему коду.
Несмотря на то, что мы используем переменную, это еще ой какой эффективный и мудрый код, потому что мы все еще используем постоянные строки. Тем не менее, требования к памяти для хранения двух строк сократилось вдвое.
Вы можете также использовать "new const" или "statick stock const", но "stock const", вероятно, лучше для этого кода, чтобы избежать предупреждения.


Битовые флаги в enum
Вы знаете, что можно хранить 32 true/false значений в одной переменной? Вы не только сэкономите память, но также получаете меньше беспорядка в коде.
Вам не обязательно знать, как работает двоичная система счисления. Однако, я рекомендую вам узнать. Вы можете прочитать в этой теме, если вы заинтересованы.
Если у вас есть, скажем, 100 true/false (BOOL) переменных для игроков, вы используете 195 КБ память. Однако, если бы вы использовали 4 массива с битовыми флагами, только 8 КБ памяти будут использоваться. Из этого получаем, что мы сэкономили 187 KБ памяти!
Вот примеры, содержащие макросы для упрощения использования:

PHP:
// Использование для всех макросов: BitFlag_X(variable, flag)
#define BitFlag_Get(%0,%1)            ((%0) & (%1))   // Возвращает ноль (false), если флаг не установлен.
#define BitFlag_On(%0,%1)             ((%0) |= (%1))  // Включить флаг
#define BitFlag_Off(%0,%1)            ((%0) &= ~(%1)) // Выключить флаг
#define BitFlag_Toggle(%0,%1)         ((%0) ^= (%1))  // Переключить флаг (поменять true/false)

enum PlayerFlags:(<<= 1) {
    // Важно, чтобы вы не забыли поставить "= 1" на первом флаге. Если этого не сделать, все флаги будут 0.
    
    PLAYER_IS_LOGGED_IN = 1,   // 0b00000000000000000000000000000001
    PLAYER_HAS_GANG,           // 0b00000000000000000000000000000010
    PLAYER_CAN_BUY_PROPERTIES, // 0b00000000000000000000000000000100
    PLAYER_BLABLA_1,           // 0b00000000000000000000000000001000
    PLAYER_BLABLA_2,           // 0b00000000000000000000000000010000
    PLAYER_BLABLA_3,           // 0b00000000000000000000000000100000
    PLAYER_BLABLA_4,           // 0b00000000000000000000000001000000
    PLAYER_BLABLA_5,           // 0b00000000000000000000000010000000
    PLAYER_BLABLA_6,           // 0b00000000000000000000000100000000
    PLAYER_BLABLA_7,           // 0b00000000000000000000001000000000
    PLAYER_BLABLA_8,           // 0b00000000000000000000010000000000
    PLAYER_BLABLA_9,           // 0b00000000000000000000100000000000
    PLAYER_BLABLA_10,          // 0b00000000000000000001000000000000
    PLAYER_BLABLA_11,          // 0b00000000000000000010000000000000
    PLAYER_BLABLA_12,          // 0b00000000000000000100000000000000
    PLAYER_BLABLA_13,          // 0b00000000000000001000000000000000
    PLAYER_BLABLA_14,          // 0b00000000000000010000000000000000
    PLAYER_BLABLA_15,          // 0b00000000000000100000000000000000
    PLAYER_BLABLA_16,          // 0b00000000000001000000000000000000
    PLAYER_BLABLA_17,          // 0b00000000000010000000000000000000
    PLAYER_BLABLA_18,          // 0b00000000000100000000000000000000
    PLAYER_BLABLA_19,          // 0b00000000001000000000000000000000
    PLAYER_BLABLA_20,          // 0b00000000010000000000000000000000
    PLAYER_BLABLA_21,          // 0b00000000100000000000000000000000
    PLAYER_BLABLA_22           // 0b00000001000000000000000000000000
};

new
    Создание массива с тем же тегом как enum
    PlayerFlags:g_PlayerFlags[MAX_PLAYERS]
;

public OnPlayerConnect(playerid) {
    // 0 - Все флаги отключены (false)
    g_PlayerFlags[playerid] = PlayerFlags:0;
}

public OnPlayerLogIn(playerid) {
    BitFlag_On(g_PlayerFlags[playerid], PLAYER_IS_LOGGED_IN);
    
//  Без макроса:
//  g_PlayerFlags[playerid] |= PLAYER_IS_LOGGED_IN;
}

public OnPlayerJoinGang(playerid) {
    BitFlag_On(g_PlayerFlags[playerid], PLAYER_HAS_GANG);
    
//  Без макроса:
//  g_PlayerFlags[playerid] |= PLAYER_HAS_GANG;
}

public OnPlayerLeaveGang(playerid) {
    BitFlag_Off(g_PlayerFlags[playerid], PLAYER_HAS_GANG);
    
//  Без макроса:
//  g_PlayerFlags[playerid] &= ~PLAYER_HAS_GANG;
}

public OnPlayerUpdate(playerid) {
    
    BitFlag_Toggle(g_PlayerFlags[playerid], PLAYER_BLABLA_19);
    
    if (BitFlag_Get(g_PlayerFlags[playerid], PLAYER_BLABLA_19)) {
        DoSomething();
    }
    
//  Без макроса:
//  g_PlayerFlags[playerid] ^= PLAYER_BLABLA_19;
//  
//  if (g_PlayerFlags[playerid] & PLAYER_BLABLA_19) {
//      DoSomething();
//  }
}

Спасибо за внимание, с вами был pahan73rus, удачного скриптинга ;)

Автор оригинального урока: Slice
Перевод на понятный русский язык: pahan73rus
 
Последнее редактирование:
Сверху Снизу