Search Unity

Use same class instance in other classes

Discussion in 'Scripting' started by thespragg, Mar 22, 2019.

  1. thespragg

    thespragg

    Joined:
    Jun 28, 2018
    Posts:
    8
    The structure of my code is as follows:

    Public class Generation : monobehaviour
    - contains generator method
    Public class planet
    - contains health check method
    Public class earth : planet

    So, in Generation it creates a new planet using new earth() and stores it to a public variable CurrentPlanet.

    Now inside the planet class there's a method to check if the planet is dead on an event being triggered, and if yes it should generate a new planet uses a method inside the Generation class and sets CurrentPlanet to be the new planet.

    However, I'm trying to avoid using static on the CurrentPlanet variable and PlanetGeneration method (I did this a lot when starting the game and its now created a large mess) so initially I thought I could do Generation generator = new Generation() and then call generator.GeneratePlanet() but that doesn't set the same instance of CurrentPlanet as the first one is attached to.

    How, without making my CurrentPlanet and GeneratePlanet methods static and therefore stateless can I call the generateplanet method from within an instanced object and have it basically overwrite itself with a new planet?
     
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    Just store a reference to the Generation class in the planet class when it is created by the Generation class. Then when you create a planet in that class, first check if currentplanet is null and if it is not, destroy what is in it.
     
  3. thespragg

    thespragg

    Joined:
    Jun 28, 2018
    Posts:
    8
    Just after you replied I realised where I went wrong, I was passing the current instance of Generation into the instanced planet class, but then still using a new instance of Generation to actually call the function.

    I'm kinda getting code smell from how this works though:

    Generation contains function called GenerateNewPlanet that takes an instance of Generation as a parameter.
    The instance gets passed into the constructor of the Planet and stored for when the planet dies
    The instance is then passed back to the GeneratePlanet method again to generate a new planet

    It just seems like I could be opening myself up to problems by passing the instance of the Generation class in and out of every planet object that gets generated or is this ok?
     
  4. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    It's just a reference, there is nothing wrong with that. Although why does Generation have a reference to another instance? Post your code for the planet and generation class, maybe we can clean it up.
     
  5. thespragg

    thespragg

    Joined:
    Jun 28, 2018
    Posts:
    8
    Here are the import parts, the planet object is pretty large and has multiple subclasses but most of the code works fine so won't include it

    This is the planet generator file

    Code (CSharp):
    1. public class PlanetGeneration : MonoBehaviour
    2. {
    3.     public PlanetGeneration Instance { get; set; }
    4.     public GameObject TimerBG { get; set; }
    5.     public GameObject TimerFG { get; set; }
    6.     public Image TimerFGfill { get; set; }
    7.     public GameObject SunExplosions { get; set; }
    8.     public Transform TimerFGchild { get; set; }
    9.     public TMP_Text PlanetMassLabel;
    10.  
    11.     public ICurrentPlanetInterface CurrentPlanet { get; set; }
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.         Instance = this;
    17.  
    18.         #region FindGameObjects
    19.         TimerBG = GameObject.Find("BossTimerBG");
    20.         TimerFG = GameObject.Find("BossTimerFG");
    21.         TimerFGfill = GameObject.Find("BossTimerFG").GetComponent<Image>();
    22.         TimerFGchild = TimerFGfill.transform.GetChild(0);
    23.         #endregion /FindGameObjects
    24.  
    25.         TimerBG.SetActive(false);
    26.         TimerFG.SetActive(false);
    27.         SunExplosions = GameObject.Find("Sun Explosions");
    28.  
    29.         //Create first planet
    30.         CurrentPlanet = GenerateNewPlanet(1,Instance);
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update()
    35.     {
    36.         CurrentPlanet.DecreaseMassFromUpgrades();
    37.         CurrentPlanet.ShrinkingToScale();
    38.         PlanetMassLabel.text = NumberSanitiser.CleanNumber(CurrentPlanet.CurrentPlanetMass).ToString();
    39.     }
    40.  
    41.     public ICurrentPlanetInterface GenerateNewPlanet(int PlanetNumber, PlanetGeneration CurrentInstance)
    42.     {
    43.         int x = Random.Range(1, 7);
    44.         if (x==1) return CurrentPlanet = new RegularPlanet(PlanetNumber, CurrentInstance);
    45.         if (x==2) return CurrentPlanet = new CrackedPlanet(PlanetNumber, CurrentInstance);
    46.         if (x==3) return CurrentPlanet = new GasPlanet(PlanetNumber, CurrentInstance);
    47.         if (x==4) return CurrentPlanet = new IcePlanet(PlanetNumber, CurrentInstance);
    48.         if (x==5) return CurrentPlanet = new MetalPlanet(PlanetNumber, CurrentInstance);
    49.         if (x==6) return CurrentPlanet = new SandPlanet(PlanetNumber, CurrentInstance);
    50.         if (x==7) return CurrentPlanet = new WaterPlanet(PlanetNumber, CurrentInstance);
    51.         return null;
    52.     }
    53. }
    Planet class:

    Code (CSharp):
    1. public class PlanetObject : ICurrentPlanetInterface
    2. {
    3.  
    4. private PlanetGeneration CurrentInstance;
    5.  
    6. public PlanetObject(int NumberOfPlanetsGenerated, PlanetGeneration PlanetGenerationInstance)
    7.    {
    8. //Set current instance to the one passed by PlanetGeneration
    9. CurrentInstance = PlanetGenerationInstance;
    10.      
    11. //Other vars etc set in rest of constructor but not needed for this
    12. }
    13. }
    Function that handles if a new planet is needed

    Code (CSharp):
    1. //This gets called on click
    2. public void IsPlanetDead()
    3.     {
    4.         CurrentPlanetMass -= ClickUpgrades.CalculateTotalMPC();
    5.         //Revisit this, could be more efficient, drone 10 is confusing, maybe create subclass
    6.         if (CurrentPlanetMass == 0 || CurrentPlanetMass - Upgrades.Drone10.AmountToIncreaseClick < 1) // && !isSun
    7.         {
    8.             AddToMasses(MassBonus);
    9.  
    10.            //Generate new planet by passing instance back to planet generator
    11.             CurrentInstance.GenerateNewPlanet(PlanetNumber + 1, CurrentInstance);
    12.         }
    13.     }
     
  6. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    At a quick glance, I would make a few changes to your PlanetGeneration class;

    Code (CSharp):
    1. public class PlanetGeneration : MonoBehaviour
    2. {
    3.     //As this isn't static, it's fairly pointless
    4.     //public PlanetGeneration Instance { get; set; }
    5.     public GameObject TimerBG { get; set; }
    6.     public GameObject TimerFG { get; set; }
    7.     public Image TimerFGfill { get; set; }
    8.     public GameObject SunExplosions { get; set; }
    9.     public Transform TimerFGchild { get; set; }
    10.     public TMP_Text PlanetMassLabel;
    11.  
    12.     //Get a reference to the class, not the interface
    13.     public PlanetObject CurrentPlanet { get; set; }
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.         //Instance = this;
    19.  
    20.         #region FindGameObjects
    21.         //I would suggest using GameObject.FindObjectOfType<> and using the classes here rather than the gameobject names if there will only ever be one of each in the scene
    22.         //as it will not fail should the name change.
    23.         /* For Example
    24.          * BossTimerFG TimerFG;
    25.          *
    26.          * private void Start()
    27.          * {
    28.          *      TimerFG = GameObject.FindObjectOfType<BoosTimerFG>();
    29.          *      TimerFGFill = TimerGF.GetComponent<Image>(); // or TimerFGFill = TimerFG.Image; //if the class has a reference already
    30.          *  }
    31.          */
    32.         TimerBG = GameObject.Find("BossTimerBG");
    33.         TimerFG = GameObject.Find("BossTimerFG");
    34.         TimerFGfill = GameObject.Find("BossTimerFG").GetComponent<Image>();
    35.         TimerFGchild = TimerFGfill.transform.GetChild(0);
    36.         #endregion /FindGameObjects
    37.  
    38.         TimerBG.SetActive(false);
    39.         TimerFG.SetActive(false);
    40.         SunExplosions = GameObject.Find("Sun Explosions");
    41.  
    42.         //Create first planet
    43.         GenerateNewPlanet(1);
    44.     }
    45.  
    46.     // Update is called once per frame
    47.     void Update()
    48.     {
    49.         CurrentPlanet.DecreaseMassFromUpgrades();
    50.         CurrentPlanet.ShrinkingToScale();
    51.         PlanetMassLabel.text = NumberSanitiser.CleanNumber(CurrentPlanet.CurrentPlanetMass).ToString();
    52.     }
    53.  
    54.     public void GenerateNewPlanet(int PlanetNumber)
    55.     {
    56.         if(CurrentPlanet != null)
    57.         {
    58.             //Do Whatever you wish with the old planet here
    59.         }
    60.  
    61.         //The 2nd parameter in the Random.Range overload method which returns an int is exclusive so you need to set it  1 higher than desired
    62.         var x = Random.Range(1, 8);
    63.  
    64.         switch (x)
    65.         {
    66.             case 2:
    67.                 CurrentPlanet = new CrackedPlanet(PlanetNumber, this);
    68.                 break;
    69.             case 3:
    70.                 CurrentPlanet = new GasPlanet(PlanetNumber, this);
    71.                 break;
    72.             case 4:
    73.                 CurrentPlanet = new IcePlanet(PlanetNumber, this);
    74.                 break;
    75.             case 5:
    76.                 CurrentPlanet = new MetalPlanet(PlanetNumber, this);
    77.                 break;
    78.             case 6:
    79.                 CurrentPlanet = new SandPlanet(PlanetNumber, this);
    80.                 break;
    81.             case 7:
    82.                 CurrentPlanet = new WaterPlanet(PlanetNumber, this);
    83.                 break;
    84.             default:
    85.                 CurrentPlanet = new RegularPlanet(PlanetNumber, this);
    86.                 break;
    87.         }
    88.     }
    89. }
     
  7. thespragg

    thespragg

    Joined:
    Jun 28, 2018
    Posts:
    8
    CurrentPlanet won't ever be null, its why i wanted to call the check generating the new object from within the old object so that if one object is dead itll overwrite itself with the next planet.

    Changin the bosstimer stuff makes sense though, was also tempted to find a way to attach them through the inspector instead of using find since there is only even one instance of the boss timer which just gets set and unset based on if the player has encountered a boss, I might however remove that code completely and put it into the boss object so its encapsulated within the relevant class.
     
  8. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    To attach them through the inspector, either;
    Code (CSharp):
    1. // Do this
    2. public GameObject TimerBG;
    3.  
    4. or
    5. // this
    6. [SerializedField]
    7. private GameObject TimerBG
    As for current planet, I didn't add a check to see if it is null, I added a check to see if it isn't null as it will be null the first time you call the method.
     
    Last edited: Mar 22, 2019