How do i stop the variable from resetting - c#

In my code I have a method with the two parameters.
One parameter takes in an int value and the other an array.
e.g
public void NextSong(int i, TagLib.File[] queue)
{
i++;
SONG_URL = queue[i].Name;
Stop();
Play();
}
My problem here is every time this variable is called like so:
NextSong(0, SongQueue);
It reverts back to the amount placed in the parameter field. How do I stop this?

Two ways:
public int NextSong(int i, TagLib.File[] queue)
{
i++;
SONG_URL = queue[i].Name;
Stop();
Play();
return i;
}
int i = 0;
i= NextSong(i, SongQueue);
Here we are passing a variable of i to the method, during the method we increment that variable and then pass it back via the return. We now have reference to that variable.
OR
public void NextSong(TagLib.File[] queue, out int i)
{
i++;
SONG_URL = queue[i].Name;
Stop();
Play();
}
int i = 0;
NextSong(SongQueue, out i);
This uses the out functionality, which enforces someone to pass a variable that will be returned. It passes the variable by reference (You could also use ref, but since int can't be null, it doesn't change much in this case).

That is working as expected, as long as 0 it will keep on being passed, the variable will always reset.
What you can do it to change the signature of the NextSong method to yield back the value of i:
public int NextSong(int i, TagLib.File[] queue)
{
i++;
SONG_URL = queue[i].Name;
Stop();
Play();
return i;
}
Then in your code you initialize some global value to 0 and call the method as follows: globalVariable = NextSong(globalVariable, SongQueue).

Related

How to add to UnityEvents of a Toggle in C# , Unity

I am creating toggle buttons programmatic in Unity at run time, and I would like to add an event to happen to these toggles.
I want to add to the Unity Event that is triggered when the toggle is clicked, but I can't seem to tie it together properly.
public Toggle myToggle_Prefab;
pulic int numberToggles;
private Toggle[] runtimeToggles;
private void Start()
{
runtimeToggles = new Toggle[numberToggles];
for(int i = 0; i < runtimeToggles.Length; i++)
{
runtimeToggles[i] = Instantiate(togglePrefab);
// ADD TO THE EVENT HERE
runtimeToggles[i].onValueChanged.AddListener(MyMethod);
}
}
private void MyMethod(int index){
// Do something
}
That doesn't work out because it is an integer paramater. The method works with a bool parameter like this:
private void MyMethod(bool arg0)
{
// Do stuff
}
But I need the integer parameter to do what I want to do. Any thoughts?
You do it like this:
for(int i = 0; i < runtimeToggles.Length; i++)
{
runtimeToggles[i] = Instantiate(togglePrefab);
// no-parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate{ MyMethod(thatInteger); });
// boolean parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate (bool b){ MyMethod(thatInteger); });
}
Wrap the method you want to call in a delegate (which takes either no parameters or the boolean) and call your method from there. Note that I've left thatInteger undefined, as your question doesn't indicate what the value is or is for.
Do note that if you want to pass the i value, you will need to copy it to a local-scope variable (See this question for why) first:
for(int i = 0; i < runtimeToggles.Length; i++)
{
// like this:
int thatInteger = i;
runtimeToggles[i] = Instantiate(togglePrefab);
// no-parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate{ MyMethod(thatInteger); });
// boolean parameter version:
runtimeToggles[i].onValueChanged.AddListener(delegate (bool b){ MyMethod(thatInteger); });
}
The boolean, by the way, I believe represents what the toggle was changed to (i.e. if it becomes checked, then that boolean passed is true).
The reason it works is because the event can invoke a narrow range of predefined delegate methods (offhand those signatures would be (), (bool state), and (UI.Toggle t) and I'm not 100% sure about that last one) covering the most probable uses of the event. Anything else has to get wrapped in a delegate that conforms to one of those; e.g. delegate{ f(int i); }, as otherwise the Unity internals don't know what it is you want to pass to your method.
No, the official documentation doesn't list these signatures (unhelpful, that) and are invoked through internal voodoo magic (like a lot of Unity MonoBehaviour methods).
I'm also new to unity and coming from other languages with Event Systems, the approach is roughly the same, just a difference in how to go about it.
I too have an array of Toggles that I not only wanted to know when one was selected but also WHICH one dispatched the event without having to write code to iterate through array indexes or other solutions. My Toggles were already in the prefab, but I will be moving to Instantiating prefabs for a dynamic list. Other than the the setup is the same and one can pass the toggle to the handler like so:
private void OnEnable()
{
_tabs = GetComponentsInChildren<Toggle>();
// Loop through the toggles to add listeners.
for (int i = 0; i < _tabs.Length; i++)
{
int idx = i;
_tabs[idx].onValueChanged.AddListener(delegate
{
TabClickHandler(_tabs[idx]);
});
}
}
private void OnDisable()
{
// Loop through toggles to remove listeners...
for (int i = 0; i < _tabs.Length; i++)
{
// As mentioned above, you need a local variable to pass to the delegate.
int idx = i;
_tabs[idx].onValueChanged.RemoveListener(delegate
{
TabClickHandler(_tabs[idx]);
});
}
}
public void TabClickHandler(Toggle tab)
{
// State changes are handled here which includes the previous toggle being set to off. Therefore, filter for only the isOn == true state changes
if (tab.isOn)
Debug.Log("Tab: " + tab.name + " isOn State:" + tab.isOn);
// Do any stuff you want here. You now can identify the tab. In my case it was to start the load process for a different inventory classification list.
}
Just make sure to remove your listeners to avoid memory leaks. If you are familiar with event systems, a lot of other languages also require this.

Locking and ref values from external function

I'm trying to track down a bug that I think might be related to the applications multithreading. I've simplified the code below:
class Outer {
private static int count;
//this function is called from multiple threads in quick succession
public void DoFoo() {
Inner.Increment(ref count);
}
}
class Inner {
private readonly static object mLock = new object();
public static string Increment(ref count) {
lock (mLock) (
if (count > 1000)
count = 0;
count++;
}
return count.ToString();
}
}
Can the locking guarantee the safety of a variable passed in that way? Is there any copying of count going on that seems non-obvious and may break the memory safety? I was thinking it might return a new int and do the assignment at the end of the method or something. Apart from that it's my understanding that the lock section would handle any caching issues.
The error which bought the issue to our attention was seemingly one of the threads having a stale version of count.
The problem here is that some other thread could read directly Outer.count when it is == 0, because you can access Outer.count without first having obtained a lock (normally as written in your code, count can be 0 only before the first call to Inner.Increment, from then on it can only have a value between 1 and 1001)
Lockless can be done in this way:
class Inner
{
public static string Increment(ref int count)
{
while (true)
{
int original = count;
int next = original;
if (next > 1000)
{
next = 0;
}
next++;
if (Interlocked.CompareExchange(ref count, next, original) == original)
{
return next.ToString();
}
}
}
}
I'm calculating a next value and using it (through Interlocked.CompareExchange) only if count hasn't changed in the meantime.

Issue with ++ operator

I'm attempting to add 1 to the variable levelname everytime the LevelChange function is called. However, everytime its called it resets the value to 1 like its originally set in the beginning of the code. I'm used to c++ and very confused. This code is a bit sloppy because ive tried a ton of ways to solve this. Any help would be appreciated.
Theres a bracket missing because for some reason i cant get this line to go in the code block.
public class NextLevel : MonoBehaviour {
int levelname = 1;
int newlevelname;
string levelnameS;
void LevelChange()
{
levelname++;
newlevelname = levelname;
string levelnameS = newlevelname.ToString(); //Converts newlevelname which is an int value to a string
Debug.Log(levelnameS); //prints to the console
SceneManager.LoadScene(levelnameS); //changes scene based on the level name. In this case its a number because thats what the levels are named.
Debug.Log(levelname);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Return))
{
LevelChange(); //calls the level change function
}
}
}
levelname is a instance property. Each instance of class NextLevel has own value of this. So if you every time create new instance of NextLevel and call Update count always start from 1.
You can switch levelname to static property or always use one instance of class NextLevel.
use public static int levelname = 1;
instead of int levelname=1

(C# Value Doesnt Change) Modifiying Static Variables from Static Callback Method

Hello i want to ask how to modifiying value static variable from static callback, and read value again. I need to stop while looping when value from static variable change. This is my code.
The problem is value from stepControlEx remain same, it doesnt change when another callback run again and modify the value. It makes my looping stuck and cannot end.
Thanks for helping
Private static int stepControl = 0;
Private static int stepControlEx
{
get { return stepControl;}
set { stepControl = value;}
}
Private static void CallbackHandle (Object object)
{
If (object == 0)
{
stepControlEx = 0;
While (stepControlEx == 0)
{
//do events
//stop when stepControl Change
}
}
Else If (object == 1)
{
stepControlEx = 1;
While (stepControlEx == 1)
{
//do events
//stop when stepControl Change
}
}
}
EDITED (SOLVED):
I tottaly change the algorithm and use thread that process data from queue. Callback function only enqueue input to queue and thread dequeue input to processing, now step control doesn't need as global variable. Thank you
Your code looks ambiguous. But I have only one comment for your code. If you have a static variable and shared among multiple threads, you need to lock the variable while changing it.
I think you get deadlocks that is why the loop never ends.
Anytime you assign any static variable in a multi-threading environment. you need to to this:
lock (new object())
{
stepControlEx = 0;
}

Send a reference to a variable instead of it's value

I have a function that does a long task and I want to update a variable somewhere else occasionally with status updates. (If there is a better method for doing this, that's fine too) I'm writing a library and this code might be called several times at once so creating another variable in the same class that stores the variable isn't an option. Here is what my code might look like:
public static bool Count(int Progress, int CountToWhat) {
for (int i = 0; i < CountToWhat; i++) {
Progress = CountToWhat / i; // This is how I'd like to update the value, but obviously this is wrong
Console.WriteLine(i.ToString());
}
}
This is not a good way of providing updates to your callers.
Better you define one ore more events in your class library (like OnError, OnProgress, etc.).
You can raise, for example, OnProgress when you want to notify progress in a certain operation:
for (int i = 0; i < CountToWhat; i++) {
OnProgress(CountToWhat / i);
Console.WriteLine(i.ToString());
}
This is a much better way of doing it, especially when notifying from worker threads.
Change the signature to:
public static bool Count(ref int Progress, int CountToWhat)
And when you call it, use the ref keyword before the variable that you pass in as the first argument.
You can use
int Progress = 0;
public static bool Count(ref int Progress, int CountToWhat)
{
....
}
Or
int Progress; //without init
public static bool Count(out int Progress, int CountToWhat)
{
....
}
A better way might be to pass an Action<int> delegate to be called to report progress:
public static bool Count(int CountToWhat, Action<int> reportProgress)
{
for (int i = 0; i < CountToWhat; i++)
{
var progress = CountToWhat / i;
reportProgress(progress);
Console.WriteLine(i.ToString());
}
}
then you'd use it like:
Count(100, p => currentProgress = p);
You could also use the BackgroundWorker class to run your long running task, and utilize its ProgressChanged event.

Categories