- Home /
Is there a way to dynamically check for variable change ?
Hi guys .
I know how to check if a variable changes . One method is like this
public class SomeClass
{
private Rect someRect;
private Rect T;
void OnEnable()
{
if (someRect == null)
{
} someRect = new Rect(0, 0, 100, 100);
}
bool RectChanged()
{
bool result = false;
if (someRect != T)
{
result = true;
T = someRect;
}
return result;
}
void OnGUI()
{
if(RectChanged())
{
Debug.Log("ChangeMade");
}
}
}
However this method is really crude .
I need to be able to dynamically check for variable changes without having to do that kind o coding I will give you an example. this wont actually work well , it is just an example of what i'm trying to do.
if i were to create instances of this class in non static classes and make the method and variable non static then it would work well. However I do have class that I need to keep static which need to use the changed function.
public class Changed
{
private static object obj;
public static bool changed(object T)
{
bool result = false;
if(obj != T)
{
obj = T;
result = true;
}
return result;
}
}
And use it this way
public class TestClass
{
private static Rect someRect = new Rect(0, 0, 100, 100);
public static void TestFunction()
{
if (Changed.changed(someRect))
{
Debug.Log("change made");
}
}
}
I figure that there is a high change of this being not possible but if you know of a way to do this then please share.
I like your Changed class. I would modify it to store a list of ALL the objects you want to check, like so:
-$$anonymous$$ake the class static: so you can call functions like so- Change.RegisterObject(obj,function); always referencing the same single instance of the class.
-Add a main List to the class (replacing your object obj member)
-Add a notify List to the class. this list should match your main List, but it might be better to create a new class say..ChangeObjectAndAction that contains only an object and an action, and make a SINGLE List.
-Add a second List, that stores a COPY of all the objects in your main list. (this part can be a bit tricky, if you are not using basic class types like "string" and "int"- because you don't want a REFERENCE to the original, you want a copy of it's data: http://answers.unity3d.com/questions/501160/how-to-duplicate-a-gameobject-using-scripting.html)
-Add a registerObject() function, to add objects to the list, and a DeRegisterObject() function to remove them from the list. The registerObject function would take an Object, and an Action (reference to another function) as parameters, and add the object to your main list and a copy to your second list. It would also add the Action to the notify list.
-Add a compareAllObjects() function, in which you go through the main list, and compare all the values it contains with all the values in your second list. If you detect any mismatches, you can call the Action function you passed in when registering the object. Then set the value in the second list to equal the value of the first list, so they match for the next pass.
Disadvantage: The comparisons would be performed once per cycle, rather than immediately. Advantage: You don't need to modify or add any code when assigning a new value to a variable.
Let me know if you like this method and need more details or code examples.
nice idea , i'm DEFINITELY going to give it a try and let you know how it goes .
If you already have it coded then you can post some code too.
$$anonymous$$y concern with this would be the speed of the code seeing as we would be using lists.
This feels heavy , having to sort through the list continuously is a bit much. Too bad I cant use Dictionaries. This would be a bit heavy
Answer by Anxo · Sep 18, 2015 at 08:56 PM
I think you might be looking for events.
public class ClassA : MonoBehaviour {
public delegate void ChangeEvent (int numberOfApples); //I do declare!
public static event ChangeEvent changeEvent; // create an event variable
public int appleCount = 0;
// trigger some change and call the event for all subscribers
public void MoreApples(){
appleCount ++;
if(changeEvent!=null) // checking if anyone is on the other line.
changeEvent(appleCount);
}
}
// Then you subscribe to the even from any class. and if the event is triggered (changed) then your other class will be notified.
public class ClassB : MonoBehaviour{
void OnEnable(){
ClassA.changeEvent += ApplesChanged; // subscribing to the event.
}
void ApplesChanged(int newAppleCount){
Debug.log("newAppleCount now = " + newAppleCount); // This will trigger anytime you call MoreApples() on ClassA
}
}
}
WUCC, check for errors.
I wonder... are the Event heavy, from optimisation point of view? Like if you have 20, 30 events listeners in the game, coud that potentially affect the performance of the game...
I guess it's worth mentioning, that when you don't need to check for that variable anymore, you shoud un-subscribe, from the event.... I suppose that's done with -= AppleChanged right?
correct. You also never want to call one with no subscribers without checking for null.
@Anxo I'm not getting it .
public class Check
{
public delegate void Changed (object T);
public static event Changed changeEvent;
public object obj;
public void $$anonymous$$akeCheck()
{
Debug.Log("ran");
int I = 0;
obj = I;
if(changeEvent!= null)
{
changeEvent(obj);
}
}
}
public class ClassB
{
static ClassB()
{
Debug.Log("set");
Check.changeEvent += Check_changeEvent;
}
static void Check_changeEvent(object T)
{
Debug.Log("Change made");
}
}
When would I call Check.$$anonymous$$akeCheck() if it will only trigger.
In my editor window , If i have a Rect , int, string and I want to only want to print out a message once the values of any of those fields changes .
public class ClassC : EditorWindow
{
// init editor window function here
private int SomeInt = 1;
private String SomeString = "name";
private Rect SomeRect = newRect();
void OnGUI()
{
if(GUI.Button(new Rect(0,0,100,40),"Change Rect"))
{
SomeRect = new Rect(UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20));
}
// Once the Rhange Rect button is clicked then ...?? }
}
I'm sorry , I'm just not used to this.
When ever you change the value. I have not tried to subscribe in static classes / voids before but when you change the value here.
new Rect(UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20));
//Now trigger event
Check.$$anonymous$$akeCheck(someobject); // you will need a reference to the class as its not static but you get what I mean right?
@Anxo Sure thing I get it , and that would work. the big concern is that If we simply want the check to be continuous.
also Check.$$anonymous$$akeCheck doesn't take any parameters . So i'm writing this on the premise that i am missing something.
i.e void OnGUI() { // keep checking for change here . if a change is made then do something
}
I wouldn't be ale to call Check.$$anonymous$$akeCheck(someobject);
without wrapping it in that if statement. which is the problem .
What you are saying makes a lot of sense if I am not wrapping the calling of the event method in an if statement. But how about when i want to so a continuous check ?
public class Check
{
public delegate void Changed (object T);
public static event Changed changeEvent;
public static object obj;
public static void $$anonymous$$akeCheck()
{
Debug.Log("ran");
int I = 0;
obj = I;
if(changeEvent!= null)
{
changeEvent(obj);
}
}
}
public class ClassB
{
static ClassB()
{
Debug.Log("set");
Check.changeEvent += Check_changeEvent;
}
static void Check_changeEvent(object T)
{
Debug.Log("Change made");
}
}
Use
public class ClassC : EditorWindow
{
// init editor window function here
private int SomeInt = 1; // not used in this example
private String SomeString = "name";
private Rect SomeRect = newRect();
void OnGUI()
{
if(GUI.Button(new Rect(0,0,100,40),"Change Rect"))
{
SomeRect = new Rect(UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20), UnityEngine.Random.Range(0, 20));
Check.$$anonymous$$akeCheck(SomeRect ); // then Check_changeEvent in ClassB would be called.
}
Check.$$anonymous$$akeCheck(SomeString); // calls Check_changeEvent in ClassB continuously . Problem.
}
}
Basically Check.$$anonymous$$akeCheck wouldn't actually do what i need it to do , which is wait for the value of the parameter it (obj) to change before carrying out some function.
ins$$anonymous$$d calling Check.$$anonymous$$akeCheck only carries out a function regardless of if the value in its parameter is changed.
Answer by Alanisaac · Sep 19, 2015 at 08:54 PM
If you're not feeling events -- A "wrapper" class that uses properties might be what you're looking for.
The class looks like:
public class ChangeTrackingWrapper<T>
{
private T _value;
private bool _hasChanged;
public ChangeTrackingWrapper()
{
}
public ChangeTrackingWrapper(T initialValue)
{
_value = initialValue;
}
/// <summary>
/// Gets or sets the wrapped value.
/// </summary>
public T Value
{
get { return _value; }
set
{
if (Equals(_value, value)) return;
_value = value;
_hasChanged = true;
}
}
/// <summary>
/// Gets a flag indicating whether the wrapped value has changed.
/// </summary>
public bool HasChanged
{
get { return _hasChanged; }
}
/// <summary>
/// Resets the changed flag.
/// </summary>
public void ResetChangedFlag()
{
_hasChanged = false;
}
}
Notes:
I think this generalizes your original example.
The magic happens in the Value setter. If the value isn't different, the setter doesn't do anything. But if the value is different, it will set the HasChanged flag to true.
You can initialize the class with an initial value, or let it be the default value (which is default(T)). It uses the generic for type safety, and isn't static, so you can use it in place of any field in a class that you'd like (see usage below). I wasn't sure if or when you wanted to "reset" that your object has changed, but I included a ResetChangedFlag() method on the class.
Usage:
public class SampleClass
{
private ChangeTrackingWrapper<Rect> _myChangeTrackingWrapper;
void OnEnable()
{
if (_myChangeTrackingWrapper == null)
{
_myChangeTrackingWrapper = new ChangeTrackingWrapper<Rect>(new Rect(0, 0, 100, 100));
}
}
void OnGUI()
{
if (_myChangeTrackingWrapper.HasChanged)
{
Debug.Log("ChangeMade");
_myChangeTrackingWrapper.ResetChangedFlag();
}
}
}
Static Usage
public class StaticSampleClass
{
public static ChangeTrackingWrapper<object> MyStaticChangeTrackingWrapper = new ChangeTrackingWrapper<object>();
public void MyMethod()
{
StaticSampleClass.MyStaticChangeTrackingWrapper.Value = new object();
var hasChangedAsString = StaticSampleClass.MyStaticChangeTrackingWrapper.HasChanged.ToString();
Debug.Log(hasChangedAsString); //Should log "true"
}
}
for some reason Set wont run at all
set
{
if (Equals(_value, value)) return;
_value = value;
_hasChanged = true;
}
I just tested this myself again. Works fine for me. Did you copy and paste the ChangeTrackingWrapper class fully?
What do you mean by won't run -- will it not compile, or does it have the wrong behavior?
yeah, i copied it fully . I do not know why but nothing in set{} runs.
@alfalfasprout Set does work.
problem is I am working in a static method
public class StaticSampleClass
{
public static ChangeTrackingWrapper<object> $$anonymous$$yStaticChangeTrackingWrapper = new ChangeTrackingWrapper<object>();
public static void $$anonymous$$y$$anonymous$$ethod()
{
$$anonymous$$yStaticChangeTrackingWrapper.Value = new object();
var hasChangedAsString = $$anonymous$$yStaticChangeTrackingWrapper.HasChanged.ToString();
Debug.Log(hasChangedAsString); //Should log "true"
}
}
Set wont be called now
the example doesn't work at all . Not sure why
Your answer
Follow this Question
Related Questions
Initialising List array for use in a custom Editor 1 Answer
Distribute terrain in zones 3 Answers
Multiple Cars not working 1 Answer
Listen to Keyboard Events on EditorWindow 1 Answer
Creating a class wide array (c#) 1 Answer