Search Unity

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

Discussion in 'Scripting' started by Chocolade, May 24, 2017.

  1. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    The first script is just for creating new GameObjects:

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. //[ExecuteInEditMode]
    7. public class InstantiateObjects : MonoBehaviour
    8. {
    9.     public GameObject prefab;
    10.     public Terrain terrain;
    11.     public float yOffset = 0.5f;
    12.     public int objectsToInstantiate;
    13.     public bool parent = true;
    14.     public bool randomScale = false;
    15.     public float setRandScaleXMin, setRandScaleXMax;
    16.     public float setTandScaleYMin, setTandScaleYMax;
    17.     public float setTandScaleZMin, setRandScaleZMax;
    18.     public bool generateNew;
    19.  
    20.     private float terrainWidth;
    21.     private float terrainLength;
    22.     private float xTerrainPos;
    23.     private float zTerrainPos;
    24.     private int numberOfObjectsToCreate;
    25.     private GameObject objInstance;
    26.     private GameObject[] createdObjects;
    27.     private string objname;
    28.  
    29.     public void Start()
    30.     {
    31.         //Get terrain size
    32.         terrainWidth = terrain.terrainData.size.x;
    33.         terrainLength = terrain.terrainData.size.z;
    34.  
    35.         //Get terrain position
    36.         xTerrainPos = terrain.transform.position.x;
    37.         zTerrainPos = terrain.transform.position.z;
    38.  
    39.         numberOfObjectsToCreate = objectsToInstantiate;
    40.  
    41.         objname = prefab.name;
    42.         MyCustomEditor.TagsAndLayers.AddTag(objname);
    43.  
    44.         generateNew = false;
    45.         generateObjectOnTerrain();
    46.     }
    47.  
    48.     public void Update()
    49.     {
    50.        
    51.     }
    52.  
    53.     private void DestroyObjects(GameObject[] objects)
    54.     {
    55.         if (objects != null && objects.Length > 0)
    56.         {
    57.             for (int i = 0; i < objects.Length; i++)
    58.             {
    59.                 DestroyImmediate(objects[i]);
    60.             }
    61.             objects = new GameObject[0];
    62.         }
    63.     }
    64.  
    65.     public void generateObjectOnTerrain()
    66.     {
    67.         for (int i = 0; i < objectsToInstantiate; i++)
    68.         {
    69.             //Generate random x,z,y position on the terrain
    70.             float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
    71.             float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
    72.  
    73.             float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
    74.  
    75.             //Generate random x,y,z scale on the terrain
    76.             float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
    77.             float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
    78.             float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);
    79.  
    80.             //Apply Offset if needed
    81.             yVal = yVal + yOffset;
    82.  
    83.             //Generate the Prefab on the generated position        
    84.             objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
    85.  
    86.             if (randomScale == true)
    87.                 objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);
    88.  
    89.             if (parent)
    90.                 objInstance.transform.parent = this.transform;
    91.  
    92.             objInstance.tag = objname;
    93.         }
    94.  
    95.         createdObjects = GameObject.FindGameObjectsWithTag(objname);
    96.     }
    97. }
    98.  
    Then i have two other scripts.
    The first one is attached to a UI button that is child of canvas in the hierarchy.

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class GenerateObjectsButton : MonoBehaviour
    7. {
    8.     public static bool buttonClicked;
    9.  
    10.     public void OnButton()
    11.     {
    12.         buttonClicked = true;
    13.     }
    14. }
    15.  
    The third script is attached to toggle UI as child under canvas in the hierarchy:

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class ToggleValueChanged : MonoBehaviour {
    8.  
    9.     private Toggle toggle;
    10.  
    11.     Toggle.onValueChanged.AddListener((value) =>
    12.     {
    13.         MyListener(value);
    14. });//Do this in Start() for example
    15.  
    16. public void MyListener(bool value)
    17. {
    18.     if (value)
    19.     {
    20.         //do the stuff when the toggle is on
    21.     }
    22.     else
    23.     {
    24.         //do the stuff when the toggle is off
    25.     }
    26. }
    27.  
    I have some problems:

    1. In the last third script in the ToggleValueChanged i'm getting many errors on this part:

    Code (csharp):
    1.  
    2. Toggle.onValueChanged.AddListener((value) =>
    3.     {
    4.         MyListener(value);
    5. });//Do this in Start() for example
    6.  
    7. public void MyListener(bool value)
    8.  
    2. 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.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    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...
     
    Chocolade likes this.
  3. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    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 ?
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I think the variable was also wrong in the code snippet, as you were using the class name instead of the variable. :)

    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) :) .. Get a reference to the object terrain creating script in the (new) combined script, for example (used to call the methods).
     
  5. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    Yes i changed the variable from class name to variable Toogle to toggle :)

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

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class GenerateObjectsButton : MonoBehaviour
    8. {
    9.     private bool toggleOnOf;
    10.     public Toggle toggle;
    11.  
    12.     private void Start()
    13.     {
    14.         toggle.onValueChanged.AddListener((value) =>
    15.             {
    16.                 MyListener(value);
    17.             });
    18.     }
    19.  
    20.     public void MyListener(bool value)
    21.     {
    22.         if (value)
    23.         {
    24.             //do the stuff when the toggle is on
    25.             toggleOnOf = true;
    26.         }
    27.         else
    28.         {
    29.             //do the stuff when the toggle is off
    30.             toggleOnOf = false;
    31.         }
    32.     }
    33.  
    34.     public void OnButton()
    35.     {
    36.        
    37.     }
    38. }
    39.  
    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 ?
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    lol Stop saying Update() function hahaha. sorry, couldn't help myself.. 3rd time I said that to your posts ;) 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) :)
     
  7. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    This is what i did in the GenerateObjectsButton script:

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class GenerateObjectsButton : MonoBehaviour
    8. {
    9.     private InstantiateObjects instantiateobjects;
    10.     private bool toggleOnOf;
    11.     public Toggle toggle;
    12.  
    13.     private void Start()
    14.     {
    15.         instantiateobjects = new InstantiateObjects();
    16.         toggle.onValueChanged.AddListener((value) =>
    17.             {
    18.                 MyListener(value);
    19.             });
    20.     }
    21.  
    22.     public void MyListener(bool value)
    23.     {
    24.         if (value)
    25.         {
    26.             //do the stuff when the toggle is on
    27.             toggleOnOf = true;
    28.         }
    29.         else
    30.         {
    31.             //do the stuff when the toggle is off
    32.             toggleOnOf = false;
    33.         }
    34.     }
    35.  
    36.     public void OnButton()
    37.     {
    38.         if (toggleOnOf == false)
    39.         {
    40.             instantiateobjects.generateObjectOnTerrain();
    41.         }
    42.         else
    43.         {
    44.             instantiateobjects.DestroyObjects();
    45.             instantiateobjects.generateObjectOnTerrain();
    46.         }
    47.     }
    48. }
    49.  
    Then in the InstantiateObjects not changed too much:

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. //[ExecuteInEditMode]
    7. public class InstantiateObjects : MonoBehaviour
    8. {
    9.     public GameObject prefab;
    10.     public Terrain terrain;
    11.     public float yOffset = 0.5f;
    12.     public int objectsToInstantiate;
    13.     public bool parent = true;
    14.     public bool randomScale = false;
    15.     public float setRandScaleXMin, setRandScaleXMax;
    16.     public float setTandScaleYMin, setTandScaleYMax;
    17.     public float setTandScaleZMin, setRandScaleZMax;
    18.     public bool generateNew;
    19.  
    20.     private float terrainWidth;
    21.     private float terrainLength;
    22.     private float xTerrainPos;
    23.     private float zTerrainPos;
    24.     private int numberOfObjectsToCreate;
    25.     private GameObject objInstance;
    26.     private GameObject[] createdObjects;
    27.     private string objname;
    28.  
    29.     public void Start()
    30.     {
    31.         //Get terrain size
    32.         terrainWidth = terrain.terrainData.size.x;
    33.         terrainLength = terrain.terrainData.size.z;
    34.  
    35.         //Get terrain position
    36.         xTerrainPos = terrain.transform.position.x;
    37.         zTerrainPos = terrain.transform.position.z;
    38.  
    39.         numberOfObjectsToCreate = objectsToInstantiate;
    40.  
    41.         objname = prefab.name;
    42.         MyCustomEditor.TagsAndLayers.AddTag(objname);
    43.  
    44.         generateNew = false;
    45.         generateObjectOnTerrain();
    46.     }
    47.  
    48.     public void Update()
    49.     {
    50.        
    51.     }
    52.  
    53.     public void DestroyObjects()
    54.     {
    55.         if (createdObjects != null && createdObjects.Length > 0)
    56.         {
    57.             for (int i = 0; i < createdObjects.Length; i++)
    58.             {
    59.                 DestroyImmediate(createdObjects[i]);
    60.             }
    61.             createdObjects = new GameObject[0];
    62.         }
    63.     }
    64.  
    65.     public void generateObjectOnTerrain()
    66.     {
    67.         for (int i = 0; i < objectsToInstantiate; i++)
    68.         {
    69.             //Generate random x,z,y position on the terrain
    70.             float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
    71.             float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
    72.  
    73.             float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
    74.  
    75.             //Generate random x,y,z scale on the terrain
    76.             float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
    77.             float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
    78.             float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);
    79.  
    80.             //Apply Offset if needed
    81.             yVal = yVal + yOffset;
    82.  
    83.             //Generate the Prefab on the generated position        
    84.             objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
    85.  
    86.             if (randomScale == true)
    87.                 objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);
    88.  
    89.             if (parent)
    90.                 objInstance.transform.parent = this.transform;
    91.  
    92.             objInstance.tag = objname;
    93.         }
    94.  
    95.         createdObjects = GameObject.FindGameObjectsWithTag(objname);
    96.     }
    97. }
    98.  
    But in the script InstantiateObjects i'm getting null exception on the line:

    Code (csharp):
    1.  
    2. createdObjects = GameObject.FindGameObjectsWithTag(objname);
    3.  
    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:

    Code (csharp):
    1.  
    2. for (int i = 0; i < objectsToInstantiate; i++)
    3.  
    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.
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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.
     
  9. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933

    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.
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Checking it out now.
     
  11. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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):
    Code (csharp):
    1.  
    2.  [SerializeField]
    3.     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 :)
     
  12. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    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.
     
  13. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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 :)

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

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    Can you please rar/zip the project after changes you did and send me a link ?
     
  15. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    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.
     
  16. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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.
     
  17. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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 :)
     
  18. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    933
    Working :) Thank you. And sorry for the mess.
     
  19. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Hey, np :) Glad ya got it working.
     
    Chocolade likes this.