0
\$\begingroup\$

I'm trying to access a method in another script without using the keyword "static";

public void CreateFarm()
{
    new Farm().NewResourceBuilding(gameObject);
}

My question is; will Unity automatically delete this object once it's done calling the method? Or will I need to manually destroy it? If the latter, how will I go about that?

I've searched on Google and been on this problem for a few days until I've finally come to this solution lol. So annoying to reference a static yet want to add a gameobject via the inspector.


Edit (Left the previous content untouched as some of answers already provided for the above might be useful to others). I have since revised my code and the flow.

I have a scene with a number of "empty lots" which I use images as placeholders childed to a Canvas.

The following steps occur:

Clicking on any of these images calls Unity's event system to open a panel, from the onclick options of the inspector. This panel will allow me to select from a number of different buildings to construct.

ChangePanels.CS

public void RuralBuildingSelected()
{
    empirePanel.SetActive(false);
    characterPanel.SetActive(false);
    ruralBuildingPanel.SetActive(true);
}

Why I wanted to avoid "static" was because no static method is callable from the inspector (AFAIK). I could have created my own script and called a public onclick, but perhaps one of you might be able to advise which option of these is more efficient.

Whichever "emptylot" I clicked on, I wanted them to send through their data into a manager script so whatever building I built, could replace the emptylot.

EmptyLot.CS

public void CreateFarm()
{
    new GameManager().GetEmpty(gameObject);
}

The following follow gives you an option of a number of buildings to construct. If I click on the farm, I need to;

  • Instantiate a new Farm
  • Parent it to the emptyLot's parent
  • To replace the emptylot with the new farm, I set them to have the same positions
  • Destroy the emptylot that called the new farm into existence
  • Bring back the main panel

Farm.CS

new public void NewResourceBuilding(ResourceBuildings resource)
    {
        Farm newFarm = Instantiate(resource) as Farm;
        newFarm.transform.SetParent(GameManager.emptyLot.transform.parent);
        newFarm.transform.position = GameManager.emptyLot.transform.position;
        newFarm.levelText = newFarm.GetComponentInChildren<Text>();
        newFarm.name = resource.name;
        Destroy(GameManager.emptyLot);
        GameObject main = GameObject.Find("Main Canvas");
        main.GetComponent<ChangePanels>().MainScene();
    }

My Questions:

  1. The "EmptyLot.CS" script creates a new GameManager to call a public method. I don't reference the object. Will this automatically be added to the garbage collector or should I just destroy it after using it? If I need to destroy it manually, how would I go about that?

  2. I note that if I went ahead with the static methods and implement my own OnClick() method I can condense this to just two scripts instead of the three. So should I do that? Or based on the above code, is it just as efficient to keep it the way it is? I'm not a pro at this, but inline with Wondra's comment (to Almo's answer) it does seem like the approach of creating objects to delete instantly is a performance issue?

Thank you for taking the time to read/help/advice

\$\endgroup\$
  • \$\begingroup\$ Can you include an example of what this Farm type looks like? Your title refers to deleting unused GameObjects — does Farm create a GameObject? Or just use the existing gameObject reference you're passing it? If you don't actually need a persistent instance of Farm, it's likely that you can refactor your code to avoid allocating one in the first place. \$\endgroup\$ – DMGregory Apr 3 '18 at 16:21
  • \$\begingroup\$ @DMGregory thank you for your comment. I've updated the question \$\endgroup\$ – Anon Apr 4 '18 at 12:34
1
\$\begingroup\$

I'm going to take a step back here because I think there's some confusion about fundamentals of the C# language and how it works in Unity.

Firstly, what happens when I say new Foo() in this method...

void DoSomeFooStuff() {
    new Foo().DoSomeStuff();
}

...depends on how the Foo type is defined.

  • If Foo is the Unity type GameObject then it creates a GameObject and adds it to the current active scene. That's an object you can see & select in the hierarchy, move in the scene view, has a name, layer, tag, a Transform component, and can have other components attached to it in the Inspector or with AddComponent<>().

    Because the scene now holds a reference to this GameObject, it will not be automatically be cleaned up. Other scripts can obtain a reference to it by walking the scene hierarchy or using various Find methods. It will stay persistent until you manually Destroy() it, or until you unload the scene it's in.

    Not every object you work with in a Unity game is a GameObject. Most objects you create this way will not be GameObjects, so I think your title "Destroy unused GameObjects" might be misleading. It doesn't look to me like Farm or GameManager are GameObjects (though since you haven't shown us their definitions or constructors, it's hard to know exactly what they are or do)

  • If Foo is a struct, then this puts a Foo instance straight onto the stack with a default initializer (eg. all numeric members default to 0), works with it in place, then pops it back off the stack when the function returns. No further cleanup is needed.

  • If Foo is a class, then this allocates space for a new Foo instance on the managed heap (a new garbage collector allocation), initializes its fields, and constructs it using its parameterless constructor if it has one.

    After that line has executed, there are no more references pointing to this Foo instance and it is now effectively garbage. It's still taking up space in memory, but nobody can call on it to do anything useful because nobody has a reference to it.

    The next time you ask the memory management system for more memory than it can immediately serve, it will invoke the garbage collector, which will mark everything that's still referenced and delete what's not. At that point this garbage goes away and you get the memory back. (This is a slight simplification ignoring generational garbage collection and compaction. But for a small & short-lived allocation like this those details don't change the conclusion)

    • A caveat to this is if Foo's constructor method Foo() stores a reference to itself somewhere, like in a master list of Foo instances (similar to how new GameObject() stores a reference to the new GameObject instance in the scene hierarchy). If it does, then it's not garbage as far as the GC is concerned, and it won't be reclaimed until after all references to it are gone.

Okay, with all that out of the way, I'd recommend a substantial refactor here.

You already have an empty lot that can accept clicks. Instead of thinking of it as an "Empty" lot that needs to be destroyed when built upon, or possibly recreated when the building is removed (more object creation & deletion, allocation & garbage collection), why not just think of it as a "Lot" that can hold multiple building types?

public class BuildingLot : MonoBehaviour {

    public IBuildingComponent currentBuilding;
    public GameObject emptyVisual;

    // This slot instance can handle the job of receiving clicks
    // & managing building selection state.
    public void Select() {
        // If we have a building, 
        // delegate our selection behaviour 
        // to that building's unique functionality
        if(currentBuilding != null) {
            currentBuilding.Select();
            return;
        }

        // Otherwise, tell the building manager an empty slot
        // has been selected, so it knows to display the right
        // building UI, and knows which slot to build on next.
        BuildingManager.GetInstance().SetSelectedLot(this);
    }

    // Your building manager can call this on the currently selected slot
    // when a particular building type button is pressed.
    public void Build(IBuildingComponent buildingPrefab) {
        // Alternatively, we can recycle an existing building
        // instance from an object pool, rather than allocate a new one.
        currentBuilding = Instantiate(buildingPrefab);
        currentBuilding.transform.SetParent(transform, false);

        // Hide any "blank/buildable slot" graphics or effects.
        emptyVisual.SetActive(false);

        // The new building instance can figure out what setup is 
        // appropriate for the new lot it finds itself in.
        currentBuilding.Initialize(this);
    }

    // If you have a clear / destroy method, you can restore the slot to empty.
    public void Clear() {
        if(currentBuilding == null)
             return;

        // Clear our building (or recyclce it back to an object pool)
        Destroy(currentBuilding.gameObject);
        currentBuilding = null;

        // Restore normal empty appearance.
        emptyVisual.SetActive(true);
    }
}

Your build method then just needs to select the right prefab based on which button was clicked, and pass it to the currently selected BuildingLot to construct. The configuration logic can live in the building component's Initialize(BuildingLot lot) instance method, so you're working with the instance of eg. Farm you actually want to keep, rather than constructing a throwaway first.

\$\endgroup\$
0
\$\begingroup\$

C# has a garbage collector, and will automatically get rid of unused objects.

However, do not use that as an excuse to allocate and forget about objects a lot. Over time it will fragment memory and cause problems.

\$\endgroup\$
  • \$\begingroup\$ Thank's Almo. Any idea on how to delete an object like that? Since there is no reference name I can call it by. \$\endgroup\$ – Anon Apr 3 '18 at 14:37
  • \$\begingroup\$ I think we need more info about how your code works to answer this properly. Why are you avoiding static? There are other possible ways, like the Singleton pattern. (I know, Josh will kill me) \$\endgroup\$ – Almo Apr 3 '18 at 15:32
  • \$\begingroup\$ @RichardMonjed the point is not-to instantiate (a lot of) objects that you will instantly throw away in the first place. \$\endgroup\$ – wondra Apr 3 '18 at 16:32
  • \$\begingroup\$ @wondra I agree. I'm thinking I'll need to implement my own OnClick method as creating an object to instantly destroy sounds like it will create unnecessary overhead. \$\endgroup\$ – Anon Apr 4 '18 at 12:37
  • \$\begingroup\$ Unity has its own garbage collector, and does not use C#'s. \$\endgroup\$ – Evorlor Sep 4 '18 at 23:09
0
\$\begingroup\$

From the code you posted it is not possible to tell whether the Farm object that will be created will be destroyed or not by garbage collection. It is true that C# has garbage collection and if no references exist to the Farm object then it would be deleted, but when an object is instantiated it could run a constructor method that could create a reference to itself in some other part of your program. The Singleton Pattern mentioned in the comments is actually an example of a time when you might set a reference from a constructor. In a case like this you should manually destroy it to be sure. It could save you time debugging issues in the future.

\$\endgroup\$
  • \$\begingroup\$ @Tarte Wizard, thank you for your comment. I've updated the question \$\endgroup\$ – Anon Apr 4 '18 at 12:35
0
\$\begingroup\$

You can use the code:

 Destroy(gameobject);

at the end of the process in the gameobject you want to dispose.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.