Should i put all the Spheres i create in the script as childs under Terrain in the Hierarchy ?

The script create number of Spheres in random positions on the Terrain.
When i create for example 100 Spheres on the Hierarchy on the left i see 100 Spheres gameobjects.
I wonder if to make it nicer to view to put them all as childs under the Terrain is a good idea ?

And if it’s a good idea is it good place to put it in the Update function after the line UpdateSpheres(); ?

Second thing is with the Destory. I’m doing:

foreach (var t in spheres)
        {
            if (Application.isEditor)
            {
                DestroyImmediate(t);
            }
            else
            {
                Destroy(t);
            }
        }

The question is if using DestroyImmediate(t); is fine ?
Maybe i should use Destroy() instead and don’t expect the GO being destroyed as soon as the code runs, but whenever the system likes (so store somewhere it’s gone, etc) ?

Not sure how to handle the Destory part.

This is the whole script code:

using System;
using UnityEngine;
using Random = UnityEngine.Random;

[ExecuteInEditMode]
public class SphereBuilder : MonoBehaviour
{
    // for tracking properties change
    private Vector3 _extents;
    private int _sphereCount;
    private float _sphereSize;

    /// <summary>
    ///     How far to place spheres randomly.
    /// </summary>
    public Vector3 Extents;

    /// <summary>
    ///     How many spheres wanted.
    /// </summary>
    public int SphereCount;

    public float SphereSize;

    private void OnValidate()
    {
        // prevent wrong values to be entered
        Extents = new Vector3(Mathf.Max(0.0f, Extents.x), Mathf.Max(0.0f, Extents.y), Mathf.Max(0.0f, Extents.z));
        SphereCount = Mathf.Max(0, SphereCount);
        SphereSize = Mathf.Max(0.0f, SphereSize);
    }

    private void Reset()
    {
        Extents = new Vector3(250.0f, 20.0f, 250.0f);
        SphereCount = 100;
        SphereSize = 20.0f;
    }

    private void Update()
    {
        UpdateSpheres();
        GameObject terrain = GameObject.Find("Terrain");
        foreach (var obj in FindObjectsOfType(typeof(GameObject)) as GameObject[])
        {
            if(obj.name == "Sphere")
                obj.transform.parent = terrain.transform;
        }
    }

    private void UpdateSpheres()
    {
        if (Extents == _extents && SphereCount == _sphereCount && Mathf.Approximately(SphereSize, _sphereSize))
            return;

        // cleanup
        var spheres = GameObject.FindGameObjectsWithTag("Sphere");
        foreach (var t in spheres)
        {
            if (Application.isEditor)
            {
                DestroyImmediate(t);
            }
            else
            {
                Destroy(t);
            }
        }

        var withTag = GameObject.FindWithTag("Terrain");
        if (withTag == null)
            throw new InvalidOperationException("Terrain not found");

        for (var i = 0; i < SphereCount; i++)
        {
            var o = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            o.tag = "Sphere";
            o.transform.localScale = new Vector3(SphereSize, SphereSize, SphereSize);

            // get random position
            var x = Random.Range(-Extents.x, Extents.x);
            var y = Extents.y; // sphere altitude relative to terrain below
            var z = Random.Range(-Extents.z, Extents.z);

            // now send a ray down terrain to adjust Y according terrain below
            var height = 10000.0f; // should be higher than highest terrain altitude
            var origin = new Vector3(x, height, z);
            var ray = new Ray(origin, Vector3.down);
            RaycastHit hit;
            var maxDistance = 20000.0f;
            var nameToLayer = LayerMask.NameToLayer("Terrain");
            var layerMask = 1 << nameToLayer;
            if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
            {
                var distance = hit.distance;
                y = height - distance + y; // adjust
            }
            else
            {
                Debug.LogWarning("Terrain not hit, using default height !");
            }

            // place !
            o.transform.position = new Vector3(x, y, z);
        }

        _extents = Extents;
        _sphereCount = SphereCount;
        _sphereSize = SphereSize;
    }
}

You definitely should NOT put that code in Update. You Finding the GameObject Terrain… then setting all the sphere’s parentes to that terrain. 40,50,60 times a second. over and over. No reason to do this… Just change Update to this:

private void Update()
    {
        UpdateSpheres();
    }

Then in Update Spheres whenever you create a sphere… set its parent there:

var o = GameObject.CreatePrimitive(PrimitiveType.Sphere);
o.transform.parent = withTag.transform;

Though I might suggest changing the variable o to something like newSphere, and maybe withTag to terrain. If you come back to this code much later, it will be easier to read it. If your scanning this code months later, or if someone else is looking at it, seeing o.something is much harder to figure out than newSphere.something

This is working fine. What about the Destroy part ?

When you say “That is working fine” do you mean your code or mine on setting the parent? As far as destroy goes, the documentation strongly suggests using Destroy in all cases, even in the editor. The entire point of Managed Code, is that we don’t have to worry about memory Management and object cleanup. Unless you really understand whats going on with Destroy Immediate and have a very specific need, I would just use Destroy and let Unity/C# Garbage Collector handle all that.

Another suggestion, if you are dynamically changing the number of spheres as the SphereCount == sphereCount is suggesting. If they aren’t equal, you could just check if sphereCount was < SphereCount and then loop through enough times to bring the count up… else sphereCount > SphereCount destroy enough spheres to bring it down. That way you aren’t destroying and recreating every sphere is the user just adds 1 more sphere. You could also add a ResetAllSPheres that does what your code does now to specifically destroy all spheres and reset them to new positions if thats what the user wants

Sorry when i say “That is working fine” i mean your code.
About the destroy part so i changed it to this:

foreach (var t in spheres)
        {
            if (Application.isEditor)
            {
                Destroy(t);
            }
            else
            {
                Destroy(t);
            }
        }

So in both cases it will use Destroy is that right ?

And about the suggestion i prefer to destroy the others for example is the user change the number of spheres from 10 to 1 then it will create only one. And if the user change from 1 to 10 it will create new 10 and not keep the old one so it will be 11. If i understood it right. You mean if i type 1 count of sphere and then type 10 so it will keep the 1 before so there will be 11 now right ?

And last thing i tried to do:

I want to make that when i deag the script to the Terrain and when i change the number of spheres each time it will make the spheres to move automatic on the x direction. From it’s current position until the terrain edge and back to it’s original start position in a loop. Each sphere will move to another direction to the edge of the terrain.
And i want that the spheres will move also in the editor and also if i run the game. Not simple.
What i tried so far is adding new function:

private void MoveObjects()
    {
        GameObject terrain = GameObject.Find("Terrain");
        foreach (var obj in FindObjectsOfType(typeof(GameObject)) as GameObject[])
        {
            if(obj.name == "Sphere")
                obj.transform.position += Vector3.forward * Time.deltaTime;
        }
    }

And calling this function from the UpdateSphere() inside the FOR loop in the bottom of the loop but it’s not doing anything. Tried to call it also outside the loop but it’s not working.

I believe this is because when you create your objects your setting obj.tag == “Sphere” and obj.name is a totally different variable. You should check if (obj.tag == “Sphere”) or Set the name to Sphere when you create it