I'm trying to remove a gameObject from the world and a list using:
Destroy(obj);
itemsInInv.Remove(obj.gameObject);
But for some reason, it's reading my list.count as 0 every time I try to reference it threw a click event. If I use a key down and remove a specific item it works fine. But when I try to read my list threw a click event it returns 0 instead of the amount of items actually in the array
Like I can push 'Z' and get the Debug.Log(itemsInInv.Count) will say 15. Then I'll click the item to try and run the same Debug.Log(itemsInInv.Count) and it returns 0.
I've tried using the built in interface on the unity program. I tried using
items.GetComponent<Button>().onClick.AddListener(() => { useItem(); });
Which actually did work 100% fine for a little while then for some reason stopped working and won't work again no matter what I do lol.
I've tried using:
void OnDestroy()
{
manager.GetComponent<UIManager>().Remove(this.gameObject);
Debug.Log("remove" + manager.GetComponent<UIManager>().itemsInInv.Count);
}
public void OnPointerClick(PointerEventData eventData)
{
Destroy(gameObject);
}
Also tried doing the remove in the same function as the click. Same problem. Idk how else this is possible to do it? Even if I create a boolean and have it set to false, then use the button to set it too true, it won't read as true when debugged.
What I'm pretty much asking is, how do I add a list of buttons to the screen. Then delete the SAME button I clicked on from the screen and the list.
Soo this is what i did to get it too work....
i added this script onto my item thats being clicked
public void OnPointerClick(PointerEventData eventData)
{
EventSystem.current.SetSelectedGameObject(gameObject);
Debug.Log("hey"+ EventSystem.current.currentSelectedGameObject);
}
then in my other script im running
items.GetComponent<Button>().onClick.AddListener(() => { useItem(); });
which runs
public void useItem()
{
Invoke("itemUsed", 0.25f);
}
which runs after 0.25
void itemUsed()
{
Debug.Log(EventSystem.current.currentSelectedGameObject);
deleteItem(EventSystem.current.currentSelectedGameObject);
sortItems();
}
I had to do the invoke because it was calling useItem, before the currently selected was being selected
Also my event System was a child of my canvas which made my gameManager unable to access it the way i was
Dont use a list. Use a Dictionary.
public static Dictionary<string, GameObject> dict = new Dictionary<string, GameObject>();
then you can use the gameobjects name.
foreach(GameObject r in FindObjectsOfType(typeof(GameObject)))
{
dict.Add(r.name, r);
}
Then you can do
Destroy(gameObject.name);
dict.Remove(gameObject.name);
Related
I have a component called ContainerDescriptor. When I remove it by right click and then left click remove component, I'd like that it removes other components as well, referenced in the script.
I'm currently using OnDestroy() in ContainerDescriptor.cs.
I'm also using the [ExecuteAlways] attribute so OnDestroy() is called in editor mode as well.
Say I have a reference towards another component called attachedContainer and I want to remove it when destroying containerDescriptor. Currently I'm doing the following.
in ContainerDescriptor.cs :
[ExecuteAlways]
public class ContainerDescriptor : MonoBehaviour
{
// ... some code
public AttachedContainer attachedContainer;
private void OnDestroy()
{
if (Application.isPlaying)
{
Destroy(attachedContainer);
}
else
{
DestroyImmediate(attachedContainer, true);
}
}
// ... some more code
}
It works well in Editor mode but when I press play I get the following error every time :
Destroying object multiple times. Don't use DestroyImmediate on the
same object in OnDisable or OnDestroy.
But Destroy() only works in play mode ! So I don't see which options are left for me.
Ideally, I could remove those components by calling DestroyImmediate() somewhere else, in some method OnRemoveComponent called when selecting remove Component in Editor, but I can't find anything like that in documentation.
If it's relevant, I'm using Unity 2019.3.
It might help to check if the object you're going to destroy is not destroyed already?
else
{
if(attachedContainer!=null)
{
DestroyImmediate(attachedContainer, true);
}
}
Please note that I haven't tested the code above. I hope you get the point.
Using a custom editor does the trick. I declare as references every component I want to delete in the custom editor script. I assign the references in containerDescriptor to the references in containerDescriptorEditor. I then use the onDestroy of the custom editor :
In containerDescriptorEditor.cs :
public class ContainerDescriptorEditor : Editor
{
private ContainerDescriptor containerDescriptor;
private AttachedContainer attachedContainer;
private AttachedContainerGenerator attachedContainerGenerator;
private ContainerInteractive containerInteractive;
private VisibleContainer visibleContainer;
// ... some code
public void OnEnable()
{
containerDescriptor = (ContainerDescriptor)target;
attachedContainer = containerDescriptor.attachedContainer;
containerInteractive = containerDescriptor.containerInteractive;
visibleContainer = containerDescriptor.visibleContainer;
}
// ... some more code
private void OnDestroy()
{
if(containerDescriptor == null)
{
RemoveContainer();
}
}
private void RemoveContainer()
{
DestroyImmediate(attachedContainer, true);
DestroyImmediate(attachedContainerGenerator, true);
DestroyImmediate(containerInteractive, true);
DestroyImmediate(visibleContainer, true);
}
// ...
}
This works for me.
Here's the script I wrote to resolve the issue.
https://github.com/saadasghar96/Unity/blob/master/Utility/MultiComponentCopierCleaner.cs
Go under Earth People Studio > Multi-Component Cleaner
If you leave the 'component' field empty in the Multi-Component Cleaner, it will remove every type of component. Type in "rigidbody" (casings don't matter as long as the component is partially named correctly) and the script will do the rest.
Be sure to back up the object you're cleaning in case things go wrong.
I've set up a very simple Editor script in Unity 2020.2.1f1 that, upon pressing an Inspector button, should change the value of a specified parameter to a value set in the code.
public override void OnInspectorGUI()
{
DrawDefaultInspector();
StateObject s = (StateObject)target;
if (s.objID == 0)
{
if (GUILayout.Button("Generate Object ID"))
{
GenerateID(s);
}
}
}
public void GenerateID(StateObject s)
{
s.objID = DateTimeOffset.Now.ToUnixTimeSeconds();
}
This all works like it's supposed to. I press the button, the correct number appears in the field, and I'm happy. However, once I switch to Play mode, the value resets to the prefab default and remains that way even when I switch Play mode off.
Am I missing some ApplyChange function or something?
(EDIT: This works, but isn't as good as the accepted answer.)
Well, yes, I am in fact missing some sort of ApplyChange function.
I don't know how I missed it, but I was looking for this:
EditorUtility.SetDirty(target);
So, in my script, I would just edit the GenerateID function:
public void GenerateID(StateObject s)
{
s.objID = DateTimeOffset.Now.ToUnixTimeSeconds();
EditorUtility.SetDirty(s);
}
And I am posting it here in case anyone runs into the same issue, that way they hopefully won't have to spend as much time looking for a solution before being reminded that SetDirty is a thing.
In my eyes better than mixing in direct accesses from Editor scripts and then manually mark things dirty rather go through SerializedProperty and let the Inspector handle it all (also the marking dirty and saving changes, handle undo/redo etc)
SerializedProperty id;
private void OnEnable()
{
id = serializedObject.FindProperty("objID");
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// Loads the actual values into the serialized properties
// See https://docs.unity3d.com/ScriptReference/SerializedObject.Update.html
serializedObject.Update();
if (id.intValue == 0)
{
if (GUILayout.Button("Generate Object ID"))
{
id.intValue = DateTimeOffset.Now.ToUnixTimeSeconds();
}
}
// Writes back modified properties into the actual class
// Handles all marking dirty, undo/redo, etc
// See https://docs.unity3d.com/ScriptReference/SerializedObject.ApplyModifiedProperties.html
serializedObject.ApplyModifiedProperties();
}
Possible to reset looping of void Start using button in unity ?
void Start()
{
if (NoAnsweredQuestion == null || NoAnsweredQuestion.Count == 0)
{
NoAnsweredQuestion = question.ToList<Question>();
}
StartCoroutine("CountDownTimer");
SetcurrentQuestion();
}
If your goal is to execute the code the in the Start Method multiple times, you have a bad design, but if you really need to have it in the start Method just destroy the script with a diffrent one and put it on again.
What I would recommend is, that you move this code just in the method that gets executed if the button gets pressed.
Anyway like 'Lestat' said your question is really strange and I can not really understand it aswell.
You can create a function using:
public void YourFunctionName (){
if (NoAnsweredQuestion == null || NoAnsweredQuestion.Count == 0)
NoAnsweredQuestion = question.ToList<Question>();
StartCoroutine("CountDownTimer");
SetcurrentQuestion();
}
If you are using Canvas (which I recommend you do!), you simply add a new button:
And then add the function to the button using the '+' sign, drag your GameObject (with your script attached to it) and select the class and then the function (must be public in order to work).
And I guess this will solve your problem.
I´m trying to do a function that go over my entire data structure and save a GameObject x and y position onto two different lists. My main problem is this function is gonna execute everytime so I need to clear the list everytime the code is executed but, I dont know why, the List.Clear() function is not working properly. When I execute this code:
public void updateChild()
{
listaXBaldas.Clear();
listaYBaldas.Clear();
hueco1.updateChildAux();
hueco2.updateChildAux();
}
private void updateChildAux()
{
if (division == -1) return;
else
{
aux1 = balda.transform.position.y;
aux2 = balda.transform.position.x;
listaYBaldas.Add(aux1);
listaXBaldas.Add(aux2);
Debug.Log(listaXBaldas.Count);
hueco1.updateChildAux();
hueco2.updateChildAux();
}
}
It saves the position as I want, but it doesnt clear the list since the List.Count() is increasing with every execution. I also tried creating a new List instead of clearing it, but I got the same problem.
I have the following code:
void Setup(CityBuilding[] buildings) {
foreach (CityBuilding building in buildings)
{
GameObject buildingDisp = Instantiate(singleGoodsDisplay);
//... Other stuff not relevent happens here
buildingDisp.GetComponent<Button>().onClick.AddListener(() => { ProcessClick(building); });
Debug.Log(building);
}
}
private void ProcessClick(CityBuilding building)
{
Debug.Log(building);
}
The bottom line is, I have a few buttons that are saved as a prefab and instantiated. I'm adding an onClick Listener to it, in the form of a lambda function. I'm passing in a data object. Right now buildings is an array of two. I see that both items are debugged, however, when I actually click on the buttons, I always see the second item is used, never the first. I'm not quite sure what I'm doing wrong. Any thoughts? Thanks!
It seems that the object reference isn't saved, it only uses the latest. What does work is to save a copy and pass it, as follows:
void Setup(CityBuilding[] buildings) {
foreach (CityBuilding building in buildings)
{
CityBuilding temp=building;
GameObject buildingDisp = Instantiate(singleGoodsDisplay);
//... Other stuff not relevent happens here
buildingDisp.GetComponent<Button>().onClick.AddListener(() => { ProcessClick(temp); });
}
}