Quelques règles basiques pour bien scripter.

Aller en bas

Quelques règles basiques pour bien scripter.

Message par Weside le Mer 4 Jan - 18:19

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 :
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;
}
La commande /dm est incluse dans la commande /tunning.

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;
et imaginons, que dans le cas où a égale 10, je veuille aussi changer la valeur de c. Je rajoute donc une ligne à la suite :
Code:
if (a==10)
   b=52;
   c=49;
Mais ce bout de code ne fait pas ce qu'on attend de lui, il affecte systématiquement la valeur 49 à c, quelque soit la valeur de a.
Le code correct est :
Code:
if (a==10)
{
   b=52;
   c=49;
}
Et si dès le départ on avait écrit :
Code:
if (a==10)
{
   b=52;
}
l'erreur n'aurai pas pu être faîte.
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;
L'utilisation de ce type de variable consomme plus de ressource que pour le type par défaut, il ne faut donc les utiliser que quand c'est nécessaire.

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 */
   }
}
On voit très vite dans ce bout de code qu'il est très facile de se tromper dans la condition (parenthèses, opérateurs).

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 */
      }
   }
}
NB : Ici, on tombe dans un autre travers où l'on est obligé d'utiliser deux fois le même code (/* On fait quelque chose */) mais ce n'est qu'un exemple

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)
{
}
Dans ce bout de script, on test la valeur de a puis la valeur incrémentée de b.
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.
avatar
Weside
Webmaster
Webmaster

Messages : 38
Date d'inscription : 04/01/2012
Age : 26

Voir le profil de l'utilisateur http://gta-sanandreas.superforum.fr

Revenir en haut Aller en bas

Re: Quelques règles basiques pour bien scripter.

Message par Tupac le Mer 4 Jan - 18:37

Long tuto mais c'est bien tu nous apprend à scripter

_________________
I ain’t dead, I ain’t done, I ain’t scared,
I ain’t run,
but still I stand, no matter what people here I am.
avatar
Tupac
Administrateur
Administrateur

Messages : 23
Date d'inscription : 04/01/2012
Age : 23
Localisation : WestGanton

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Quelques règles basiques pour bien scripter.

Message par Dope™ le Mer 4 Jan - 18:49

Je vais me laisser un bon petit weekend pour lire sa et bien comprendre sinon merci. Very Happy
avatar
Dope™
Administrateur
Administrateur

Messages : 7
Date d'inscription : 04/01/2012
Age : 25

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Quelques règles basiques pour bien scripter.

Message par Underweb™ le Mer 4 Jan - 20:20

merci à toi sa va m'aidez . quand je pense que samir voulais pas m'aidez :/ '' il avais pas le temps enfin du genre sa ''

_________________
~ GTA San Andreas Une Communauté de Rêve ~
~ Modérateur du Forum GTA - San Andreas , Besoin d'aide ?
~ Je suis la pour vous aidez | Je suis la pour vous montrez le vrai RôlePlay ~
~ Underweb Un RôlePlay Fait Un RôlePlay Mieux ~
avatar
Underweb™
Modérateur
Modérateur

Messages : 39
Date d'inscription : 04/01/2012
Age : 22
Localisation : ~ Montreuil 93100 ~

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Quelques règles basiques pour bien scripter.

Message par Syg le Jeu 9 Fév - 9:41

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

Syg
Petit nouveau

Messages : 1
Date d'inscription : 09/02/2012

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Quelques règles basiques pour bien scripter.

Message par Contenu sponsorisé


Contenu sponsorisé


Revenir en haut Aller en bas

Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum