Using the "If OldVariable != Variable then {do stuff; OldVariable = Variable)" trick

thebitmasterthebitmaster PRO Posts: 75
edited September 2012 in Working with GS (Mac)
I want my actor to change color every time an internal variable, let's say it's called HitPoints, is updated. Since this variable can get updated in a number of places, I want the Rule that changes the color to be separate from the ones that update the variable. In order to detect the change and trigger and event, I create another variable of the same type and call it "Old HitPoints." I set this to something different from the original HitPoints so force the first firing of the rule to make sure we start at the right color. I then use this type of Rule:

If OldHitPoints != HitPoints then
Update my color
OldHitPoints = Hitpoints

The rule fires once, but never again. This is the only condition in the rule, and I could swear I've used this kind of detection before to cause the rule to fire every single time the two variables are different. It fires immediately because OldHitPoints != HitPoints, but subsequent changes to HitPoints doesn't make the rule fire again.

I can add a timer-type condition or an actual timer around it to force re-evaluation, but naturally this is a huge performance hit. I can also add a boolean called UpdateColor, and then do this:

Whenever I change HitPoints, I set UpdateColor to "true", then

If UpdateColor then
Update my color
UpdateColor = false

This seems to work, but why does *that* version work when the first version doesn't? It obviously has something to do with whatever conditions cause GS to re-evaluate a Rule, but the only difference I see is that the first version has variables on both sides of the = sign, but the second version has a constant on the right.

Thanks for any help!

Comments

  • Ok, now I'm seeing another Weirdism with even the simpler case.

    What I want here is that when an Actor has a certain internal variable altered, it will change color to reflect this.

    To do that I make two rules:

    Rule 1:
    If you click this object
    ChangeVariable ClickCount to ClickCount+1
    ChangeVariable BooleanUpdateColor to True

    Rule 2:
    If BooleanUpdateColor is True
    Change the color of the object based on ClickCount
    ChangeVariable BooleanUpdateColor to False

    So when you click on the Actor, ClickCount should increase, and then the second rule should fire and update the color.

    If you set the initial value if BooleanUpdateColor to false, it will work with every click, and update the color every time as I expect it to.

    If, however, you set the initial value of BooleanUpdateColor to true, Rule 2 will fire once at the beginning, but never again, despite the fact that ClickCount will continue to increment.

    This definitely seems like a bug to me. The behavior on the first run through Rule 2 *should* be different based on the initial value of BooleanUpdateColor, but second and subsequent runs should behave *exactly* the same irrespective of the initial value. They do not.

    If you watch the value of BooleanUpdateColor, in the first case it stays at False, because it's changed from True to False so fast you can't see it. In the second case, though, when BooleanUpdateColor is initially set to True, it stays at True forever, which is further proof that Rule 2 is never been run again.

    To thicken the plot, if you re-write Rule 2 to have the last statement in a timer, it *does* work correctly.

    Rule 2:
    If BooleanUpdateColor is True
    Change the color of the object based on ClickCount
    After 0.5 seconds:
    ChangeVariable BooleanUpdateColor to False

    This would normally make me suspect some kind of synchronization bug, but to me the comparison of the two first cases differing only by the initial value of BooleanUpdateColor means it's a bug.

    Thoughts? Rebuttals? Am I missing something? I've already submitted it to GS.
  • gyroscopegyroscope I am here.Member, Sous Chef, PRO Posts: 6,598
    edited September 2012

    Hi @thebitmaster

    As far as I can see, there's no bug. If you set BooleanUpdateColor (why not BoolUpCol? ;-) ) to true, then it'll jump straight to Rule 2 and so bypass the ClickCount updated value.

    I can't see your worry – just set the initial state of BooleanUpdateColor to false as you did, and maybe add a condition in Rule 1, and you should be fine.

    Rule 1:
    When touch is pressed and BooleanUpdateColor is false
    ChangeVariable ClickCount to ClickCount+1
    ChangeVariable BooleanUpdateColor to True

    ""You are in a maze of twisty passages, all alike." - Zork        temp domain http://spidergriffin.wix.com/alphaghostapps

  • jonmulcahyjonmulcahy Member, Sous Chef Posts: 10,408
    I'd do it this way:

    I the actor that changes color create a local attribute called currentHitPooints

    Then put a rule

    // this sets the initial value

    Change self.currenthitpoints = game.hitpoints

    // this updates the color and value of self.currenthitpoints. There might be an 'is not equal to' operator, I just forget, but you can fake it if you need to by using two rules and make the rule flag to any.

    Wen self.currenthitpoints > game.hitpoints or self.currenthitpoints< game.hitpoints
    Change color
    Change attribute self.currenthitpoints = game.hitpoints

    That should do it I think. Writing from memory on the train
  • Hi @gyroscope.

    Thanks for trying to help.

    If the initial condition of BooleanUpdateColor is set to True, it should fire Rule 2 immediately. The last statement in Rule 2 is to set BooleanUpdateColor to False, which *should* put us back into the original case of it having been set to False to begin with. After that, the behavior should be exactly the same, but it's not.

    If I set it to BooleanUpdateColor to False, with each click the color changes correctly, and BooleanUpdateColor ends up as False after each change, as it should be.

    If I set it to BooleanUpdateColor to True, the color changes at the beginning of the program, and the BooleanUpdateColor is set to False. This all looks correct. However, if I start clicking, ClickCount increases, but the color never changes again, and BooleanUpdateColor remains True, which means that Rule 2 never fires again, even though BooleanUpdateColor = true is it's one and only condition.
  • Hi @jonmulcahy,

    Thanks for trying to help.
    How is what you're suggesting different than my first post above? It's using the same OldVariable = Variable methodology. Other than one variable maybe being a global instead of a local (both of mine were local), it looks the same.

    That's why I'm confused, though, because I'm pretty sure I used exactly the method that both of us seem to be describing, and whenever Variable was updated by an separate rule, the If OldVariable != Variable rule would fire correctly. Now it's not happening anymore.
  • jonmulcahyjonmulcahy Member, Sous Chef Posts: 10,408
    edited September 2012
    Hi @jonmulcahy,

    Thanks for trying to help.
    How is what you're suggesting different than my first post above? It's using the same OldVariable = Variable methodology. Other than one variable maybe being a global instead of a local (both of mine were local), it looks the same.

    That's why I'm confused, though, because I'm pretty sure I used exactly the method that both of us seem to be describing, and whenever Variable was updated by an separate rule, the If OldVariable != Variable rule would fire correctly. Now it's not happening anymore.
    for this type of function I don't trust booleans. then tend to trigger too often in my eyes.

    the global attribute is needed so all actors can easily track the hitpoints.

    give my method a try, it should work (in theory :) )

  • Hi @johmulcahy,

    I think you might be looking at the wrong post. The first post I made in this thread didn't use booleans, it used integers, and seems to be exactly the same system you're describing, which, at least currently, doesn't work, even though both of us seem to remember it working in the past. Can you take a look?

    In this case the hitpoint-style variable I'm discussing is something that every individual actor has, so it has to be local. But there are several different places within that actor that can alter the hitpoints, which is why I want to have the Rule that changes the color separate, so it fires no matter which other Rule alters the hitpoints.
  • Is there a good way to upload and reference a GS project in these discussions? Looking at a project is *so* much easier than trying to describe it all in pseudocode. :)
  • Hi @johmulcahy,

    I did a test using the method I first suggested that you also described. If the initial condition of Variable and OldVariable are the same, it works. However, if they're different, it fires once, and never works again. Try it yourself. I've tried it with both Booleans and Integers, and it's the same bug on either one.

    This also means that a simple For loop in a rule doesn't work correctly, either.

    ChangeAttribute Looper to 0

    Rule 1:

    If Looper < 200 then
    Do Something
    ChangeAttribute Looper to Looper +1

    ...will only run once, not 200 times. If the entire thing is put inside a timer, or even just the ChangeAttribute is put inside a timer, it will work, but it should not need to be inside the timer. It should keep running as fast as the engine can execute until it's gone 200 times.
  • RThurmanRThurman Member, Sous Chef, PRO Posts: 2,881
    Here is an old thread that might still be relevant:
    forums.gamesalad.com/discussion/5545/tip-arrays-and-loops
  • Thanks, @RThurman!
Sign In or Register to comment.