Pawn pagrindai
- Funkcijų kvietimas
- Funkcijos
- Kintamieji
- Ciklai
- Direktyvai
Funkcijų kvietimas
Kai naudojame funkciją sakome kad "kviečiame funkciją" (calling a function). Tai įprastas terminas ir toliau naudosiu jį.
Funkcijos su parametrais gali skirtingai perduoti duomenis priklausomai nuo jų tipo:
stock Function()
{
new int = 5; // Sukuriam kintamajį "int" ir priskiriam jam reikšmę "5"
funca(int); // Iškviečiame funkciją "funca"
printf("Int = %d",int);
//Funkcijoje "printf" matysime rezultatą: 5
}
stock funca(int) //Funkcija turi parametrą "int".
{
int = 69; // Priskiriam kintamajma naują reikšmę, "69"
}
Kaip matome pavyzdyje viršuje, funkcijose su Integer(žr. žemiau.) tipo parametrais, gaunamos kintamųjų kopijos. T.y. funkcijoje "funca" kintamasis "int" yra kitas nei funkcijoje "Function".
O dabar pabandykime tą patį su masyvu:
stock Function()
{
new array[1]; // Sukuriame 1 dyždio masyvą*
array[0] = 1; // 0 masyvo elementui priskiriame skaičių "1"
funcb(array); // Iškviečiam funkciją "funcb" ir duodame jei parametrą - mūsų masyvą
printf("Array[0] = %d",array[0]);
//funkcijoje "printf" maystime rezultatą: 41
}
stock funcb(array[]) // Funkcija su masyvo tipo parametru
{
array[0] = 41; // Masyvo 0-ajam elementui priskiriam skaičių "41"
}
* Vieno dydžio masyvas yra betikslis.
Šiuo atveju, pasikeičia ir originalas. Kai perduodame masyvus į funkcijas, pakeičiame patį kintamąjį, kopijos nėra Toks kintamųjų perdavimas vadinamas "passed by reference", kuris reiškia kad perduodamas tik kintamojo nuoroda, t.y. adresas atmintyje...
Puikus to pavyzdys yra plačiai naudojama funkcija GetPlayerName.
new name[ MAX_PLAYER_NAME ];
GetPlayerName(playerid, name, sizeof(name));
Ši funkcija užpildo duotą masyvą žaidėjo vardo raidėmis.
Funkcijos
Funkcija yra kodo gabalas kuris gali būti kviečiamas iš betkur. Didelė Pawn scriptinimo ir sudeda būtent iš jų. Jos gali turėti parametrų(žiūrėti žemiau),o gali ir neturėti, jos gali gražintį kokį nors kintamajį(ar net kelis).
Panagrinėkime keletą native funkcijų:
SetPlayerHealth(playerid, Float:health);
Ši funkcija nieko negražina ir turi du parametrus: playerid ir health. Kad ji veiktu jums juos reikia užpildyti, playerid tai yra žaidėjo id kuriam taikysime funkcija (dažniausiai ir būna playerid), o štai „Float:Health“ turime pakeisti į skaičių: tokį kiek gyvybių norime suteikti žaidėjui.
Kita funkcija:
GetPlayerInterior(playerid);
Ši funkcija turi tik vieną parametrą. Bet ji gražina žaidėjo interjero ID. Tačiau norėdami funkcija kaip pavyzdyje, funkcija atlikus savo darbą jums duos interjero ID, o jis tiesiog pradings...Kodėl? Todėl nes mes juo niekam nepriskiriame.
Jį priskirti galima į kintamąjį.
new interior;
interior = GetPlayerInterior(playerid);
Dabar kintamajame „interior“ bus skaičius rodantis kuriam interjere yra žaidėjas.
Aukščiau nagrinėjom native Pawn funkcijas. Bet jų kartais būna per mažai, laimei, galima susikurti savo. Yra trys būdai: stock, public bei paprasta funkcija. Pažiūrėkime į stock funkciją:
stock SendMessageToAdmin(msg[])
{
for(new i; i < MAX_PLAYERS; i++)
{
if(IsPlayerAdmin(i))
{
SendClientMessage(i,0xFF0000FF,msg);
}
}
return 1;
}
Ši funkcija turi vieną parametra: string tipo „msg“. Ką ji padaro? Ji pereina per visus žaidėjus ir patikrina ar žaidėjas administratorius, jeigu taip nusiunčia jam žinutę su duotu tekstu. Ir tada gražina „1“ norėdama pasakyti kad darbą atliko (dažniausiai niekas nesidomi ar ji ką nors gražino)
Stock funkcijos gali gražinti betkokio tipo kintamąjį taipat jos parametrai gali būti betkokiu tipo kintamieji. Šios funkcijos taipat gali gražinti ne vieną kintamąjį ar turėti neprivalomus parametrus.
Funkcijų tipai
Funkcijas galima kurti keliais būdais, tai yra tiesiog rašant pavadinimą, su žodžiu "stock" bei "public" funkcijos. Senuose bei jūsų pamėgtuose GRP moduose visoms funkcijoms naudojamas "public" būdas... Ko tikrai nereikia ir tai daryti yra beprasmiška.
stock
Šio tipo funkcijas kompiliatorius ignoruos tol kol jos nebus panaudotos, todėl jeigu ji nebus panaudota jos net nebus .amx faile. Net jei joje bus klaidų, kol ji niekur nebus kviečiama kompiliatorius klaidų nerodys. Šis funkcijų tipas naudojamas dažniausiai
public
Pirmiausia, svarbu žinoti jog šios funkcijos turi būti taipat parašytos su "forward"(pvz žemiau). Šio tipo funkcijos negali gražinti "string" tipo kintamųjų. Didžiausias skirtumas tarp šių ir stock funkcijų tai kad kompiliuojant, .amx faile išsaugomas jos pavadinimas, NE atminties adresas kaip stock funkcijose, bet to žinoti nereikia. Tiesiog funkcijose kaip SetTimer/Ex, CallLocalFunction,CallRemoteFunction negali kviesti "stock" funkcijų.
forward funkcija();
public funkcija()
{
// kodas
}
//VISOS public funkcijos turi turėti ir "forward". Jis gali būti bet kur kode, bet įprasta
//jį rašyti prie funkcijos arba darbo viršuje. Atkreipkite dėmesį į tai kad forward eilutė
//baigiasi kabliataškiu,o public ne.
Paprastos funkcijos
Šios funkcijos neturi jokio specialaus žodelio. Jos tiesiog rašomos: pavadinimas ir parametrai(pvz žemiau). Jeigu jos nebus panaudotos gausite "symbol is never used" įspėjimą.\
funkcija()
{
//kodas
}
Kintamieji
Kintamasis Pawn kalboje yra 4 baitai atminties, kurioje gali būti laikoma/perašoma/trinama informacija. 4 baituose(vienam kintamajam) galima laikyti skaičiuos nuo -2147483648 iki 2147483647. Masyvas yra keli kintamieji pasiekiami per indeksus. String irgi yra masyvas, kurio kiekviename indekse laikoma viena raidė.
Integer
Sukurti įprastą kintamąjį galime taip:
new kintamasis;
Tai sukurs kintamąjį kurio reikšmė yra 0. Kuriant kintamąjį galima iškarto nustatyto jo reikšmė:
new kintamasis = 38
Dabar kintamajame bus skaičius „38“. Kelis kintamuosius galima kurit eilutėje:
new kintamasis,kintamasis2=5,kintamasis3=452;
Kintamųjų turimą reikšmę galima įvairiai redaguoti pvz:
new kintamasis = 6;
Kintamasis = Kintamasis + 3;
Tai prie kintamojo reikšmės pridės 3 ir gautąją reiškmę „sudės“ į tą patį kintamajį. Tai galima parašyti ir lengviau:
new Kintamasis=6;
Kintamasis +=3;
Tą patį galima daryti ir su kitais veiksmais. Pridedant ar atimant vienetą kodą galima dar labiau sutrumpinti:
new Kintamasis = 8;
Kintamasis++;
Tai pridės vienetą prie turimos reikšmės (8) ir „sudės“ viską atgal. Taspats veikia su atimtimi bet žinoma pliusus keičia minusai.
Float
Kaip pastebėjote paprasti kintamieji gali laikyti TIK sveikus skaičius, t.y. skaičius kurie neturi vertės po kablelio. Ateina laikas kai naudosime skaičius su kableliu, tada naudojame „Float“ tipo kintamuosius. Float kintamieji yra kuriami kitaip:
new Float:Kintamasis = 3.145415;
String
Dėja skaičiais visada neapsiribosim. O raidžių į paprastus ar Float tipo kintamuosius nesutalpinsime. Tam naudojami String tipo kintamieji. Jų kūrimas šiek tiek kitoks. Jūs turite iš anksto žinoti kiek reikės raidžių taipat pridėti dar vieną cell skirtą „Null termination character“ skamba keistai be tai tėra skaičius 0 kuris reiškia string pabaigą(jums jo prirašinėti niekur nereikia). PVZ:
new string[10];
string = "Labas";
Tai sukurs String kintamąjį skirta 9 simboliams ir Null character. Su String tipo kintamaisiais galima nuveikti daugybę dalykų kas jau reikalauja atskiros temos. Antra eilutė nustatyts string reikšmę į "Labas".
Masyvo viduje viskas atrodytų taip:
Indeksas | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
Vertė | L | a | b | a | s | \0 | \0 | \0 | \0 | \0 |
"Null terminator character" yra rašomas \0.
Žinoma masyvo viduje negali būti raidžių, kadangi Pawn yra betipė kalba. Net ir tas žodis "Labas", yra string viduje yra laikomas skaičiais. Pawn naudoja ASCII vertes(ASCII lentelė). Todėl iš tikrųjų tas masyvas atrodo taip:
Indeksas | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
Vertė | 76 | 97 | 98 | 97 | 115 | 0 | 0 | 0 | 0 | 0 |
Tuo galima labai smagiai pasinaudoti.
new string[ 6 ];
string = "Labas";
string[ 0 ] -= 5;
// L raidė yra 76.
// Atimame 5 gauname 71
// 71 yra raidė G
// Dabar mūsų string yra "Gabas"
Boolean
Dar vienas kintamųjų tipas yra "Boolean" arba sutrumpintai "bool". Šie kintamieji gali turėti tik 2 reikšmes: true arba false ( tiesa arba netiesa). Juos sukuriame taip:
new bool:Kintamasis;
Iškarto sukūrus jie turi reikšmę - false. Kaip ir visų kintamųjų, jų reikšmė galite bet kada keisti:
new bool:Kintamasis;
Kintamasis = true;
Jiems negalima priskirti nei raidžiū, nei skaičių.
Kūrimas
Kintamūjų rūšis ir naudojama aptarėme, bet ko nepaminėjau (nors parodžiau) tai kaip juos sukurti. Viršuje naudojome žodelį "new", tai yra pats paprasčiausias būdas sukurti kintamąjį, jų yra daugiau bet pagrindis vistiek lieka "new".
Visgi norėčiau trumpai apžvelgti kitus:
const
Jis skirtas kurti kintamiesiems kurie negalės būti pakeisti. Kaip suprasti? Tai nereiškia kad vieną kartą parašius jis toks liks visą gyvenimą. Koks kintamasis jeigu jis negali kisti? Šie kintamieji kažkiek per ilgą laiką sutaupo resursų. Kam jų reikia? Pavyzdžiui spawn koordinatėms, orų ciklui ar kažkam kas nekis.
Keli blogi pavyzdžiai:
const Kintamasis; // Gausite: " error 001: expected token: "=", but found ";"" klaidą.
const Kintamasis = 3;
Kintamasis = 5; // Gausite du error. CONST kintamieji, negali būti pakeisti.
Tai tiek apie "constant" kintamuosius.
static
šie kintamieji pasižymi tuo kad nepraranda vertės tarp iškvietimų. Jeigu jūs jį sukursite kaip globalų kintamąjį, jis nieko neišsiskirs nuo paprasto globalaus kintamojo. Bet jeigu jį sukursite funkcijoje, jis funkcijai baigus darbą visdar turės savo vertę ir jei ta funkcija bus vėl iškviesta, jis bus tokspat. Keli pavyzdžiai:
stock Skaitliukas()
{
static Skaitliuk;
Skatiliuk++;
printf("Skaitliukas rodo: %d",Skaitliuk);
return 1;
}
OnPlayerConnect(playerid)
{
Skaitliukas();
return 1;
}
Jei būtumę naudoję "new" funkcija "Skaitliukas" visada rašytų "Skaitliukas rodo: 1", bet kadangi tai yra "static" kintamasis, jis po kiekvieno iškvietimo paaugs vienu.
stock
Šių kintamųjų paslaptis ta kad jeigu jų nepanaudosi nebus "Symbol is never used" įspėjimo. Nieko daugiau su jais.
Tai tiek apie static,const ir stock kintamuosius. Taipat šių žodelių gali būt ne vienas kaip "stock static" ar "new static const"
Ciklai
Kartais tenka rašyti panašų kodą daug kartų, dėl to buvo išrasti ciklai(labai supaprastintai kalbant). Kodo blokas(gali būti ir vienas sakinys) yra vykdomas nurodytą skaičių kartų arba tol kol patenkinama nurodyta sąlyga. Viskas paaiškės prie konkrečių ciklų ir pavyzdžių.
while
Šiame cikle esantis kodas buvo vykdomas tol kol nurodyta sąlyga bus teisinga. Dažniausiai naudojama, kai konkrečiai nežinoma kiek kartų reikia įvykdyti kodą.
new skaicius = 5;
while(skaicius > 0) // Skliaustuose nurodyta ciklo sąlyga. Kol skaicius bus daugiau už nulį, tol bus vykdomas kodas.
{
printf("Skaicius yra:%d", skaicius);
skaicius--;
}
// Gausime tokį rezultatą:
Skaicius yra:5
Skaicius yra:4
Skaicius yra:3
Skaicius yra:2
Skaicius yra:1
Po to, vėl tikrinant sąlygą, kintamasis bus lygus nuliui, o tai NĖRA daugiau už nulį. Todėl bus vykdomas kodas toliau.
for
Naudojant šį ciklą, reikia nurodyti kiek kartų bus vykdomas kodas. Jo kūrime dažniausiai sukuriamas dar vienas naujas kintamasis(gali būti sukurtas ir ankščiau).
for(new i = 0; i < MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
printf("Žaidėjas %d yra prisijungęs", i);
}
}
// Išnagrinėkime pirmąją eilutę.
new i = 0; // Naujo kintamojo i sukūrimas duodant jam pradinę reikšmę - 0. Pavadinimas gali būti bet koks, kintamasis gali būti sukurtas ir aukščiau
i < MAX_PLAYERS; // Nurodoma kiek kartų bus vykdomas kodas. Šiuo atveju kol i bus mažesnis nei MAX_PLAYERS. Gali būti ir daugiau,daugiau/mažiau lygu, nelygu ar konkretus skaičius.
i++ // Nurodoma kad kas kartą įvykdžius kodą i reikia padidinti vienu. Gali būti ir i +=2 ar i += 3 ir t.t. Skaičiuojant atgal gali būti ir i-- ar i -= 2 ir t.t.
// Jei nepakeistume kintamojo reikšmės, ciklas niekada nesibaigtų. Nors keisti jo reikšmę galima ir ciklo kodo bloke.
do-while
Šis ciklas priešingai nei kiti, pirmiausia atlieka kodą, o po to tikrina ar sąlyga atitinkama. Naudojant šį ciklą esame garantuoti kad kodas bus įvykdytas bent vieną kartą.
new skaicius = 5;
do
{
printf("Skaicius yra:%d",skaicius);
skaicius--;
}
while(skaicius > 0);
}
// Gausime tokį rezultatą:
Skaicius yra:5
Skaicius yra:4
Skaicius yra:3
Skaicius yra:2
Skaicius yra:1
Nesibaigiantis ciklas
Iš tikrųjų tai yra tas pats for ciklas, tačiau jis kuriamas šiek tiek kitaip:
for( ;; )
{
// Ciklo kodas.
}
Šis ciklas niekada nesibaigs(nebent išjungus programą), kadangi Pawn yra viena-siūlė kalba(angl. single-threaded) šis ciklas naudojamas retai, kadangi kol vykdys ciklą daugiau nieko neveiks.
Direktyvai
Funkcijos, kintamieji skirti duoti instrukcijas serveriui, o štai direktyvai duoda instrukcija kompiliatoriui. Jie prasideda simboliu '#' (grotelės). Y_Less vien apie šias instrukcijas kompiliatoriui yra parašęs 7 temas. Minėtų temų didžioji dalis yra skirtos define direktyvui... Bet aš trumpai aprašysiu visus.
#define
Pakeičia tekstą į kitą tekstą. Jį panaudoti ko gero galima plačiausiai iš visų. Kaip minėjau apie jį yra parašęs Y_Less. Dalykas kurį norėčiau paminėti tai, kad rašant tekstą kurį norite keisti, jame negali būti tarpų. Viskas kas yra po tarpo laikoma pakaitalu.
Labai dažnai galima define panaudoti vietoj "const" kintamųjų taip sutaupant atminties.
#include
Prideda nurodyto failo turinį ten, kur buvo parašyta include eilutė. Visi yra naudoję šį direktyvą, dažnai nuo jo prasideda visa modifikacija.
#include <a_samp>
Tai prideda viską iš failo "a_samp"(failo tipas nesvarbus) į tą eilutę. O jei failas neegzistuoja, mums parodys klaidą:
fatal error 100: cannot read from file: "pavadinimas"
#tryinclude
Pabando pridėti nurodyto failo turinį. Beveik toks pat kaip "#include", tačiau jei failo nebus, nebus ir klaidos, priešingai nei su include.
#assert
Patikrina ar sąlyga yra teisinga, jei ne sustabdo kompiliavimą. Savaime suprantama sąlygoje kintamųjų ar funkcijų būti negali.
#define SKAICIUS 5
#assert SKAICIUS == 4
Tikiuosi žinote kad 5 nėra lygūs 4, todėl priėjus šią vietą kompiliavimas bus nutrauktas ir gausime tokią žinutę:
fatal error 110: assertion failed: 5 == 4
#error
Sustabdo kompiliavimą ir parašo nurodytą žinutę.
// Pirma eilutė patikrina ar NĖRA nurodytas "SKAICIUS"
#if !defined SKAICIUS
#error Nenurodytas skaicius
#endif
Ir šiame kode, "SKAICIUS" neegzistuoja, todėl kompiliuojant gausime klaidą:
fatal error 111: user error: Nenurodytas skaicius
#if #elseif #else ir #endif
Loginiai patikrinimai kompiliatoriui. Visiškai tas pats kas "if else if else" kode.
// Šauktukas prieš "defined" reiškia priešingą reikšmę
#if !defined SKAICIUS
#define SKAICIUS 5
#else
#if SKAICIUS > 5
#define SKAICIUS SKAICIUS-5
#endif
#endif
// Visiems #if turi būti ir #endif
#undef
Tai pašalins kažką ką prieš tai nurodė #define.
#define SKAICIUS 5
// Čia dar galima naudoti "SKAICIUS"
#undef SKAICIUS
#assert SKAICIUS
// Assert jau rodys klaidą, kadangi "SKAICIUS" nebėra, taspats būtų ir bandant jį kur nors naudoti
#endinput, #endscript
Nutraukia failo įkėlimą. Tai ko gero puikiausias būdas sustabdyti fail'ą nuo includinimo kelis kartus:
// Pagrindinė modifikacija
#include <failas>
#include <failas>
// Failas.inc
#if defined FAILAS_YRA
#endinput
#endif
#define FAILAS_YRA
Tai veiks, nes po pirmo karto kai bus pridėtas "failas.inc" "FAILAS_YRA" jau bus sukurtas, ir antrą kartą pridėtas jis nebus.
Neparašiau apie #pragma ir #emit, kadangi jie yra pakankamai platūs. Apie #pragma galite rasti wiki.
Jeigu kyla neaiškumų, ar kažką praleidau sakykite :)
Ron prieš 9 metus
Čia yra būtent tai, ko man ir reikėjo prieš keletą mėnesių! Nieko nemokantiems ir norintiems išmokti rekomenduoju perskaityti bei pasimokyti pagrindų iš šios pamokos, kadangi vargu ar rasite geresnę pamoką kurioje viskas aprašoma trumpai, bet gan aiškiai suprantamai :)