C_far

Créer un décompte

8 messages dans ce sujet

Créer un décompte

J'crée ce tutoriel car ça fait plusieurs fois qu'on me demande comment créer un décompte.

Je vais essayer d'être le plus compréhensif possible, donc n'hésitez pas à me faire une remarque si vous ne comprenez la formulation d'une phrase.

Il n'y a rien de bien compliqué je vous rassure.[/centre]

Niveau de difficulté : Débutant

  • Requis :
  • Une connaissance des bases du Pawn
  • De la logique
    Fonctionnement :
    Notre décompte se lancera lorsque le joueur se connectera, il devra attendre 10 secondes pour spawn.
    Durant ces 10 secondes nous lui afficherons qu'il spawnera dans X secondes, ce nombre changera toutes les secondes.
    A la fin de ce décompte, nous ferons spawn le joueur et nous lui afficherons qu'il a spawn.
    Théorie et Pratique :
    Nous allons d'abord déclarer 2 tableaux :
    Un qui stockera le nombre de secondes par joueur (imaginons qu'il y ait plusieurs joueurs qui spawnent en 10 secondes)
    Un qui stockera l'id du timer que nous utiliserons pour notre décompte
     
    new 
    	temps_Decompte[MAX_PLAYERS char], // J'ai déclaré un tableau char, car nous stockerons un entier qui ne sera pas supérieur à 10.
    	timer_Decompte[MAX_PLAYERS];


    Voilà!
    On va maitenant s'occuper du code lorsque le joueur se connecte.
    Vous pensez que je vais utiliser OnPlayerConnect, mais je vais volontairement utiliser OnPlayerRequestClass pour cacher le bouton spawn.

     
    public OnPlayerRequestClass(playerid, classid)
    {
    
    	return 1;
    }


    Voici notre callback. Pour "cacher" ce fameux bouton spawn, je vais utiliser la fonction TogglePlayerSpectating.
    En utilisant cette fonction dans notre callback, cela cachera obligatoirement ce bouton spawn hé oui (essayez!).

     
    public OnPlayerRequestClass(playerid, classid)
    {
    	TogglePlayerSpectating(playerid, true);
    
    	return 1;
    }


    Maintenant on va pouvoir s’intéresser à la partie affichage du texte et lancement du timer.
    Nous utiliserons les fonctions :
    GameTextForPlayer pour afficher le texte sur l'écran du joueur.
    SetTimerEx pour lancer un timer avec en paramètre l'id joueur.
    Je vous invite à consulter le wiki des fonctions, je ne vais pas expliquer leur fonctionnement.

     
    public OnPlayerRequestClass(playerid, classid)
    {
    	TogglePlayerSpectating(playerid, true);
    
    	GameTextForPlayer(playerid, "Vous allez spawn dans 10 secondes.", 1100, 6); // On affiche au joueur qu'il spawnera dans 10 secondes
    	timer_Decompte[playerid] = SetTimerEx("DecompteJoueur", 1000, true, "i", playerid); // On lance le timer avec une intervalle d'une 1 seconde. On précise que nous souhaitons l'id du joueur en paramètre. On récupère son id qu'on stocke dans notre variable.
    
    	return 1;
    }


    Il ne nous manque qu'une chose dans notre callback !
    Attribuer le nombre de secondes de notre décompte à notre tableau temps_Decompte

     
    public OnPlayerRequestClass(playerid, classid)
    {
    	TogglePlayerSpectating(playerid, true);
    
    	GameTextForPlayer(playerid, "Vous allez spawn dans 10 secondes.", 1100, 6);
    	timer_Decompte[playerid] = SetTimerEx("DecompteJoueur", 1000, true, "i", playerid);
    	temps_Decompte{playerid} = 10; // Nous utilisons des accolades au lieu de crochets autour de playerid, car c'est un tableau char (packed).
    
    	return 1;
    }


    La première partie est faite.
    Attaquons maintenant le code qui sera exécuté dans notre timer.

     
    forward DecompteJoueur(playerid);
    public DecompteJoueur(playerid)
    {
    
    	return 1;
    }


    Premièrement nous allons décrémenter notre variable qui stocke le nombre de secondes, car nous voulons évidemment un entier inférieur à celui d'avant dans notre décompte :')).

     
    forward DecompteJoueur(playerid);
    public DecompteJoueur(playerid)
    {
    	temps_Decompte{playerid}--;
    
    	return 1;
    }


    On va maintenant ré-utiliser notre fonction GameTextForPlayer pour afficher le texte. Mais étant donné que notre texte comportera le nombre de secondes restantes, qui seront différentes chaque seconde, on va devoir formater notre chaîne de caractères. Pour ça, on utilise format.

     
    forward DecompteJoueur(playerid);
    public DecompteJoueur(playerid)
    {
    	temps_Decompte{playerid}--;
    
    	static
    		str[34]; // J'ai indexé mon tableau à 34, car notre texte ne comporte pas plus de 33 caractères + le nullbyte
    
    	format(str, sizeof(str), "Vous allez spawn dans %d secondes.", temps_Decompte{playerid});
    	return GameTextForPlayer(playerid, str, 1100, 6);
    }


    Il va maintenant falloir arrêter notre timer, lui dire qu'à 0 seconde il s'arrête, qu'il fasse spawn notre joueur et qu'il affiche un message au joueur comme quoi celui-ci vient de spawn!
    Outch ça en fait des actions! Non j'déconne sur 4 lignes c'est fait. Nous utiliserons :
    TogglePlayerSpectating pour désactiver le mode spectateur du joueur. En le désactivant, le joueur spawnera.
    GameTextForPlayer pour afficher le texte.
    KillTimer pour arrêter notre timer.

     
    forward DecompteJoueur(playerid);
    public DecompteJoueur(playerid)
    {
    	temps_Decompte{playerid}--;
    	if(temps_Decompte{playerid} == 0) // Si le nombre de secondes est à 0
    	{
    		TogglePlayerSpectating(playerid, false); // On désactive le mode spectateur du joueur et le fera spawn
    
    		GameTextForPlayer(playerid, "Vous venez de spawn.", 2000, 6); // On affiche au joueur qu'il a spawn
    		KillTimer(timer_Decompte[playerid]); // On arrête le timer.
    
    		return 1; // On retourne pour sortir de notre timer
    	}
    
    	static
    		str[34];
    
    	format(str, sizeof(str), "Vous allez spawn dans %d secondes.", temps_Decompte{playerid});
    	return GameTextForPlayer(playerid, str, 1100, 6);
    }

     

Modifié par C_far

Partager ce message


Lien à poster
Partager sur d’autres sites

En reprenant ton exemple, on peut tout regrouper en une seule fonction et ainsi s'épargner la lourdeur de voir trainer des variables globales ici ou ailleurs.

 

Exemple :

#include <a_samp>

// pas de new globaux.

public OnPlayerRequestClass(playerid, classid)
{
	TogglePlayerSpectating(playerid, true);
	GameTextForPlayer(playerid, "Vous allez spawn dans 10 secondes.", 1100, 6);
	SetPlayerSpawnDelay(playerid, 10);
	return 1;
}

forward SetPlayerSpawnDelay(playerid, seconds);
public SetPlayerSpawnDelay(playerid, seconds)
{
	static timer[MAX_PLAYERS char];
	static timer_tag;
	if(seconds != -1) // pour initialiser le timer.
	{
		timer{playerid} = seconds;
		return timer_tag = SetTimerEx("SetPlayerSpawnDelay", 1000, true, "ii", playerid, -1);
	}
	if(timer{playerid} == 0) // le reste comme dans ton exemple.
	{
		KillTimer(timer_tag);
		return TogglePlayerSpectating(playerid, false);
	}
	new string[34];
	format(string,sizeof(string),"Vous allez spawn dans %d", timer{playerid});
	timer{playerid}--;
	GameTextForPlayer(playerid, string, 1100, 6);
	return 1;
}

Ca rajoute un if et un parametre dans la fonction, du coup un calcul supplémentaire à chaque appel. Est ce que ca en vaut la peine ? Dans la mesure ou on a pas à éparpiller son code, définir des variables globales, les initialiser dans une autre callback etc, c'est tout benef, surtout avec ce langage ou t'es assez limité. Et pour une fois on peut se vanter d'avoir utilisé toutes les propriétés utiles des static.

 

 

Modifié par Hodor
toggleplayerspec dans la fonction pas controlable. rectifié

Partager ce message


Lien à poster
Partager sur d’autres sites

@Hodor ce tutoriel est dédié aux débutants et était le premier que j'ai rédigé sur le forum officiel, il est évident que je n'allais pas mentionner de parties compliquées.

Maintenant dans ton exemple, la variable statique peut être un bon moyen d'éviter la variable globale, mais y voyons une optimisation ? Au contraire, tout ce qui change c'est la portée des variables, car une variable statique ou globale sera située obligatoirement dans le DATA, donc sera initialisé dans l'amx et la valeur sera stockée tout le temps du programme. Tout ce qu'il y a de bon dans ton cas, c'est la portée.

 

Maintenant, l'intérêt d'avoir ce genre de variables en global ? Pouvoir y accéder depuis d'autres fonctions, car là c'est un exemple bidon que j'ai posté, mais l'utilité d'un décompte peut être poussé à bien plus intéressant sur de bons systèmes.

 

Il y a 10 heures, Hodor a dit :

Ca rajoute un if et un parametre dans la fonction, du coup un calcul supplémentaire à chaque appel.

Une comparaison supplémentaire à chaque appel, c'est déjà beaucoup trop. Sans parler d'un paramètre supplémentaire, qui surplombe donc le FRAME inutilement.

 

Conclusion :

Ce que tu as fait est bien, mais n'est pas optimisé.

 

Edit : D'ailleurs ton code ne permettra de ne faire tourner qu'un décompte à la fois, sans quoi si un deuxième est lancé pendant le premier, à la fin de celui-ci le second décompte s'arrêtera, mais le premier continuera de tourner.

Modifié par C_far

Partager ce message


Lien à poster
Partager sur d’autres sites

Ah oui j'ai mal copié ton exemple j'ai pas mis timer_tag[max_players], erreur d'inattention, c'est vrai que c'est con sinon. Après, je n'ai pas dit que c'était le code le plus rapide évidemment ca ne l'est pas du coup, et si tu comptes réutiliser ta variable globale timer_decompte etc, ca va pas le faire c'est sur, mais sinon ca reste valable dans le sens ou on ne cherche pas toujours à avoir un code le plus rapide possible au détriment d'autres facteurs. Merci pour ta rep C_ !

Partager ce message


Lien à poster
Partager sur d’autres sites

Salut j'ai un soucis, lors de la fin de ton décompte, il spawn, mais je suis kick du serveur. Ma connexion a été fermé... Une réponse ? Un conseil ?

Partager ce message


Lien à poster
Partager sur d’autres sites

Pfiou j'en avais oublié ce tutoriel horrible

 

13 minutes ago, Audrey said:

Salut j'ai un soucis, lors de la fin de ton décompte, il spawn, mais je suis kick du serveur. Ma connexion a été fermé... Une réponse ? Un conseil ?

 

Le code ? 

Modifié par C_far

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai pris exactement le tient, mais voici:

 

public DecompteJoueur(playerid)
{
	temps_Decompte{playerid}--;
	if(temps_Decompte{playerid} == 0) // Si le nombre de secondes est à 0
	{
		TogglePlayerSpectating(playerid, false); // On désactive le mode spectateur du joueur et le fera spawn

		GameTextForPlayer(playerid, "Vous venez de spawn.", 2000, 6); // On affiche au joueur qu'il a spawn
		KillTimer(timer_Decompte[playerid]); // On arrête le timer.

		return 1; // On retourne pour sortir de notre timer
	}

	static
		str[148];

	format(str, sizeof(str), "Chargement du jeu en cours... Attendez %d secondes.", temps_Decompte{playerid});
	return GameTextForPlayer(playerid, str, 1100, 6);
}

 

public OnPlayerRequestClass(playerid, classid) //  -1442.4795,2593.5142,55.8359
{
    TogglePlayerSpectating(playerid, true);
    
    GameTextForPlayer(playerid, "Vous allez spawn dans 10 secondes.", 1100, 6); // On affiche au joueur qu'il spawnera dans 10 secondes
	timer_Decompte[playerid] = SetTimerEx("DecompteJoueur", 1000, true, "i", playerid); // On lance le timer avec une intervalle d'une 1 seconde. On précise que nous souhaitons l'id du joueur en paramètre. On récupère son id qu'on stocke dans notre variable.
    temps_Decompte{playerid} = 10; // Nous utilisons des accolades au lieu de crochets autour de playerid, car c'est un tableau char (packed).

    

	/*SetPlayerPos(playerid,199.0846,-150.0331,1.5781);
	SetPlayerCameraPos(playerid, 199.2307,-143.8328,1.5781);
	SetPlayerCameraLookAt(playerid, 199.0846,-150.0331,1.5781);
	SetPlayerFacingAngle(playerid,359.1443);*/
	return 1;
}

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !


Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.


Connectez-vous maintenant