Quelques règles basiques pour bien scripter.
5 participants
Page 1 sur 1
Quelques règles basiques pour bien scripter.
Voici un topic qui énumère quelques règles simples que tout bon scripteur peut (doit ?) appliquer.
Si je fais ce topic, c'est simplement parce que beaucoup de problèmes sont liés à la non application de certaines de ces règles (qui peuvent être adaptées selon le cas et les humeurs de chacun, je ne veux aucunement m'imposer).
L'INDENTATION
L'indentation d'un script n'est pas là que pour faire joli, elle augmente le lisibilité du code et permet de détecter certaines erreurs facheuses très difficilement détectable dans un script non indenté.
L'indentation doit devenir un réflexe au moment de l'écriture du script, pas après coup car ça peut être compliqué et source d'erreurs.
Voici un exemple pris dans un post de ce forum :
Ce bout de script compile et s'exécute.
Mais si l'on teste ce bout de script, on se rendra compte que la commande /dm ne fonctionne pas.
Si on indente correctement ce bout de script, l'erreur saute au yeux :
LES ACCOLADES
Les accolades servent à délimiter des bloc d'instructions dans un script.
On peut en mettre autant qu'on veut du moment qu'une accolade ouvrante correspond à une accolade fermante de même niveau.
Même si elles ne paraissent pas nécessaire, il faut prendre l'habitude de toujours mettre les accolades.
Prenons un bout de script :
Le code correct est :
L'exemple en question est simple et beaucoup d'entre vous n'aurait pas fait l'erreur mais je peux vous assurer que dans un script plus compliqué, c'est une erreur que l'on peut faire facilement et qui se révèle très difficile à trouver.
LA FACTORISATION
Quand je développe (en C/C++ ou en PAWN), j'applique toujours la règle la plus importante à mes yeux : la factorisation.
Ce que j'appelle factorisation, c'est mettre en "facteur" la partie commune à plusieurs bouts de code, comme en math lorsque que l'on factorise une expression.
Exemple en math : [tt](x+1)(x+2)+(x+1)(2x+4) = (x+1)(3x+6) = 3(x+1)(x+2)[/tt]
Dans le cas d'un script, on cherchera à créer une fonction à partir d'un bout de script que l'on retrouve plus d'une fois à l'identique dans le script.
Exemple :
Dans le code ci dessus, on trouve deux parties de code identique (aux valeurs des paramètres près).
Si je veux par exemple positionner l'angle du joueur quand il est à pied, il faut que je corrige à deux endroits (plus si j'ai plus de commandes de téléport).
Je vais donc factoriser pour faire une fonction que j'appellerais [tt]TeleporterJoueur[/tt] :
Le code du dessus peut donc maintenant s'écrire :
On voit tout de suite les avantages d'un telle factorisation :
- Si un téléport fonctionne alors tous les autres fonctionneront (sauf si les coordonnées sont fausses),
- L'ajout d'un nouveau téléport est simple, rapide et sans risque,
- La correction d'un bug ne conduit qu'à la modification de la fonction [tt]TeleporterJoueur[/tt], pas des 10 ou 15 commandes de téléport,
- L'ajout d'une nouvelle fonctionnalité dans le téléport est très simple à mettre en œuvre et à tester,
- Le code est plus concis et lisible donc plus facilement corrigible et modifiable.
Je le répète, la factorisation est la règle la plus importante de tout bon développeur.
Et elle peut s'appliquer dans de très nombreux cas, pas seulement avec les téléports.
LES VARIABLES
Il existe deux type de variables en PAWN :
- Les variables locales (déclarées dans les fonctions, callback ou bloc d'instructions)
- Les variables globales (déclarées en dehors de toute fonction ou callback)
Les variables locales ne peuvent être utilisées que dans le bloc d'instructions où elles sont déclarées.
Les variables globales peuvent être utilisées dans toutes les fonctions ou callback du script.
Les variables locales ne posent pas de problème particulier car leur portée est limitée au bloc d'instructions dans lequel elles sont declarées.
Par contre, il faut faire attention avec les variables globales qui peuvent être modifiées partout dans le script.
Cette particularité peut être source d'erreurs quand une fonction ou callback modifie la valeur d'un variable globale pendant qu'un autre fonction ou callback est en train de la lire.
Il faut donc être très rigoureux dans l'utilisation que l'on fait de ce type de variables.
Chaque fois que c'est possible, on utilisera des variables locales.
Une bonne habitude consiste à déclarer toutes ses variables locale (même celle utilisées dans les boucles) en début de fonction ou de callback pour éviter les surprises à la compilation (voire à l'exécution).
Exemple qui ne compile pas :
En PAWN, il n'existe qu'un seul type de variable (contrairement au C/C++). Ce type par défaut est entier.
Mais il est possible d'utiliser un tag (étiquette) pour transformer cette variable en un autre type.
Le seul tag disponible en standard en PAWN est Float:. Il permet de transformer une variable de type entier en variable de type décimal (ou nombre à virgule flottante).
Exemple :
LES CONDITIONS
Les conditions sont la clé d'un code non linéraire. Elles permettent d'orienter le script en fonction de ce qui se passe sur le serveur.
Les conditions s'utilisent avec les mots clés (ou instructions) suivants : if, while, do/while et for.
Les conditions peuvent très vite devenir très compliquées à formuler et il ne faut pas hésiter à les éclater en plusieurs conditions.
Par exemple, on veut tester pour chaque joueur qui est connecté si il est passager d'un véhicule ou si il est à pied.
On pourrait écrire :
Il vaut donc mieux écrire :
Dans un test, les termes (séparés par && ou ||) sont évalués un par un et comparer à 1 (vrai).
Dans le cas d'un condition ne comportant que des &&, l'évaluation de cette condition s'arrête dès qu'un des termes est faux. Ceci peut avoir des conséquences inattendues.
Exemple :
Si a=2 et b=4 alors les test est vérifié et en sortie, a=2 et b=5 (puisqu'il sera incrémenté)
Si a<>2 et b=4 alors le test n'est pas vérifié mais en sortie, b est toujours égal à 4 car comme a n'est pas égal à 2, l'évaluation de la condition s'arrête et le code se poursuit après le bloc d'instructions du if.
Si je fais ce topic, c'est simplement parce que beaucoup de problèmes sont liés à la non application de certaines de ces règles (qui peuvent être adaptées selon le cas et les humeurs de chacun, je ne veux aucunement m'imposer).
L'INDENTATION
L'indentation d'un script n'est pas là que pour faire joli, elle augmente le lisibilité du code et permet de détecter certaines erreurs facheuses très difficilement détectable dans un script non indenté.
L'indentation doit devenir un réflexe au moment de l'écriture du script, pas après coup car ça peut être compliqué et source d'erreurs.
Voici un exemple pris dans un post de ce forum :
- Code:
if (strcmp("/tunning", cmdtext, true, 10) == 0)
{
if(IsPlayerInAnyVehicle(playerid)) // Si le joueur est dans un véhicule....
{
new Car = GetPlayerVehicleID(playerid), Model = GetVehicleModel(Car);
switch(Model) { case 448,461,462,463,468,471,509,510,521,522,523,581,586: return SendClientMessage(playerid,0xFF0000AA,"> Vous ne pouvez pas ajouter de composants dans des motos."); } //Si on ajoute des composants dans des motos ou vélos notre jeu crash...
ShowMenuForPlayer(tunning,playerid); // On active le menu "tunning"
TogglePlayerControllable(playerid,0); // On freeze le joueur
}
else{ // Sinon....
SendClientMessage(playerid,COLOR_GREEN,"> Vous devez être dans un véhicule");
}
if(strcmp(cmdtext, "/dm") == 0)
{
SetPlayerPos(playerid, -1452.3439, -531.1021, 13.5776);
SendClientMessage(playerid, COLOR_ORANGE, "Tu est au dm de la gare de SF");
return 1;
}
return 1;
}
Ce bout de script compile et s'exécute.
Mais si l'on teste ce bout de script, on se rendra compte que la commande /dm ne fonctionne pas.
Si on indente correctement ce bout de script, l'erreur saute au yeux :
- Code:
if (strcmp("/tunning", cmdtext, true, 10) == 0)
{
if(IsPlayerInAnyVehicle(playerid)) // Si le joueur est dans un véhicule....
{
new Car = GetPlayerVehicleID(playerid), Model = GetVehicleModel(Car);
switch(Model) { case 448,461,462,463,468,471,509,510,521,522,523,581,586: return SendClientMessage(playerid,0xFF0000AA,"> Vous ne pouvez pas ajouter de composants dans des motos.");
ShowMenuForPlayer(tunning,playerid); // On active le menu "tunning"
TogglePlayerControllable(playerid,0); // On freeze le joueur
}
else
{
SendClientMessage(playerid,COLOR_GREEN,"> Vous devez être dans un véhicule");
}
if(strcmp(cmdtext, "/dm") == 0)
{
SetPlayerPos(playerid, -1452.3439, -531.1021, 13.5776);
SendClientMessage(playerid, COLOR_ORANGE, "Tu est au dm de la gare de SF");
return 1;
}
return 1;
}
LES ACCOLADES
Les accolades servent à délimiter des bloc d'instructions dans un script.
On peut en mettre autant qu'on veut du moment qu'une accolade ouvrante correspond à une accolade fermante de même niveau.
Même si elles ne paraissent pas nécessaire, il faut prendre l'habitude de toujours mettre les accolades.
Prenons un bout de script :
- Code:
if (a==10)
b=52;
- Code:
if (a==10)
b=52;
c=49;
Le code correct est :
- Code:
if (a==10)
{
b=52;
c=49;
}
- Code:
if (a==10)
{
b=52;
}
L'exemple en question est simple et beaucoup d'entre vous n'aurait pas fait l'erreur mais je peux vous assurer que dans un script plus compliqué, c'est une erreur que l'on peut faire facilement et qui se révèle très difficile à trouver.
LA FACTORISATION
Quand je développe (en C/C++ ou en PAWN), j'applique toujours la règle la plus importante à mes yeux : la factorisation.
Ce que j'appelle factorisation, c'est mettre en "facteur" la partie commune à plusieurs bouts de code, comme en math lorsque que l'on factorise une expression.
Exemple en math : [tt](x+1)(x+2)+(x+1)(2x+4) = (x+1)(3x+6) = 3(x+1)(x+2)[/tt]
Dans le cas d'un script, on cherchera à créer une fonction à partir d'un bout de script que l'on retrouve plus d'une fois à l'identique dans le script.
Exemple :
- Code:
if (strcmp ("/mls", cmdtext, true) == 0)
{
new vehicleid;
if (IsPlayerInAnyVehicle (playerid))
{
vehicleid = GetPlayerVehicleId (playerid);
SetPlayerPos (playerid, 1.0, 2.0, 3.0);
SetVehiclePos (vehicleid, 1.0, 2.0, 3.0);
SetVehicleZAngle (vehicleid, 4.0);
PutPlayerInVehicle (playerid, vehicleid, 0);
SetCameraBehindPlayer (playerid);
}
else
{
SetPlayerPos (playerid, 1.0, 2.0, 3.0);
SetCameraBehindPlayer (playerid);
}
}
if (strcmp ("/msf", cmdtext, true) == 0)
{
new vehicleid;
if (IsPlayerInAnyVehicle (playerid))
{
vehicleid = GetPlayerVehicleId (playerid);
SetPlayerPos (playerid, 5.0, 6.0, 7.0);
SetVehiclePos (vehicleid, 5.0, 6.0, 7.0);
SetVehicleZAngle (vehicleid, 8.0);
PutPlayerInVehicle (playerid, vehicleid, 0);
SetCameraBehindPlayer (playerid);
}
else
{
SetPlayerPos (playerid, 5.0, 6.0, 7.0);
SetCameraBehindPlayer (playerid);
}
}
Dans le code ci dessus, on trouve deux parties de code identique (aux valeurs des paramètres près).
Si je veux par exemple positionner l'angle du joueur quand il est à pied, il faut que je corrige à deux endroits (plus si j'ai plus de commandes de téléport).
Je vais donc factoriser pour faire une fonction que j'appellerais [tt]TeleporterJoueur[/tt] :
- Code:
TeleporterJoueur (playerid, Float:X, Float:Y, Float:Z, Float:Angle)
{
new vehicleid;
if (IsPlayerInAnyVehicle (playerid)
{
vehicleid = GetPlayerVehicleId (playerid);
SetPlayerPos (playerid, X, Y, Z);
SetVehiclePos (vehicleid, X, Y, Z);
SetVehicleZAngle (vehicleid, Angle);
PutPlayerInVehicle (playerid, vehicleid, 0);
SetCameraBehindPlayer (playerid);
}
else
{
SetPlayerPos (playerid, X, Y, Z);
SetCameraBehindPlayer (playerid);
}
}
Le code du dessus peut donc maintenant s'écrire :
- Code:
if (strcmp ("/mls", cmdtext, true) == 0)
{
TeleporterJoueur (playerid, 1.0, 2.0, 3.0, 4.0);
}
if (strcmp ("/msf", cmdtext, true) == 0)
{
TeleporterJoueur (playerid, 5.0, 6.0, 7.0, 8.0);
}
On voit tout de suite les avantages d'un telle factorisation :
- Si un téléport fonctionne alors tous les autres fonctionneront (sauf si les coordonnées sont fausses),
- L'ajout d'un nouveau téléport est simple, rapide et sans risque,
- La correction d'un bug ne conduit qu'à la modification de la fonction [tt]TeleporterJoueur[/tt], pas des 10 ou 15 commandes de téléport,
- L'ajout d'une nouvelle fonctionnalité dans le téléport est très simple à mettre en œuvre et à tester,
- Le code est plus concis et lisible donc plus facilement corrigible et modifiable.
Je le répète, la factorisation est la règle la plus importante de tout bon développeur.
Et elle peut s'appliquer dans de très nombreux cas, pas seulement avec les téléports.
LES VARIABLES
Il existe deux type de variables en PAWN :
- Les variables locales (déclarées dans les fonctions, callback ou bloc d'instructions)
- Les variables globales (déclarées en dehors de toute fonction ou callback)
Les variables locales ne peuvent être utilisées que dans le bloc d'instructions où elles sont déclarées.
Les variables globales peuvent être utilisées dans toutes les fonctions ou callback du script.
Les variables locales ne posent pas de problème particulier car leur portée est limitée au bloc d'instructions dans lequel elles sont declarées.
Par contre, il faut faire attention avec les variables globales qui peuvent être modifiées partout dans le script.
Cette particularité peut être source d'erreurs quand une fonction ou callback modifie la valeur d'un variable globale pendant qu'un autre fonction ou callback est en train de la lire.
Il faut donc être très rigoureux dans l'utilisation que l'on fait de ce type de variables.
Chaque fois que c'est possible, on utilisera des variables locales.
Une bonne habitude consiste à déclarer toutes ses variables locale (même celle utilisées dans les boucles) en début de fonction ou de callback pour éviter les surprises à la compilation (voire à l'exécution).
Exemple qui ne compile pas :
- Code:
UneFonction ()
{
new a=8;
if (a==8)
{
new b;
b=a+12;
}
a=b+5; // Le compilateur produira l'erreur "b n'est pas défini"
}
En PAWN, il n'existe qu'un seul type de variable (contrairement au C/C++). Ce type par défaut est entier.
Mais il est possible d'utiliser un tag (étiquette) pour transformer cette variable en un autre type.
Le seul tag disponible en standard en PAWN est Float:. Il permet de transformer une variable de type entier en variable de type décimal (ou nombre à virgule flottante).
Exemple :
- Code:
new Float:b = 12.84;
LES CONDITIONS
Les conditions sont la clé d'un code non linéraire. Elles permettent d'orienter le script en fonction de ce qui se passe sur le serveur.
Les conditions s'utilisent avec les mots clés (ou instructions) suivants : if, while, do/while et for.
Les conditions peuvent très vite devenir très compliquées à formuler et il ne faut pas hésiter à les éclater en plusieurs conditions.
Par exemple, on veut tester pour chaque joueur qui est connecté si il est passager d'un véhicule ou si il est à pied.
On pourrait écrire :
- Code:
for (i=0 ; i<MAX_PLAYERS ; i++)
{
if (IsPlayerConnected (i) && (IsPlayerInAnyVehicle (i) && GetPlayerState (i) == PLAYER_PASSENGER) || GetPlayerState (i) == PLAYER_ON_FOOT))
{
/* On fait quelque chose */
}
}
Il vaut donc mieux écrire :
- Code:
for (i=0 ; i<MAX_PLAYERS ; i++)
{
if (IsPlayerConnected (i))
{
if (IsPlayerInAnyVehicle (i))
{
if (GetPlayerState (i) == PLAYER_PASSENGER)
{
/* On fait quelque chose */
}
}
else if (GetPlayerState (i) == PLAYER_ON_FOOT)
{
/* On fait quelque chose */
}
}
}
Dans un test, les termes (séparés par && ou ||) sont évalués un par un et comparer à 1 (vrai).
Dans le cas d'un condition ne comportant que des &&, l'évaluation de cette condition s'arrête dès qu'un des termes est faux. Ceci peut avoir des conséquences inattendues.
Exemple :
- Code:
if (a==2 && ++b==5)
{
}
Si a=2 et b=4 alors les test est vérifié et en sortie, a=2 et b=5 (puisqu'il sera incrémenté)
Si a<>2 et b=4 alors le test n'est pas vérifié mais en sortie, b est toujours égal à 4 car comme a n'est pas égal à 2, l'évaluation de la condition s'arrête et le code se poursuit après le bloc d'instructions du if.
Re: Quelques règles basiques pour bien scripter.
Long tuto mais c'est bien tu nous apprend à scripter
Tupac- Administrateur
- Messages : 23
Date d'inscription : 04/01/2012
Age : 28
Localisation : WestGanton
Re: Quelques règles basiques pour bien scripter.
Je vais me laisser un bon petit weekend pour lire sa et bien comprendre sinon merci.
Dope™- Administrateur
- Messages : 7
Date d'inscription : 04/01/2012
Age : 30
Re: Quelques règles basiques pour bien scripter.
merci à toi sa va m'aidez . quand je pense que samir voulais pas m'aidez :/ '' il avais pas le temps enfin du genre sa ''
Underweb™- Modérateur
- Messages : 39
Date d'inscription : 04/01/2012
Age : 27
Localisation : ~ Montreuil 93100 ~
Re: Quelques règles basiques pour bien scripter.
Un joli copier/coller à la virgule près d'un topic que j'ai fait il y a longtemps sur GtaOnline.
Comme il semblerait que Weside ait oublié de citer ses sources, je le fais à sa place :
http://www.gtaonline.fr/forums/index.php/topic,6250.0.html
Cordialement
Syg
Comme il semblerait que Weside ait oublié de citer ses sources, je le fais à sa place :
http://www.gtaonline.fr/forums/index.php/topic,6250.0.html
Cordialement
Syg
Syg- Petit nouveau
- Messages : 1
Date d'inscription : 09/02/2012
Page 1 sur 1
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum
|
|