How can i interact between 3 scripts when two of them are UI ?

The first script is just for creating new GameObjects:

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

//[ExecuteInEditMode]
public class InstantiateObjects : MonoBehaviour
{
    public GameObject prefab;
    public Terrain terrain;
    public float yOffset = 0.5f;
    public int objectsToInstantiate;
    public bool parent = true;
    public bool randomScale = false;
    public float setRandScaleXMin, setRandScaleXMax;
    public float setTandScaleYMin, setTandScaleYMax;
    public float setTandScaleZMin, setRandScaleZMax;
    public bool generateNew;

    private float terrainWidth;
    private float terrainLength;
    private float xTerrainPos;
    private float zTerrainPos;
    private int numberOfObjectsToCreate;
    private GameObject objInstance;
    private GameObject[] createdObjects;
    private string objname;

    public void Start()
    {
        //Get terrain size
        terrainWidth = terrain.terrainData.size.x;
        terrainLength = terrain.terrainData.size.z;

        //Get terrain position
        xTerrainPos = terrain.transform.position.x;
        zTerrainPos = terrain.transform.position.z;

        numberOfObjectsToCreate = objectsToInstantiate;

        objname = prefab.name;
        MyCustomEditor.TagsAndLayers.AddTag(objname);

        generateNew = false;
        generateObjectOnTerrain();
    }

    public void Update()
    {
       
    }

    private void DestroyObjects(GameObject[] objects)
    {
        if (objects != null && objects.Length > 0)
        {
            for (int i = 0; i < objects.Length; i++)
            {
                DestroyImmediate(objects[i]);
            }
            objects = new GameObject[0];
        }
    }

    public void generateObjectOnTerrain()
    {
        for (int i = 0; i < objectsToInstantiate; i++)
        {
            //Generate random x,z,y position on the terrain
            float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
            float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);

            float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));

            //Generate random x,y,z scale on the terrain
            float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
            float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
            float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);

            //Apply Offset if needed
            yVal = yVal + yOffset;

            //Generate the Prefab on the generated position        
            objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);

            if (randomScale == true)
                objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);

            if (parent)
                objInstance.transform.parent = this.transform;

            objInstance.tag = objname;
        }

        createdObjects = GameObject.FindGameObjectsWithTag(objname);
    }
}

Then i have two other scripts.
The first one is attached to a UI button that is child of canvas in the hierarchy.

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

public class GenerateObjectsButton : MonoBehaviour
{
    public static bool buttonClicked;

    public void OnButton()
    {
        buttonClicked = true;
    }
}

The third script is attached to toggle UI as child under canvas in the hierarchy:

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

public class ToggleValueChanged : MonoBehaviour {

    private Toggle toggle;

    Toggle.onValueChanged.AddListener((value) =>
    {
        MyListener(value);
});//Do this in Start() for example

public void MyListener(bool value)
{
    if (value)
    {
        //do the stuff when the toggle is on
    }
    else
    {
        //do the stuff when the toggle is off
    }
}

I have some problems:

  1. In the last third script in the ToggleValueChanged i’m getting many errors on this part:
Toggle.onValueChanged.AddListener((value) =>
    {
        MyListener(value);
});//Do this in Start() for example

public void MyListener(bool value)
  1. What i want to do is when i click the button it will call the function generateObjectOnTerrain() from inside the Update function in the InstantiateObjects button. And according to if the toggle is checked or not decide if to destroy first the old gameobjects or not. If the toggle is checked(true) destroy if not just create more new.

First, in general, post the errors you get.

In this case, you’re having a problem because code can’t be executed in the main body of a class; it has to be in a function definition, like inside Start() for example. Which is pretty much what the comment tells you to do…

1 Like

Great working.

And about integrate between the 3 scripts ? Can i use in the button script and the toggle script with public static bool variables and then use them in the first script in the Update function ?

I think the variable was also wrong in the code snippet, as you were using the class name instead of the variable. :slight_smile:

I think if you put those 2 (button/toggle) scripts into 1 script, then when you change the toggle, you could have a variable there that is set (on/off). When the button is clicked and the method runs, it can check that variable. Then it can decide to call 1 or 2 methods (from your first script) :slight_smile: … Get a reference to the object terrain creating script in the (new) combined script, for example (used to call the methods).

Yes i changed the variable from class name to variable Toogle to toggle :slight_smile:

About the rest what i did is i put both scripts in one:

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

public class GenerateObjectsButton : MonoBehaviour
{
    private bool toggleOnOf;
    public Toggle toggle;

    private void Start()
    {
        toggle.onValueChanged.AddListener((value) =>
            {
                MyListener(value);
            });
    }

    public void MyListener(bool value)
    {
        if (value)
        {
            //do the stuff when the toggle is on
            toggleOnOf = true;
        }
        else
        {
            //do the stuff when the toggle is off
            toggleOnOf = false;
        }
    }

    public void OnButton()
    {
       
    }
}

But what should i do now in the OnButton function ?
And then how should i call it in the Update function in the first script ?

lol Stop saying Update() function hahaha. sorry, couldn’t help myself… 3rd time I said that to your posts :wink: But bear with me a moment here…
Make a variable of the type of your Script that does the building/placement.
Then, link that gameobject/script to the new variable.
Then, in the OnButton() , check your variable and call ā€˜create’ and/or ā€˜destroy’ (for placing stuff… you know their real names) :slight_smile:

This is what i did in the GenerateObjectsButton script:

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

public class GenerateObjectsButton : MonoBehaviour
{
    private InstantiateObjects instantiateobjects;
    private bool toggleOnOf;
    public Toggle toggle;

    private void Start()
    {
        instantiateobjects = new InstantiateObjects();
        toggle.onValueChanged.AddListener((value) =>
            {
                MyListener(value);
            });
    }

    public void MyListener(bool value)
    {
        if (value)
        {
            //do the stuff when the toggle is on
            toggleOnOf = true;
        }
        else
        {
            //do the stuff when the toggle is off
            toggleOnOf = false;
        }
    }

    public void OnButton()
    {
        if (toggleOnOf == false)
        {
            instantiateobjects.generateObjectOnTerrain();
        }
        else
        {
            instantiateobjects.DestroyObjects();
            instantiateobjects.generateObjectOnTerrain();
        }
    }
}

Then in the InstantiateObjects not changed too much:

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

//[ExecuteInEditMode]
public class InstantiateObjects : MonoBehaviour
{
    public GameObject prefab;
    public Terrain terrain;
    public float yOffset = 0.5f;
    public int objectsToInstantiate;
    public bool parent = true;
    public bool randomScale = false;
    public float setRandScaleXMin, setRandScaleXMax;
    public float setTandScaleYMin, setTandScaleYMax;
    public float setTandScaleZMin, setRandScaleZMax;
    public bool generateNew;

    private float terrainWidth;
    private float terrainLength;
    private float xTerrainPos;
    private float zTerrainPos;
    private int numberOfObjectsToCreate;
    private GameObject objInstance;
    private GameObject[] createdObjects;
    private string objname;

    public void Start()
    {
        //Get terrain size
        terrainWidth = terrain.terrainData.size.x;
        terrainLength = terrain.terrainData.size.z;

        //Get terrain position
        xTerrainPos = terrain.transform.position.x;
        zTerrainPos = terrain.transform.position.z;

        numberOfObjectsToCreate = objectsToInstantiate;

        objname = prefab.name;
        MyCustomEditor.TagsAndLayers.AddTag(objname);

        generateNew = false;
        generateObjectOnTerrain();
    }

    public void Update()
    {
       
    }

    public void DestroyObjects()
    {
        if (createdObjects != null && createdObjects.Length > 0)
        {
            for (int i = 0; i < createdObjects.Length; i++)
            {
                DestroyImmediate(createdObjects[i]);
            }
            createdObjects = new GameObject[0];
        }
    }

    public void generateObjectOnTerrain()
    {
        for (int i = 0; i < objectsToInstantiate; i++)
        {
            //Generate random x,z,y position on the terrain
            float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
            float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);

            float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));

            //Generate random x,y,z scale on the terrain
            float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
            float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
            float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);

            //Apply Offset if needed
            yVal = yVal + yOffset;

            //Generate the Prefab on the generated position        
            objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);

            if (randomScale == true)
                objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);

            if (parent)
                objInstance.transform.parent = this.transform;

            objInstance.tag = objname;
        }

        createdObjects = GameObject.FindGameObjectsWithTag(objname);
    }
}

But in the script InstantiateObjects i’m getting null exception on the line:

createdObjects = GameObject.FindGameObjectsWithTag(objname);

At the first time when running the game it’s fine but then each time i click the button no matter what state the toggle is i’m getting null exception on that line and using a break point i see that on the line:

for (int i = 0; i < objectsToInstantiate; i++)

The variable objectsToInstantiate value is 0.
When running the game the value is 5 but then when clicking the button the value is 0.

Also when i change the toggle state it’s not getting to the DestroyObjects only if i change the state few times and click the button.

Okay, state takes a few clicks … that is weird, but it works sometimes. Is the toggle properly setting but just not calling the right function? Could this be a mixup with what the variable is set to first/compared to which toggle is on at load time?

how can the variable be 0 for objectsToInstantiate? No idea. I don’t see any code that changes the value for that.

how can you get a null reference , I don’t understand that either.

If this still isn’t working, I wouldn’t mind looking at the project, if you the file isn’t too big and/or you could recreate a version with just the relevant stuff, and post a dropbox download link. I’d look it over for you.

Here is a link for a new project i created with the only needed things.
Same result with the null exception when clicking the ui button.

https://1drv.ms/u/s!AtV2OUzEcRzrlnU98sI15AE98Elz

It’s my own one drive.
The file name is New Unity Project 3.rar

2MB file size.

Tell me if you have a problem to get the file. Thank you.

Checking it out now.

Okay you have a few issues here.
One is that there are 2 copies of the instantiate objects script. 1 on the buildings, and one on the teleportation things.
So, which one of those do you want to use ?

Next up, you had (in the copy you sent me) an older script on the toggle game object. it didn’t have the newer ā€œGenerate objects buttonā€ script that you recently wrote.

One more thing. I changed the code (this line):

 [SerializeField]
    private InstantiateObjects instantiateobjects;

then I dragged one of the game objects with that script into the slot. I used the buildings one just to test.

The result(s) I’m seeing are: No new bugs, and the buildings number (of objects created) goes up by 5 every time I click the button. If the toggle is also on, it removes the old ones so I have only 5 every time. If it’s off, it keeps adding 5 more buildings.
The reason you had to click yours a few times to make it work, was that your default was set to ā€œonā€ but the bool variable in the code was set to ā€˜false’ by default. I turned off the toggle in the scene, and now it always lines up on the first try.
The other option is to set the bool to true at the beginning and leave the toggle on to start.

Hope that helps. I can’t really ā€œseeā€ the scene lol but I can see the hierarchy enough to see that some stuff should/does appear to work :slight_smile:

First thank you.

The reason the script instantiate objects is on two GameObjects is i want to create two set of gameobjects.
One using the buildings with a cube the second using a prefab to create teleportation booths.
The idea in my logic was to create one instantiate objects script so i can use it on any gameobject to create any objects.
So on one gameobject to create the buildings one second one to create cylinders and another one to create spheres.
And if on one gameobject in the inspector i change the properties it will take effect for this only.

So for example on one gameobject it will create 5 cubes(buildings) and if on other gameobject i will set to create 20 spheres i will have on the terrain 5 cubes and 20 spheres.

Yep, I mean I knew/figure that was the idea… Since I only had 1 button and 1 toggle, I just had to pick ā€œoneā€ of the gameobjects (with the script) – for testing :slight_smile:

Did you try the changes/suggestions that I wrote? Can you get your copy to work?

Can you please rar/zip the project after changes you did and send me a link ?

No could not make it on the new project either the old one.
If you can send me a link for the project after the changes i will take a look.

Well, I have a newer version of Unity than you, so I don’t think you could open a proper package.
It’s only like 2-3 small changes.
Just try to read my post from above and let me know which parts you didn’t understand/didn’t work… and I’ll try to explain it better.

  1. Fix the script on the toggle so it’s using the (newer) script.

  2. change the private InstantiateObjects instantiateobjects = new InstantiateObjects();
    to : [SerializeField] InstantiateObjects instantiateobjects; // drag the proper game object to this in the inspector

  3. change the toggle bool variable to be true to start and leave the toggle ā€œisOnā€ in the inspector
    or leave the bool to false (default) and uncheck the box ā€œisOnā€ for the toggle in the inspector

I think that’s all I did. 3 small things :slight_smile:

Working :slight_smile: Thank you. And sorry for the mess.

Hey, np :slight_smile: Glad ya got it working.

1 Like