[DB - AL] Système de missions

Pour les scripts écrits en C#
Règles du forum
Merci de respecter la NOMENCLATURE suivante pour vos TITRES de messages :

Commencez par le niveau de vos scripts
DB = Débutant
MY = Moyen
CF = Confirmé

Puis le domaine d'application
-RS = Réseau
-AL = Algorithmie

Exemple :

[DB-RS] Mouvement perso multijoueur
jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 24 Mars 2020 14:23

Bonjour,
pour le niveau de difficulté, tu veux 70% de facile, 20% de moyen et 10% de difficile si j'ai bien compris. Je ne vois pas l'intérêt de d'actualiser 3 booléens pour cela. Un int ou un enum serait probablement plus adapté :

Code : Tout sélectionner

using UnityEngine;

public class TestRandom : MonoBehaviour {

    public enum DifficultyLevel { Facile, Moyen, Difficile };

    [SerializeField] DifficultyLevel m_DifficultyLevel;

    private void Update () {
    	// Juste pour tester notre petite fonction. 100 utilisations m'ont donné 69 / 21 /10.
        if (Input.GetKeyDown (KeyCode.Space)) {
            m_DifficultyLevel = RandomDifficultyLevel ();
            Debug.Log (m_DifficultyLevel.ToString ());
        }
    }

    private DifficultyLevel RandomDifficultyLevel () {
        float randomNumber = Random.value;
        // On teste la probabilité cumulée.
        if (randomNumber < 0.7f) {
            return DifficultyLevel.Facile;
        } else if (randomNumber < 0.9f) {
            return DifficultyLevel.Moyen;
        } else {
            return DifficultyLevel.Difficile;
        }
    }
}
Même principe pour les missions.

jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 24 Mars 2020 15:00

Pour les monstres, j'ai compris ton besoin comme cela : si la mission est de type "Monstre", tu veux tirer au sort un monstre adapté au niveau de difficulté.
Je te propose de créer un tableau par niveau (on peut aussi faire un tableau de tableaux, mais si tu n'es pas à l'aise avec les tableaux multi dimensions, plusieurs tableaux conviendront très bien. J'ai supposé que tes monstres sont de type GameObject, et que tu remplis tes tableaux via l'inspecteur :

Code : Tout sélectionner

    // Déclaration des tableaux de monstres.
    [SerializeField] GameObject [] m_EasyMonsters;
    [SerializeField] GameObject [] m_MediumMonsters;
    [SerializeField] GameObject [] m_DifficultMonsters;
Une fois le type de mission déterminé, et la difficulté tirée au sort, on peut appeler cette fonction :

Code : Tout sélectionner

    // Renvoie un monstre en fonction du niveau de difficulté.
    private GameObject MonsterMission (DifficultyLevel _difficultyLevel) {
        // Choisit le tableau de monstre du niveau de difficulté requis.
        GameObject [] monstersArray = m_EasyMonsters; // Pour éviter l'erreur de compilation sur variable non initialisée.

        switch (_difficultyLevel) {
            case DifficultyLevel.Facile:
                monstersArray = m_EasyMonsters;
                break;
            case DifficultyLevel.Moyen:
                monstersArray = m_MediumMonsters;
                break;
            case DifficultyLevel.Difficile:
                monstersArray = m_DifficultMonsters;
                break;
            default:
                Debug.LogError ("Problème dans la fonction MonsterMission du script TestRandom.");
                break;
        }

        // Détermine un monstre au hasard dans le tableau.
        return monstersArray [Random.Range (0, monstersArray.Length)];
    }

jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 24 Mars 2020 15:12

J'ai fait un petit test en remplaçant les GameObject par des string :
Image
Cela fonctionne plutôt bien :
Image
Je ne suis pas sur des performances, mais je pense que la durée de la mission est très supérieure au tirage au sort :)

jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 24 Mars 2020 15:50

Pour le choix entre tableau et liste, je peux (depuis peu^^) me prononcer. En fait, les listes ne sont que des tableaux déguisés...
Quand on crée une liste, le C# crée un tableau en sous-main. Chaque fois qu'on ajoute un élément qui le fait arriver à la fin de son tableau camouflé, C# crée un tableau 2 fois plus grand, et recopie le premier dans le second (voir à ce sujet la doc Microsoft sur la propriété Capacity d'une liste). Donc point de vue performance et en général, il vaut mieux utiliser un tableau. Il y a des cas où les listes sont toutefois nécessaires :
* on ne sait pas quelle taille aura le tableau ;
* on a besoin d'une fonction que list implémente, mais pas array.
Vis à vis du forum, vaux mieux que je repost un autre topic ou je pourrais le balancer ici si quelqu'un est prêt à se pencher dessus ?
A mon avis, si c'est une autre question, il vaut mieux un autre sujet. Plus facile pour répondre, pour lire les réponses, pour clore les sujets et pour retrouver une information du type jailucamaiscestou.
Dernière édition par jmhoubre le 24 Mars 2020 16:31, édité 1 fois.

jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 24 Mars 2020 16:29

Ah et petite question général aussi, il vaux mieux avoir genre 10 tableaux/listes avec 3/4 truc dans chaque ou un seul avec tout regroupé dedans? Ca change quelque chose niveau optim ?
J'ai un peu cherché sur le net francophone, je n'ai pas trouvé de réponse. Sur le net anglophone, beaucoup de sujets sur la même question à propose des tables SQL et autres bases de données. Il y a aussi une réponse sur l'utilisation du fertilisant dans l'agriculture du Paraguay qui m'a laissé un peu sur ma faim...

J'ai donc remplacé table (traduction de Google) par array... :mrgreen: ce qui m'a amené à cet article : Better to store in one big array or several smaller ones?, dans lequel quelques points sont intéressants :
* quelqu'un a fait un test et a trouvé que le gros tableau était plus rapide que plusieurs petits (mais c'est en PHP) ;
* quelqu'un a écrit que l'accès à un élément du tableau par son index est constant quelque soit le nombre d'éléments du tableau ;
* enfin on se pose la question de ce que fait le langage "in the hood" (~derrière la scène), voir plus haut ce que fait le C# quand il manipule une liste.

Je pense que la question se pose en termes différents. Voici quelques réflexions de mon cru, que je soumets à votre sagacité :
* je ne vois pas l'intérêt de diviser un gros tableau en plusieurs petits : si les données représentent la même chose, elles devraient être groupées.
* plusieurs tableaux, cela pose plusieurs soucis à résoudre (je ne dis pas que c'est dur, je dis juste qu'il faut y penser) :
** comment fait-on pour décider si telle donnée va dans le tableau A ou le B ?
** parcourir le tableau : il faut parcourir plusieurs tableaux.

Je pense que le cas par cas est à considérer. Pour ce qui concerne tes monstres, un seul tableau est tout à fait possible, si l'élément permet de stocker la difficulté du monstre. Il faudra simplement adapter le code :
* mauvaise méthode : on tire un monstre au hasard, et on retire tant qu'il n'est pas de la bonne difficulté (si on a pas de chance, et qu'en plus il y a 998 monstres faciles et 2 difficiles, on risque d'être un peu long sur le tirage d'un difficile).
* autre méthode : on extrait un tableau temporaire avec les monstres de la bonne difficulté (ben on revient à plusieurs tableaux non ?)
* bonne méthode : je ne vois pas.

La question mérite d'être résolue, car elle se pose dans de multiples cas : le monstre peut avoir un ou plusieurs types de zones de vie, et en fonction de l'endroit où va se passer la mission, la probabilité d'en trouver sera nulle ou très importantes. C'est ce que fait Minecraft, je vais essayer de voir comment se passent les tirages aléatoires dans ce jeu.
C'est aussi le cas pour le tirage aléatoire de loot : je suis toujours surpris, dans Fallout 4, de trouver des munitions dans une école (quoique, c'est un jeu US non ?), ou des armes de 120 cm de long dans une caisse à outils individuelle...

djulio74
Messages : 536
Inscription : 19 Déc 2009 22:55
Contact :

Re: [DB - AL] Système de missions

Message par djulio74 » 24 Mars 2020 19:13

jmhoubre a écrit :
24 Mars 2020 15:50
Pour le choix entre tableau et liste, je peux (depuis peu^^) me prononcer. En fait, les listes ne sont que des tableaux déguisés...
Quand on crée une liste, le C# crée un tableau en sous-main. Chaque fois qu'on ajoute un élément qui le fait arriver à la fin de son tableau camouflé, C# crée un tableau 2 fois plus grand, et recopie le premier dans le second (voir à ce sujet la doc Microsoft sur la propriété Capacity d'une liste). Donc point de vue performance et en général, il vaut mieux utiliser un tableau. Il y a des cas où les listes sont toutefois nécessaires :
* on ne sait pas quelle taille aura le tableau ;
* on a besoin d'une fonction que list implémente, mais pas array.
Vis à vis du forum, vaux mieux que je repost un autre topic ou je pourrais le balancer ici si quelqu'un est prêt à se pencher dessus ?
A mon avis, si c'est une autre question, il vaut mieux un autre sujet. Plus facile pour répondre, pour lire les réponses, pour clore les sujets et pour retrouver une information du type jailucamaiscestou.
Alors oui et non. L'unique avantage de la liste est de pouvoir ajouter ou supprimer des éléments au milieu de la liste, qui dans l'absolu ne sert pas souvent.
Si tu ne sait pas quelle taille aura ton array (et oui un array n'est pas un tableau, qui lui a 2 dimensions):
- créer un array plus grand
- avoir un compteur pour le nombre d'éléments dont tu as besoin
- si ton array deviens trop petit pour le nombre d'éléments, il y a la méthode Array.Resize.
- si le maniement de tes listes est ponctuelle, pas besoin de changer pour des array
- si tu modifié/accède très souvent ta liste de grande taille ( nombreuse fois par frame, chaque frame) alors oui passe par des array.

petit exemple vite fait :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class ExempleArray : MonoBehaviour
{
    private int[] tableau;
    // count représente la taille de l'array "tableau"
    private int count = 0;

    void Start()
    {
        // créé un array de int "vide"
        tableau = new int[10];
    }


    void Update()
    {
        // si appuy sur "a"
        if (Input.GetKeyDown("a"))
        {
            // pour 20 itérations
            for (int i = 0; i < 20; i++)
            {
                // assigne la "derniere" valeur de l'array, celle à l'index "count"
                tableau[count] = i;
                // on incrémente "count" pour indiqué qu'on a "ajouté" une valeur
                count += 1;

                // si on a assigné la derniere valeur dispo dans l'array
                if( count == tableau.Length)
                {
                    // on augmente la taille de l'array
                    Array.Resize(ref tableau, tableau.Length + 10);
                }
            }
            // petit debug pour savoir la taille de l'array
            print(tableau.Length.ToString());
        }        
    }
}
Donc,
- on se sait pas quelle sera la taille nécessaire pour l'array "tableau" mais utilise un array quand même
- en appuyant sur "a" on assigne un certain nombre de valeur dans l'array "tableau" comme en ajoutant des éléments a une liste.
- si l'array devient trop petit, on lui assigne une nouvelle dimension
- utilisation du Resize avec parcimonie, reste gourmand en ressource. on augmente pas la taille un par un mais par pallier, taille initial et palier d'augmentation à évaluer en faisant des test (fréquence de changement, taille de l'array, au cas par cas)

Perso je me suis énormément servi des array en remplacement des liste, sur des donné de plusieurs millions d'éléments, et les temps de traitement sont assez rapide.

Pour ta question de un grand array ou plusieurs petits, je dis un seul grand

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

Sicryn
Messages : 11
Inscription : 23 Mai 2018 17:40

Re: [DB - AL] Système de missions

Message par Sicryn » 25 Mars 2020 13:54

Bonjour à vous !

Tout d'abord merci beaucoup pour le temps passé à rédiger vos réponses ! C'est vraiment cool !

Vous avez répondu à pas mal de chose et fait comprendre pas mal d'autres, mais je me suis rendu compte que je n'avais peut-être pas été assez clair et précis sur ce à quoi ressemble mon jeu, ce qui fait que la partie que jmhoubre m'a écrit à propos des monstres est très intéressante mais je pense malheureusement assez inadapté pour mon utilisation dans ce projet.

Je vais donc essayer d'être beaucoup plus précis sur mon jeu:

Mon jeu est un jeu de gestion/narration entièrement en 2D dans lequel on peut faire prospérer une guilde de mercenaire dans un univers urban fantasy. Tout le gameplay tourne sur le fait que l'on reçoit des missions aléatoire, tournées comme si l'on avait prit la déposition de quelqu'un qui était venu nous voir pour régler un problème.
Les missions en elles-mêmes sont donc importantes dans leur narration, et sont donc de petits pavés rédigés sans d'aléatoire ou de concaténation dans l'écriture. Elles seront donc toutes déjà écrites par rapport à si elles sont destinées à être une enquête magique de difficulté medium ou un monstre à abattre de faible difficulté par exemple, donnant des indices sur des éléments de gameplay dans leur écriture.
Une fois la mission reçu, le gameplay reste très simple, on ne va pas partir vraiment en mission mais en gros mettre des points plus ou moins dans tels compétences par rapport au style de mission (visualisez un peu un gameplay à la Game dev tycoon si vous voulez). Donc il n'y à pas de GameObject, de vrai mobs à tuer ou autre, seulement du texte. Et c'est en ça que réside mon problème. J'écris au pif un exemple de mission :

" Un nouveau client est venu aujourd'hui. Il avait l'air d'être très pressé et m'a débité une histoire sans queue ni tête. Un loup-garou doué d'une intelligence perturbante aurait prit possession de tout un domaine et utiliserait les anciens habitants et domestiques comme garde-manger. Si ce qu'il dit est vrai, cela va poser un problème. Je ferais mieux de bien me préparer pour le combat."

Le joueur va recevoir ça, il va avoir l'information que la difficulté est de niveau medium, et les indices du texte le guidera sur le fait qu'il vaux mieux mettre des points dans la préparation et dans le combat plutôt que dans la précipitation et le calme.
Sauf que moi, dans mon code, il y aura donc eu le tirage au sort du style de mission, qui sera tomber sur monstre, parmi d'autres style de mission possible, et sur difficulté medium. Avec ces deux tirage, cela ira tirer la mission, de type string, dans mon tableau rempli de jolies missions. Mais moi derrière, dans cet exemple, comment je fais pour savoir que le loup-garou a été mentionné quelque part dans mon string tiré au hasard et qu'il faut donc le débloquer dans le bestiaire ?

Et est-ce que je peux éviter une répétition avec un tableau ? Parce que j'utilisais une liste pour pouvoir ensuite supprimer la mission qui a déjà été tirée.

J'espère que mon pavé et mon exemple sont clair et vous aiderons à mieux cerner le petit problème.
En tout cas, encore merci mille fois pour le temps accordé, et merci pour l'exemple d'optimisation de mes tirages au sort pour la difficulté et le style de mission jmhoubre, ça va bien me servir !
Et les infos et points que vous avez donnés sur les différences entre tableau, liste, et leur taille etc est très intéressant !

jmhoubre
Messages : 112
Inscription : 05 Oct 2019 22:05

Re: [DB - AL] Système de missions

Message par jmhoubre » 26 Mars 2020 11:32

Merci. Je ne comprends pas vraiment en quoi ton problème est insoluble. Je vais tenter un deuxième essai. :naah:
Sauf que moi, dans mon code, il y aura donc eu le tirage au sort du style de mission, qui sera tomber sur monstre, parmi d'autres style de mission possible, et sur difficulté medium. Avec ces deux tirage, cela ira tirer la mission, de type string, dans mon tableau rempli de jolies missions. Mais moi derrière, dans cet exemple, comment je fais pour savoir que le loup-garou a été mentionné quelque part dans mon string tiré au hasard et qu'il faut donc le débloquer dans le bestiaire ?
Je comprends que tu as le texte de la mission (text_misssion) tout prêt dans un tableau, et que ton souci est de savoir quel monstre est concerné (nom_monstre).
Je te propose plusieurs pistes :

1) une recherche de tous les nom_monstre possibles dans text_mission. Un peu bourrin, mais cela ne change pas trop ce que tu as déjà fait.

2) une petite modification du texte de tes missions : tu coupes text_mission en 2 morceaux, avant le nom du monstre et après le nom du monstre. Par exemple :
text_mission_debut = "Un nouveau client est venu aujourd'hui. Il avait l'air d'être très pressé et m'a débité une histoire sans queue ni tête. "
text_mission_fin = " doué d'une intelligence perturbante aurait prit possession de tout un domaine et utiliserait les anciens habitants et domestiques comme garde-manger. Si ce qu'il dit est vrai, cela va poser un problème. Je ferais mieux de bien me préparer pour le combat."
Tu peux utiliser ma génération de monstre pour obtenir le monstre, et tu concatènes ces 3 morceaux.

3) Ajouter dans ton tableau de missions une colonne pour le monstre concerné. En écrivant ceci, je me demande si j'ai bien compris ton souci... :aille:

Désolé si je suis encore tombé à côté.

djulio74
Messages : 536
Inscription : 19 Déc 2009 22:55
Contact :

Re: [DB - AL] Système de missions

Message par djulio74 » 26 Mars 2020 20:29

De mon coté, j'utiliserai des struct.
une struct si jamais est un ensemble de données defini dans une seul variable.
Par exemple une Vector3 est un struct incorporé à unity, qui contien 3 variabkle noté x, y et z, auxquelles tu accede via Vector3.x par exemple.

Pour ton cas, tu aaurai besoin d'une liste de mission (sous forme d'array ou simple liste) sous forme de struct comprenant les infos comme la difficulté, le type, le texte, une info si déjà réalisée.. etc.

un exemple vite fait :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class ExempleStruct : MonoBehaviour
{

	private struct MISSION
	{
		public int Type;
		public int difficulte;
		public int Monstre;
		public int TexteMission;
		public bool realisee;
		

		public MISSION(int Type, int difficulte, int Monstre, int TexteMission, bool realisee)
		{
			this.Type = Type;
			this.difficulte = difficulte;
			this.Monstre = Monstre;
			this.TexteMission = TexteMission;
			this.realisee = realisee;
		}
	}

	private int NombreMissions = 10;
	private int Missionsrealisees = 0;

	private string[] TypeText;
	private string[] difficulteText;
	private string[] MonstreText;
	private string[] MissionTexte;
	private MISSION[] Mission;




	// Start is called before the first frame update
	void Start()
	{
		// trois type de missions et leurs noms
		TypeText = new string[3]{
		"monstre",
		"enigme",
		"recherche" };

		// 4 niveau de difficulté et leurs noms
		difficulteText = new string[4]{
		"facile",
		"moyen",
		"difficile",
		"extreme"       };

		// defini 8 monstres et leurs noms
		MonstreText = new string[8]{
		"grenouille",
		"chat",
		"griphon",
		"tortue",
		"hydre",
		"serpent",
		"lion",
		"loup-garou"
		};

		// créer un nombre defini de mission, avec "NombreMissions"
		// défini un petit texte pour chacune des missions
		MissionTexte = new string[NombreMissions];
		for (int i = 0; i < NombreMissions; i++)
		{
			MissionTexte[i] = "Voici votre mission si vous l'accepté : texte de la mission n°" + i.ToString();
		}

		// initialisation des missions, aléatoirement
		Mission = new MISSION[NombreMissions];
		for ( int i = 0; i < NombreMissions; i++)
		{
			Mission[i].Type = UnityEngine.Random.Range(0, TypeText.Length);
			Mission[i].difficulte = UnityEngine.Random.Range(0, difficulteText.Length);
			Mission[i].Monstre = UnityEngine.Random.Range(0, MonstreText.Length);
			Mission[i].TexteMission = i;
			Mission[i].realisee = false;
		}
	}


	void Update()
	{
		// tirage au sort d'une mission aléatoire parmis les non réalisées
		if (Input.GetKeyDown("a"))
		{
			RechercheMission();
		}
	}

	void RechercheMission()
	{
		// on verifié que le nombre de mission réalisés n'est pas égale au nombre de missions total
		if (Missionsrealisees == NombreMissions)
		{
			Debug.Log(" toutes les missions ont été réalisées");
			return;
		}

		// on choisis aléatoirement une mission
		int missionaleatoire = UnityEngine.Random.Range(0, NombreMissions);

		// si la mission choisie est déja marquée "réalisée", 
		if ( Mission[missionaleatoire].realisee == true)
		{
			// on arrete là et on en cherche une autre
			RechercheMission();
			return;
		}

		// on récupère les info de la mission en cours
		int Ty = Mission[missionaleatoire].Type;
		int Di = Mission[missionaleatoire].difficulte;
		int Mo = Mission[missionaleatoire].Monstre;
		int Tm = Mission[missionaleatoire].TexteMission;
		bool Re = Mission[missionaleatoire].realisee;

		// on debug les infos de la mission
		Debug.Log(" type : " + TypeText[Ty] + " ; difficulté : " + difficulteText[Di] + " ; Monstre : " + MonstreText[Mo] + " ; Texte de la mission : " + MissionTexte[Tm]);

		// on marque la mission en cours comme "réalisée"
		Mission[missionaleatoire].realisee = true;
		// on incrémente le nombre de missions réalisées
		Missionsrealisees += 1;
	}
}
voilà j'ai tenté de l'annoter au mieux, si tu as des questions n'hesite pas.
dans ma void Start, j'ai fait de façon aléatoire pour générer les missions. a toi de définir chaque missions suivant tes besoins.

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

Sicryn
Messages : 11
Inscription : 23 Mai 2018 17:40

Re: [DB - AL] Système de missions

Message par Sicryn » 26 Mars 2020 22:41

Bonsoir à vous deux !

Alors tout d'abord encore merci, vous êtes génial de prendre du temps pour faire des réponses aussi développées et intéressantes ! C'est vraiment top.

Ensuite,
jmhoubre a écrit :
26 Mars 2020 11:32
3) Ajouter dans ton tableau de missions une colonne pour le monstre concerné. En écrivant ceci, je me demande si j'ai bien compris ton souci... :aille:
Eh ben le pire c'est que je crois que c'est cette phrase qui m'a fait un déclic. Malheureusement parfois je n'arrive tout simplement pas à visualiser une solution à mon problème parce que j'ai encore de grosse lacune en C#, et ta phrase m'a fait m'intéressé aux tableaux multidimensionnels, que je ne connaissais même pas, et j'ai donc fais des tests et je pense que je touche quelque chose de pas mal.
Quelque chose de pas mal qui se rapproche de ce que tu me propose djulio74 d'ailleurs, mais avec un tableau multidimensionnel à la place d'une struct ( que je n'avais jamais utilisé non plus d'ailleurs ).

J'ai rajouté dans mon tableau multidimensionnel une colonne qui comporte un nom de montre si besoin, et une autre colonne qui renseigne si la mission à déjà été utilisée ou non et relance la pioche au besoin.

Je pense m'en tirer avec ma nouvelle compréhension de mes tableaux, mais vous pensez qu'il y a une différence notable entre le tableau et le struct qui pousserait à plutôt utiliser le struct ? Que je sache si ça vaux le coût que je me penche vraiment sur le sujet ahah !

Encore merci pour vos réponses ! Avec les différents éléments qu'on m'a dit, j'ai déjà amélioré pas mal de petites choses dans mes codes ! ;)

Bonne soirée

Répondre

Revenir vers « (C#) CSharp »