C# Clear list not working properly - c#

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.

Related

Is there a way to await a flag change in a function?

I've attempted to make a simple step mode for an algorithm I'm running, and here is how it looks like:
public async Task<bool> AStarAlgorithmAsync(PFSquare curr = null)
{
// some algorithm code here
foreach(var square in Sorroundings)
{
if (SteppedMode)
{
await Task.Run(Pause);
}
if (await AStarAlgorithmAsync(square))
{
return true;
}
}
}
In my application, I have a Boolean called SteppedMode that decides if the algorithm should run one iteration per click event.
Pause() looks like this:
private void Pause()
{
while (!ContinueStep) { }
ContinueStep = false;
return;
}
And in another part of my (GUI) application I have an event which sets the boolean ContinueStep to true which in theory should end the while loop and continue the algorithm function. Currently this bit of code locks my GUI thread up and I'm almost certain there is a better way to do this.
I'm trying to get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm. Is there an easier and cleaner way to do this?
(This is a GUI application, not a console application.)
Your property is moonlighting as a method.
It makes no sense to set a property, to then have that property revert back to its original state immediately. As a consumer, I would be majorly confused by that behavior. Think about this code:
var myObj = new MyObject();
myObj.MyBoolean = true;
Console.WriteLine(myObj.MyBoolean); // FALSE!?
It just doesn't make sense.
The only effect you want to trigger by setting this property is to execute some code. That's exactly what methods are supposed to be used for:
public void ContinueStep()
{
Console.WriteLine("I did some work");
}
So instead of this:
myObj.ContinueStep = true;
you should be doing this:
myObject.ContinueStep();
This doesn't lock up your UI thread, while also being a lot more sensical to your consumer. The method suggests that some action will be taken (which may or may not lead to state changes in the object - that's a contextual expectation).
Infinite recursion
As an aside; based on your code, AStarAlgorithmAsync is a recursive function, and seemingly infinitely so. There doesn't seem to be an ending condition.
Every recursive level will interate over the first surrounding and then trigger the next level, which again will interate over the first surrounding and then trigger the next level, which again ...
That can't be right, but it's unclear to me how to fix it as the bigger picture is not explained in your question
A simple implementation
What I'm trying to do is get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm, is there an easier and cleaner way to do this?
A simple example of such a thing:
private int _index = 0;
private List<object> _myList = ...; // assume this list contains some elements
public void ProcessNextObject()
{
if(_index < _myList.Length)
{
Process(_myList[_index]);
_index++;
}
}
private void Process(object o)
{
Console.WriteLine("Processing this object!");
}
You can then hook up your click event to call ProcessNextObject().
Note that in this example, the list is processed once and cannot be processed again. By manipulating the index value, you can change that behavior as you like.

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

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.

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

Why is my Button not correctly registering the output?

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

Have MessageBox appear once (code inside timer)

UPDATE: I've managed to fix my problem. Using the code below, I moved my MessageBox AFTER my XML saving and changed the Timer from 100ms to 400ms. I now have 1 box appear, thank god. Although If anyone has a short cut to updating a single value (ActReminded) in the List array(ActListTask), that'd be great to know.
I'm having a little issue with displaying the MessageBox. Show inside a timer without it spamming me. Here's the part of the code I've been working with:
public class ActiveTasks
{
//Properties here
}
public List<ActiveTasks> ActTaskList = new List<ActiveTasks>();
for (int i = 0; i < ListActive.Items.Count; i++)
{
if (DTime.Date == newDateTime.Date)
{
if (newDateTimeLeft.CompareTo(TimeSpan.Zero) <= 0 && ActTaskList[i].ActReminded != "true")
{
MessageBox.Show("!!!!");
ActTaskList.Add(new ActiveTasks()
{
ActTitle = ActTaskList[i].ActTitle,
ActDesc = ActTaskList[i].ActDesc,
ActDate = ActTaskList[i].ActDate,
ActTime = ActTaskList[i].ActTime,
ActStatus = ActTaskList[i].ActStatus,
ActReminded = "true",
ActRepeat = ActTaskList[i].ActRepeat
});
ListActive.Items.RemoveAt(i);
ActTaskList.RemoveAt(i);
XDocument XmlActTasks = GenerateActiveListToXML(ActTaskList);
}
}
}
I actually decided I may want to hold onto the reminder status, whether it has been shown or not as I wouldn't want a repeated reminder every time the program is opened. Since I don't know of a way to update an individual part of ActTaskList I just re-added it, and then deleted the original. This code manages to recognise that if it happens, it will change the reminder status from false, to true; after I've Ok'ed all the spam. So it will stop the MessageBox once I've managed to closed all the Messageboxes. However, it doesn't stop the spam. Would it be anything to do with the fact I've set the timer to 100ms? Or could their be an alternative way to make the messagebox appear without it being inside the timer?
The odds of the current time lining up exactly to the second what is happening in your loop is small. Why not treat newDateTime as a cut off point and just set a flag?
//Declare this outside of the loop
bool hasDisplayed = false;
//Inside the timer event handler
if (!hasDisplayed && DateTime.Now >= newDateTime)
{
hasDisplayed = true;
MessageBox.Show("!!!!!!!!!!!!!");
}
Can you do something like this?
Action message = () => MessageBox.Show("!!!!!!!!!!!!!"));
object lockOb = new object();
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
lock(lockOb)
if(null != message)
{
message();
message = null;
}
}
You say you've already tried a boolean indicating the message has already been shown, I'm assuming because the code probably looked like it did below.
void TimerLoop()
{
bool msgAlreadyShown;
if(!msgAlreadyShown)
{
MessageBox.Show("!!!!!!!");
}
// Other work in your timer function
}
The problem with that code is that the bool will be set to false each time the function is called by the timer. You haven't posted much code, but you've at least stated what you're trying to accomplish, a timer that checks if a reminder should be presented to the user.
I'm about to make some wild guesses about how you've put together your software, there's a good chance it's way off, but I hope it might point you in the right direction. You could have some sort of reminder class like this:
public class Reminder
{
string Message { get; set;}
DateTime Alarm { get; set; }
bool IsDismissed { get; set; }
}
I'm assuming you might want to have multiple reminders that can be checked for in the timer loop, so your timer loop could look something like:
private List<Reminder> _activeReminders; // A list of reminders
void TimerLoop(object s, ElapsedEventArgs e)
{
lock(_activeReminders)
{
var now = DateTime.Now;
foreach(var reminder in _activeReminders)
{
// only run this code if the time has passed and it hasn't already
// been shown
if(now.CompareTo(reminder.Alarm) >= 0 && !reminder.IsDismissed)
{
MessageBox.Show(reminder.Message);
reminder.IsDismissed = true;
}
}
}
}
This is a pretty naive implementation, since you probably don't want to hold onto the reminders for forever and the reminders are never removed from the _activeReminders list, but you essentially just need to add some sort of state to determine if the reminder has already been shown.
Of course, this isn't a complete example either, since I never new up the _activeReminders field or add anything to it, but I think this might help get the idea of what you need to do across. Also, you might not care about multiple reminders, and your timer code could look nothing like this. The main idea was to show you how you can keep track of the state of a reminder, and tailor it to your own code. The above was just an example.
Also, I haven't actually tested it, so treat it more like pseudocode than anything else. However, the logic is sound, and should it should only cause the message box to appear once.

Categories