Page 1 sur 1

[RESOLU] Garder une liste d'instances de GO sur une nouvelle scène

Publié : 03 Sep 2020 14:48
par DarkFlameMaster
Hello, j'étais passé me représenter il y a 3ans pensant me remettre à fond dans le GameDev mais au final mes études ont vraiment prit le dessus et donc j'ai pas du tout participé après ma réinscription désolé :-|
Au final maintenant je suis dans mon année finale où on a un projet pro à faire tout au long de l'année et pour moi ça sera un JV.

Tout se passe bien pour le moment mais j'arrive à un point ou je bloque tout seul et avec l'aide trouvable sur internet.
C'est la 1ère fois que je veux garder des données d'une scène à l'autre et c'est assez obscur pour moi, en me renseignant j'ai vu qu'il y avait globalement 5 méthodes pour garder des valeurs d'une scène à l'autre :

- une classe en static pour store tout les valeurs voulues
- utiliser un object en DontDestroyOnLoad qui store toutes les valeurs
- les PlayerPrefs
- Sauvegarder sur un fichier mais ça me semble overkill sachant que je ne veux pas sauvegarder entre chaque session de jeu
- utiliser un singleton mais de un je ne comprend pas trop le concept et de deux ça a l'air d'être un sujet vachement controversé

Mon problème vient du fait que je n'ai vraiment aucune idées de quelle méthode adopter(surtout parmi les 3 premières vu que les 2 dernières me semblent pas bonnes pour mon utilisation).

Pour clarifier la situation je cherche à faire une sorte de créateur d'armée ,l'armée est composée d'escouades qui sont elles mêmes composées d'unités individuelles.

Pour le moment je procède comme ceci :

J'ai un script "Army_Manager" qui gère les escouades, je peux les sélectionner, en rajouter, connaitre le nombre d'escouades etc...
Chaque escouade a un script "Squad" qui lui contient le type et le nombre d'unités dans celle ci.

En gros dans "Army_Manager" j'ai une Liste de GameObjects qui contient toutes mes escouades et dans "Squad" j'ai une liste de GameObject qui contient toutes les unités dans une escouade.

Ce que j'aimerai en gros c'est de garder chaque escouade avec leurs propres données contenues dans le script "Squad" et de les transférer dans une autre scène, mais c'est là que ça bloque :/

J'ai tenté quelque chose avec une classe statique pour stocker la liste d'escouades mais sans surprise ça ne marche pas, ça me créer une liste avec le bon nombre d'escouades mais les valeurs sont nulles.

Les scripts qui me semblent utile de linker, y'a pas mal de trucs sans rapport mais j'ai essayé d'être le plus clair possible avec des commentaires etc.. j'espère que c'est compréhensible :

Army_Creator :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;

public class Army_Creator : MonoBehaviour
{
    public List<GameObject> squads = new List<GameObject>();
    public List<GameObject> selected_units = new List<GameObject>();

   public GameObject current_squad;
    GameObject last_squad;

    public GameObject squad_prefab;

    int ofset_x = 0;

    public LayerMask squad_mask;
    public LayerMask unit_mask;

    public GameObject unit_Selection_List;

    public TextMeshProUGUI points_text;
    public int army_points;

    private void Start()
    {
        //Hide the unit selection list
        unit_Selection_List.SetActive(false);
    }

    private void Update()
    {
        Select_Squad();
        Select_Unit();
    }

    public void SaveArmy()
    {
        foreach(GameObject squad in squads)
        {
            Army_Manager.player_one_squads_list.Add(squad);
            SceneManager.LoadScene("Test_Map");
        }
    }

    public void Update_Score()
    {
        army_points = 0;

        foreach (GameObject squad in squads)
        {
            army_points += squad.GetComponent<Squad>().points_cost;
            points_text.text = "points : " + army_points;
        }
    }

    public void Show_Unit_Selection_List(bool is_active)
    {
        if (is_active)
        {
            unit_Selection_List.SetActive(true);
        }
        else
        {
            unit_Selection_List.SetActive(false);
            unit_Selection_List.GetComponent<Unit_Selection_List_Script>().isActive = false;
        }
    }


    public void AddSquad()
    {
        //Create a squad
        GameObject squad_instance = Instantiate(squad_prefab, transform.position + new Vector3(ofset_x, 0, 0), Quaternion.identity);
        squads.Add(squad_instance);
        Update_Score();

        //ofset to the instat<niated squad
        ofset_x += 20;
    }

    public void Add_Unit_To_Selected_Squad()
    {
        //Instantiate an unit in the squad
        if (current_squad != null)
        {
            current_squad.GetComponent<Squad>().AddUnit(current_squad.GetComponent<Squad>().squad_member.prefab);
            Update_Score();
        }
    }

    public void Select_Squad()
    {
        //Select a squad and change the current squad
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, Mathf.Infinity, squad_mask))
        {
            if (Input.GetButtonDown("Fire1"))
            {
                Change_Current_Squad(null, hit.transform.parent.gameObject);
            }
        }

    }

    public void Select_Unit()
    {
        //Select an unit inside the selected squad
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, Mathf.Infinity, unit_mask))
        {
            if (Input.GetButtonDown("Fire1"))
            {
                hit.transform.GetComponent<Unit_Script>().Select_Unit(gameObject, false);

                //Show_Unit_Selection_List(true);
                unit_Selection_List.GetComponent<Unit_Selection_List_Script>().Generate_Buttons(current_squad, gameObject);
            }
        }
    }

    public void Clear_Unit_Selection()
    {
        //Deselect all units selected
        foreach(GameObject unit in selected_units)
        {
            unit.GetComponent<Unit_Script>().Select_Unit(gameObject, true);
            //selected_units.Remove(unit);
        }

        //Clear the list of selected units
        selected_units.Clear();

        //Hide the unit selection list
        Show_Unit_Selection_List(false);
    }

    public void Change_Current_Squad(GameObject last_squad, GameObject _current_squad)
    {
        //if there is already a current squad then put it as last squad
        if (current_squad != null)
        {
            last_squad = current_squad;
            current_squad = _current_squad;
        }
        //if it's the first time don't set any last squad
        else
        {
            current_squad = _current_squad;
        }

        //print("last squad is " + last_squad);
        //print("current squad is " + current_squad);

        //Select the current squad
        current_squad.GetComponent<Squad>().selected = true;

        //if there is a last squad deselect it
        if (last_squad != null)
            last_squad.GetComponent<Squad>().selected = false;

        //Deselect all selected units
        Clear_Unit_Selection();
    }

}

Squad :

Code : Tout sélectionner

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

public class Squad : MonoBehaviour
{
    public List<GameObject> units = new List<GameObject>();
    public List<Unit> available_unit_types = new List<Unit>();

    public Unit leader;
    public Unit squad_member;

    public GameObject squad_selector;

    public Material selected_material;
    public Material not_selected_material;

    int x = 2;
    int z = 0;

    public int max_units = 20;
    int units_number = -1;

    public int points_cost;

    public bool selected = false;

    GameObject army_creator;
    Army_Creator army_creator_ref;

    private void Start()
    { 
        GameObject current_instance = Instantiate(leader.prefab, transform.position, transform.rotation);
        current_instance.transform.parent = transform;
        units.Add(current_instance);
        units_number++;

        army_creator = GameObject.Find("Army_Creator");
        army_creator_ref = army_creator.GetComponent<Army_Creator>();
    }

    private void Update()
    {
        Selected();
    }

    public void AddUnit(GameObject added_unit)
    {
        if (units_number < (max_units - 1))
        {
            GameObject current_instance = Instantiate(added_unit, transform.position + new Vector3(x, 0, z), transform.rotation);
            current_instance.transform.parent = transform;

            units.Add(current_instance);

            x += 2;
            units_number++;
            current_instance.GetComponent<Unit_Script>().number_in_the_list = units_number;
            int current_unit_point_cost = 0;
            current_unit_point_cost = current_instance.GetComponent<Unit_Script>().unit_ref.point_cost;

            //print(current_instance.GetComponent<Unit_Script>().unit_point_cost);
            points_cost += current_unit_point_cost;

            if (x >= 8)
            {
                z += 2;
                x = 0;
            }
        }
    }

    public void Replace_unit(GameObject replaced_unit, GameObject replacing_unit, int _i)
    {
        units[_i] = replacing_unit;
        units[_i].transform.SetParent(transform);
        units[_i].GetComponent<Unit_Script>().number_in_the_list = replaced_unit.GetComponent<Unit_Script>().number_in_the_list;

        points_cost -= replaced_unit.GetComponent<Unit_Script>().unit_ref.point_cost;
        points_cost += replacing_unit.GetComponent<Unit_Script>().unit_ref.point_cost;

        army_creator_ref.Update_Score();

        Destroy(replaced_unit);
    }


    public void Selected()
    {
        if (selected == true)
        {
            squad_selector.GetComponent<Renderer>().material = selected_material;
            foreach (GameObject unit in units)
            {
                unit.layer = LayerMask.NameToLayer("Units");
            }
        }
        else
        {
            squad_selector.GetComponent<Renderer>().material = not_selected_material;
            foreach (GameObject unit in units)
            {
                unit.layer = LayerMask.NameToLayer("Default");
            }
        }
    }
}

La classe statique dans laquelle je stocke ma liste d'escouades :

Code : Tout sélectionner

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

public static class Army_Manager
{
    public static List<GameObject> player_one_squads_list = new List<GameObject>();
}
et le code ou je load cette liste dans une nouvelle scène :

Code : Tout sélectionner

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

public class Army_Placer : MonoBehaviour
{

    public List<GameObject> player_one_squads;
    private void Awake()
    {
        foreach(GameObject squad in Army_Manager.player_one_squads_list)
        {
            player_one_squads.Add(squad);
        }

        print(player_one_squads.Count);
    }
}
Voilà, désolé du pavé mais j'ai essayé d'être le plus clair possible, merci d'avance pour l'aide éventuelle :)

Re: Garder une liste d'instances de GO sur une nouvelle scène

Publié : 03 Sep 2020 15:13
par boubouk50
J'ai pas lu le pavé ni le code.

La fonction DontDestroyOnLoad te permet de garder en vie les gameObjects affectés entre les scènes, donc ne sont pas détruits à un changement de scène.
Par contre, cette solution ne fonctionne qu'au runtime, durant la période de jeu. Il n'y a aucune sauvegarde.

Passer par une ressource externe qui enregistre les configurations et pouvant être lues n'importe où est une solution plus persistante, par contre il te faut passer une référence. Là, tu as l'embarras du choix (ScriptableObject, XML, JSON, etc)

Rien ne t'empêche d'associer les deux. Un GameObject qui ne se détruit pas, qui gère ton armée et qui sauvegarde et charge les données depuis un fichier.

Re: Garder une liste d'instances de GO sur une nouvelle scène

Publié : 03 Sep 2020 16:23
par DarkFlameMaster
Merci de ta réponse

Je vais voir avec le DontDestroyOnLoad, le truc c'est que je suppose que si je garde mon objet qui gère l'armée les listes seront nulles quand même au changement à moins de mettre chaque squad et unités en DontDestroyOnLoad ? C'est quelque chose d'envisageable ou c'est pas fait pour gérer un grand nombre de GameObjects ?

Re: Garder une liste d'instances de GO sur une nouvelle scène

Publié : 03 Sep 2020 16:38
par boubouk50
Seuls les gameObjects DontDestroyOnLoad ne sont pas détruits (et ses enfants). Si tu références des objets qui sont détruits, ces références seront nulles. Il faudra donc les recharger.

Aussi, une autre possibilité est le chargement additif de scène.
Tu peux posséder une scène qui gère ton armée, qui reste fixe, et charger/décharger les scènes annexes.

Tout ceci doit être bien défini clairement avec tous les tenants et aboutissants pour ne pas se faire piéger.
Si tu fais un jeu basé sur des niveaux qui se suivent, tu dois déterminer la persistance. Est-ce que ton armée reste visible tout le temps entre les scènes? Si oui, il faut que l'armée soit persistante. Si non, tu peux alors recharger ton armée au début de chaque scène selon le même pattern.
A toi de décider.

Au delà du code, il y a d'abord le design qui répond à une simple question: Qu'est ce que je veux réellement faire. Visuellement. Ensuite seulement arrive la mise en code.

Re: Garder une liste d'instances de GO sur une nouvelle scène

Publié : 03 Sep 2020 17:05
par DarkFlameMaster
Ok merci beaucoup :)

Je vais voir comment gérer tout ça, j'ai maintenant quelques pistes grâce à toi.

Concrètement il n'y aura que 2 scènes par parties, du moins où je vais devoir gérer l'armée, celle de création, et une seconde scène où il y aura la bataille, sachant qu'on replacera chaque escouade une à une.

Je vais voir ce que ça donne en mettant chaque escouade enfant de mon army_manager, le mettre en DontDestroyOnLoad et comme ça je garde chaque valeurs comme il faut

Re: Garder une liste d'instances de GO sur une nouvelle scène

Publié : 04 Sep 2020 19:41
par DarkFlameMaster
Du coup le DontDestroyOnLoad marche nickel, toute l'armée reste à sa place, une fois que je load la 2nd scène de déparente et je désactive le tout.

Merci beaucoup pour ton aide :)

Une fois ta réponse obtenue passe ton sujet en "Résolu" en éditant le titre du sujet.
Je le fais pour toi cette fois ci ;-)