Problème d'actualisation ScrollView

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Problème d'actualisation ScrollView

Message par SoPic » 01 Mars 2022 12:01

Bonjour à tous !

J'ai un souci récurrent avec les ScrollView. Dès que je souhaite réactualiser la liste j'ai toujours quelques objects qui ne s'actualisent pas. Exemple :
Première utilisation, tout se passe bien :
Image

Deuxième utilisation, je change de perso, ça se gâte... (il n'y en a que 2, ça c'est normal)
Image

Je reviens sur le premier...
Image

Pour actualiser je fais un bête Destroy avec une boucle, et je recommence. J'ai fait du Debug.Log a chaque étape et tout semble se faire dans le bon ordre et sans encombre. Je ne comprend pas :0

Mon code :

Code : Tout sélectionner

public void AfficherJoueurs()
    {
        foreach (Transform child in ContentChoixPlayer.transform) { GameObject.Destroy(child.gameObject); }

        Players[] Player = JsonHelperPlayers.FromJson<Players>(calculStats.ListePlayers);

        List<string> classementMini = new List<string>();
        List<string> classementID = new List<string>();

        int taillePlayerTab = Player.Length;
        for (int i = 0; i < taillePlayerTab; i++)
        {
            if (nomJoueur.text == Player[i].Nom)
            {
                Instantiate(BouttonChoixPlayer, ContentChoixPlayer.transform);
                classementMini.Add(calculStats.ListeMiniatures[i]);
                classementID.Add(Player[i].ID);
            }
        }
        
        for (int a = 0; a < classementMini.Count; a++)
        {
            ContentChoixPlayer.transform.GetChild(a).transform.GetChild(0).GetComponent<Text>().text = classementID[a];

            byte[] Bytes = Convert.FromBase64String(classementMini[a]);
            Texture2D newSprite = new Texture2D(1, 1);
            newSprite.LoadImage(Bytes);
            Image Img = ContentChoixPlayer.transform.GetChild(a).GetComponent<Image>();
            Sprite mySprite = Sprite.Create(newSprite, new Rect(0.0f, 0.0f, newSprite.width, newSprite.height), new Vector2(0.5f, 0.5f), 100.0f);
            Img.sprite = mySprite;
        }
    }
Si quelqu'un a une piste, je suis preneur. Pour l'instant la seule solution que j'ai c'est de créer suffisamment de GameObjects en attente et donc d'en garder des vides à l'écran. C'est pas classe :-/
Merci !

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: Problème d'actualisation ScrollView

Message par jmhoubre » 01 Mars 2022 15:25

Bonjour,

en l'absence d'informations plus complètes (comme par exemple des détails sur le prefab BouttonChoixPlayer, la structure de Players, ou à quoi correspond la variable nomJoueur, son initialisation ...), voici quelques questions (pas forcément des pistes) :
  1. Akai et Calluslas sont des persos, quel est le lien entre un perso et un joueur ? Un joueur peut avoir plusieurs persos ? Dans le code, quel est la (ou les) variable identifiant les persos ? Et les joueurs ?
  2. Akai affiche 9 sprites : à quoi correspondent ces sprites ?
  3. Comment est appelée la fonction AfficherJoueurs ? (je pense le savoir, mais je préfère une certitude que de chercher à partir d'une hypothèse).
  4. AfficherJoueurs est au pluriel, quels joueurs affiche-t-elle ?
  5. A la fin de la boucle "for (int i = 0; i < taillePlayerTab; i++)", classementMini et classementID devraient contenir le même nombre d'éléments que le nombre de Player dont le nom vaut nomJoueur. Est-ce le cas ?
  6. Cette boucle compte les objets à afficher, les instancie et et renseigne les 2 listes. La boucle suivante met à jour le sprite. Visiblement, c'est cette boucle qui semble poser un problème.
  7. classementMini.Count vaut-il bien la valeur attendue ?
  8. Pourquoi ne pas déplacer le code de la seconde boucle dans une fonction, qui serait appelée dans la 1ère boucle ?

    Code : Tout sélectionner

    public void AfficherJoueurs()
    {
    	// ...
    	
    	for (int i = 0; i < taillePlayerTab; i++)
    	{
    		if (nomJoueur.text == Player[i].Nom)
    		{
    			Instantiate(BouttonChoixPlayer, ContentChoixPlayer.transform);
    			classementMini.Add(calculStats.ListeMiniatures[i]);
    			classementID.Add(Player[i].ID);
    			ButtonUpdate ();
    		}
    	}
    }
    
    private void ButtonUpdate ()
    {
    	// Probablement à adapter.
    	ContentChoixPlayer.transform.GetChild(a).transform.GetChild(0).GetComponent<Text>().text = classementID[a];
    	byte[] Bytes = Convert.FromBase64String(classementMini[a]);
    	Texture2D newSprite = new Texture2D(1, 1);
    	newSprite.LoadImage(Bytes);
    	Image Img = ContentChoixPlayer.transform.GetChild(a).GetComponent<Image>();
    	Sprite mySprite = Sprite.Create(newSprite, new Rect(0.0f, 0.0f, newSprite.width, newSprite.height), new Vector2(0.5f, 0.5f), 100.0f);
    	Img.sprite = mySprite;
    }
    
  9. Rien à voir avec ton souci, mais détruire et instancier les slots d'affichage me parait peu efficace. Je placerai tous les slots dans l'inspecteur, en mode désactivé.
    La boucle foreach serait remplacée par :

    Code : Tout sélectionner

    foreach (Transform child in ContentChoixPlayer.transform)
    {
    	child.gameObject.SetActive (false);
    }
    et l'instantiation par :

    Code : Tout sélectionner

    	ContentChoixPlayer.transform.GetChild(i).SetActive (true);
    

Avatar de l’utilisateur
boubouk50
ModoGenereux
ModoGenereux
Messages : 6225
Inscription : 28 Avr 2014 11:57
Localisation : Saint-Didier-en-Bresse (71)

Re: Problème d'actualisation ScrollView

Message par boubouk50 » 01 Mars 2022 15:29

Salut,

Ça pourrait venir des délais de validation des effets. Par exemple, le Destroy n'est pas immédiat, du coup, ça pourrait entraîner un mauvais compte. Il est effectué entre l'Update () et le LateUpdate ().
Doc: Actual object destruction is always delayed until after the current Update loop, but will always be done before rendering.

Un système de pooling des vignettes (je vais nommer comme ça le prefab instancié) pourrait aussi être judicieux. Au lieu de supprimer/recréer, tu peux simplement masquer/afficher. Le scrollRect permet que les éléments désactivés ne soient pas pris en compte.
"Ce n'est pas en améliorant la bougie, que l'on a inventé l'ampoule, c'est en marchant longtemps."
Nétiquette du forum
Savoir faire une recherche
Apprendre la programmation

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Problème d'actualisation ScrollView

Message par SoPic » 01 Mars 2022 15:57

Merci pour votre piste. Effectivement jouer avec l'activation me semble envisageable. Par contre il faut que je fasse attention à avoir une réserve suffisante. :gene:

Je vais tenter de répondre à jmhoubre sur ce que je peux.

Un joueur est défini par son nom (ex Akai) et un même joueur peut avoir plusieurs versions que l'on va appeler perso, d'où les 9 vignettes
Tous ces persos (679) sont dans une base de données que je récup en json au lancement de l'appli et que je stock dans une string (ListePlayer) dans un autre script.
json que je transforme en Objet Players. Je traite simplement à part les mini qui sont les miniatures des joueurs car je dois faire une manipulation sur les string pour que le Convert fonctionne correctement.

Cette fonction est affichée quand je clique sur une grosse vignette vide pour choisir un perso
Image
Ca ouvre une pop-up avec les screens précédents.

classementID et classementMini ont bien la même longueur qu'il y a de Akai dans la base de données

La 2ème boucle semble effectivement ne pas s'activer entièrement si il y a eut le destroy avant. Ca c'est mon mystère. Je préfèrerais qu'elle ne s'active pas du tout...

Pour ce qui est d'appeler dans la première boucle il me semble que je n'avais pas réussi la 1ère fois que j'ai rencontré ce problème. Ca doit faire 3 ans... vu mon expérience ça équivaut à une éternité, faudrait peut-être que je re essaye.

Je tente les 2 solutions et je reviens. Merci ::d

SoPic
Messages : 32
Inscription : 02 Oct 2019 18:12

Re: Problème d'actualisation ScrollView

Message par SoPic » 01 Mars 2022 16:14

Bon j'ai tenté, non pas d'appeler la deuxième boucle dans une autre fonction, mais de tout mettre dans la première boucle. Sans succès. Mais vu que ça ne marchait pas moins bien, j'ai gardé car ça simplifie le code :cote:

Par contre le pansement avec le SetActive fonctionne très bien, c'est très propre !

Je mets le code modifié :

Code : Tout sélectionner

public void AfficherJoueurs()
    {
        foreach (Transform child in ContentChoixPlayer.transform) { child.gameObject.SetActive(false); }

        Players[] Player = JsonHelperPlayers.FromJson<Players>(calculStats.ListePlayers);

        int nbr = 0;

        int taillePlayerTab = Player.Length;
        for (int i = 0; i < taillePlayerTab; i++)
        {
            if (nomJoueur.text == Player[i].Nom)
            {
                //Instantiate(BouttonChoixPlayer, ContentChoixPlayer.transform);

                ContentChoixPlayer.transform.GetChild(nbr).gameObject.SetActive(true);
                ContentChoixPlayer.transform.GetChild(nbr).transform.GetChild(0).GetComponent<Text>().text = Player[i].ID;

                byte[] Bytes = Convert.FromBase64String(calculStats.ListeMiniatures[i]);
                Texture2D newSprite = new Texture2D(1, 1);
                newSprite.LoadImage(Bytes);
                Image Img = ContentChoixPlayer.transform.GetChild(nbr).GetComponent<Image>();
                Sprite mySprite = Sprite.Create(newSprite, new Rect(0.0f, 0.0f, newSprite.width, newSprite.height), new Vector2(0.5f, 0.5f), 100.0f);
                Img.sprite = mySprite;

                nbr++;
            }
        }
    }
Je considère le sujet résolu ! Je laisse ouvert et je fermerai tout à l'heure s'il n'y a pas d'autres solutions qui pourrait enrichir mes connaissances.

Merci beaucoup à vous :super:

Répondre

Revenir vers « Scripting »