- Home /
Why when using a ui button script with ui toggle to interact with another script it's not working good ?
I have in the Hierarchy a Canvas and as a child i have a ui button and ui toggle. On the button i attached a new 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();
}
}
}
And this is the InstantiateObjects script:
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);
}
}
What it does now when running the game is creating new gameobjects. Now what i want to do is:
When i click the button depending on what state the toggle is create new gameobjects. If the toggle is true then first destroy all created gameobjects and create new ones.
If the toggle is set to false and i click the button then just create moew new gameobjects don't destroy the others.
But i can't make the GenerateObjectsButton script to work good with the InstantiateObjects script.
First each time i click the button i'm getting null exception in the script InstantiateObjects on the line:
createdObjects = GameObject.FindGameObjectsWithTag(objname);
createdObjects is null. And another problem is the logic in the GenerateObjectsButton is not working good i need to change the toggle to false true twice to get to the DestroyObjects method.
But the main goal is using the button according to the toggle state to create new gameobjects with/without destroying the old ones.
Answer by bobisgod234 · May 25, 2017 at 07:13 AM
You are creating a monobehaviour with the new keyword.
instantiateobjects = new InstantiateObjects();
This is not allowed, and in fact should be raising some error along the lines of "You are trying to create a MonoBehaviour using the 'new' keyword".
You need to attach it to a gameobject using AddComponent(). Something like this, which will attach it to the same object as GenerateObjectsButton:
instantiateobjects = gameObject.AddComponent<InstantiateObjects>();
Next, it seems like that script was designed to run in the editor, but is now being used in play mode. Change DestroyImmediate to just a regular Destroy.
I notice you are using some custom code to create tags (from the looks of it, from here ). I do not know exactly how this works, but it looks like it was intended for use in the editor, and not in play mode, and as such may not actually work in play mode. Just manually add the value of objname as a tag to TagManager in the editor (Edit->Project Settings->Tags and Layers), and remove the "MyCustomEditor.TagsAndLayers.AddTag(objname);" line.
Try these changes and see if the problem is fixed.
Also, There is no purpose to attaching a listener to a Toggle to only store the on/off value, when you can access that directly with "Toggle.IsOn". We can simplify the first script down to
public class GenerateObjectsButton : MonoBehaviour
{
private InstantiateObjects instantiateobjects;
public Toggle toggle;
private void Start()
{
instantiateobjects = gameObject.AddComponent<InstantiateObjects>();
}
public void OnButton()
{
if (!toggle.IsOn)
{
instantiateobjects.generateObjectOnTerrain();
}
else
{
instantiateobjects.DestroyObjects();
instantiateobjects.generateObjectOnTerrain();
}
}
}