How to reset looping of Start function using button in Unity 3D? - c#

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.

Related

In Unity, how can I remove multiple components when removing one in editor?

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.

Is it possible to return a variable type to the script that called a coroutine?

Alright, I have a Dialogue system in place in my game. It is pretty simple in its design. Each dialogue contains a list of nodes which would be what the NPC would say and for each node there is also a list of options that the player can choose from either to end the conversation or continue on. The dialogue part works flawlessly. What I am trying to do is combine it with a questing system. Before I get too far into my questing system I need to figure out a way to connect my dialogue system to my questing system or NPC (preferably my NPC). The way my dialogue system is set up is with a Singleton pattern and each NPC would just call a method on it that starts a dialogue with a player based on its local dialogue variable.
I've been sitting here thinking about how I can pass a value from my dialogue manager to my NPC but, considering that my run method is a Coroutine I can't figure out how to return that value after I exit the coroutine if I need to. I feel like this should be possible but, I really can't think of a way to do this. Any help would be appreciated.
An ideal situation is to get the RunDialogue method to return a variable type(bool?), but only after EndDialogue has been called from within the run method. If it returns true then assign the quest otherwise do nothing.
From DialogueManager:
public void RunDialogue(Dialogue dia)
{
StartCoroutine(run(dia));
}
IEnumerator run(Dialogue dia)
{
DialoguePanel.SetActive(true);
//start the convo
int node_id = 0;
//if the node is equal to -1 end the conversation
while (node_id != -1)
{
//display the current node
DisplayNode(dia.Nodes[node_id]);
//reset the selected option
selected_option = -2;
//wait here until a selection is made by button click
while (selected_option == -2)
{
yield return new WaitForSeconds(0.25f);
}
//get the new id since it has changed
node_id = selected_option;
}
//the user exited the conversation
EndDialogue();
}
From NPC:
public override void Interact()
{
DialogueManager.Instance.RunDialogue(dialogue);
}
There is a way to make a coroutine return a value. Requires some nesting, you can have a look at this video if you want to go this way: Unite 2013 - Extending Coroutines # 20m38s on "adding return values"
Otherwise, you can pass a callback to the coroutine. This will probably do it for you.
Add some function at a point where it makes sense (e.g. the NPC):
public void OnGiveQuest()
{
// Add the quest
}
Add it to the dialogue call:
public override void Interact()
{
DialogueManager.Instance.RunDialogue(dialogue, OnGiveQuest);
}
Then change your RunDialogue and run to take a callback:
public void RunDialogue(Dialogue dia, System.Action callback = null)
{
StartCoroutine(run(dia, callback));
}
Now, for the coroutine, you either pass the callback further to EndDialogue or handle it in here too after the end call.
IEnumerator run(Dialogue dia, System.Action callback = null)
{
...
//the user exited the conversation
EndDialogue();
if(callback != null)
callback();
}
Now, I made it the way that you would only add the callback if you want to start a quest. Otherwise you just leave it out (default value of null).

C# Clear list not working properly

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.

Having issues with State changes in a Text Adventure in Unity 4.6

Basically, I'm trying to get Unity to change the game's state when the player clicks a button. For whatever reason though, it isn't reading the second key press that actually changes the state. The code looks like this:
void state_Cell () {
if (CelP == 0) {
text.text = "Some text";
if (Input.GetKeyDown (KeyCode.C)) { //This works
++CelP;
text.text = "Some more text.";
if (Input.GetKeyDown (KeyCode.B)) { //This doesn't work
++CelP;
MyState = States.Bed;
}
}
}
}
If it will help, this is the code for the Bed state that I can't access in the game:
void state_Bed () {
if (BedP == 0) {
text.text = "Still more text.";
if (Input.GetKeyDown (KeyCode.D)) {
MyState = States.Delivery;
BedP++
}
}
}
The most confusing part about this for me is that Unity's compiler isn't giving me any errors. I encountered this problem with a different state before, and what I did was move the "++Variable" to a different location, but that didn't work for this statement.
Aside from that, I've also tried to removing the "if statement" in the Bed state, but that didn't work either.
Another thing that I attempted was changing "B" in the "KeyCode" to different keys, but that didn't work at all.
I also tried to include a "print();" statement at the very beginning of the Bed state, but it didn't trigger either.
The last thing that I've tried was putting a "print();" statement after the "(KeyCode.B))", but that didn't work either. So from that I know that the problem is that Unity isn't reading or responding to the key press.
Any help would be appreciated. And you need anymore information than what I've supplied, tell me and I'll give it to you.
Thanks in advance.
You should create two states for Cell, first one listens to C and changes the state to second, second one listens to B and changes the state to Bed.
The reason is that state machines do not follow after where they left in the middle of the code inside one state. You need to be more specific when you are defining your states.
I assume that state_Cell is being called from an Update or a similar Coroutine which is being called every x frame. so local variables inside state_Cell is reset every time it is called. so machine does not know if user previously pressed C or did not press anything.
When user presses a key then technically the state has been changed. when user presses another key then again state changes.
Edit
In case you don't want to add extra states this is the simplified version of your state_Cell
You have to be careful when to set the extra Boolean to false.
bool c_pressed = false;
void state_Cell () {
if (c_pressed || Input.GetKeyDown (KeyCode.C)) {
c_pressed = true;
if (Input.GetKeyDown (KeyCode.B)) {
c_pressed = false;
MyState = States.Bed;
}
}
else
c_pressed = false;
}
}

Unity Click button

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);

Categories