Thread Tools Display Modes
07/04/15, 11:06 PM   #1
Capadillo
Join Date: Feb 2014
Posts: 12
Smoothly Animated Status Bar... Help!

I'm looking to smoothly animate a custom attribute bar. Currently I am using ZO_StatusBar_SmoothTransition. It's nice and simple but it does what the default bars are doing.

That is, because EVENT_POWER_UPDATE only fires every couple of seconds, it has a stop-start animation. I'm looking to make it a completely smooth animation without stopping, but I don't know how to accommodate for the period of time between updates without making the bar slower.

I've tried a couple of different ideas with all of them not working or having too many drawbacks to be of real use.

Any input would be appreciative!
  Reply With Quote
07/05/15, 01:49 AM   #2
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
I haven't tried it, but I would look at something like this (example using stamina):


Lua Code:
  1. local amountRegen = 0
  2.  
  3. if IsUnitInCombat("player") then
  4.     amountRegen = GetPlayerStat(STAT_STAMINA_REGEN_COMBAT, STAT_BONUS_OPTION_APPLY_BONUS, STAT_SOFT_CAP_OPTION_APPLY_SOFT_CAP)
  5. else
  6.     amountRegen = GetPlayerStat(STAT_STAMINA_REGEN_IDLE, STAT_BONUS_OPTION_APPLY_BONUS, STAT_SOFT_CAP_OPTION_APPLY_SOFT_CAP)
  7. end

Then I believe the regen time is every 2 seconds, I don't think there is any API to get that regen time delay, I think its just always 2 seconds (I could be wrong).

So when you update your stat if your using a status bar statusBar:SetValue(curStam) or if your doing the size translation you posted in the other thread..whatever, just adjust the time so it will complete in 2 seconds. Then by the time it finishes the next regen tick will come in & your update function fires again, sets the new powerValue and gives it 2 seconds to complete, so it never stops smoothly filling in the bar.

The only problem I see with this is that your bar will always be 2 seconds behind the real bar when stats are regening because the original bars update the full amount immediately while your smooth animation will take 2 seconds to fill the bar in by the amount passed in from the power update event.

I think the only solution to that problem would be to anticipate the next regen tick & and start filling in your status bar for the "next tick" before it actually happens. So like as soon as the stamina drops start running your updateBar function & filling in the bar immediately by the amount: amountRegen (posted in code above) instead of waiting for that next powerUpdate event to fire & tell you that you just gained xxx stamina....since we already know how much we are going to gain & I believe the regen time is always the constant 2 seconds then by the time the power update event fires & says you gained that amountRegen stamina, your bar has just finished filling it in and your ready to handle the next tick.

Last edited by circonian : 07/05/15 at 03:05 AM.
  Reply With Quote
07/05/15, 02:58 AM   #3
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
Here you go, here is a complete example. This example does "anticipate" the next tick (as I mentioned above) so it does not fall behind during the 2 second regen delay.

I just altered my Stamina Bar tutorial code to smooth out the status bar updates. It was a tutorial for beginners so the bar is pretty ugly but it updates very smoothly. You can find it on my dropbox here:
Circonians Stamina Bar Tutorial Addon (with Smooth Updates)

I added some comments about what I did & why, you really only need to worry about the file CirconianStaminaBar.lua, lines 15-68 are the part that update the status bar. This is from a tutorial I wrote so theres probably other comments in there too. If you try to replicate it & have problems or have trouble understanding some other part of the code look at the tutorial: Circonians Stamina Bar Tutorial

Also note I intentionally did not smooth out the drops/loss of stats. I agree to making it look nice, but I figured when you get hit for xxx dmg, that health is gone immediately so the bar should reflect that. Although you could easily alter the code to smooth out the stat losses as well.

Last edited by circonian : 07/05/15 at 03:47 AM.
  Reply With Quote
07/05/15, 03:54 AM   #4
Capadillo
Join Date: Feb 2014
Posts: 12
Originally Posted by circonian View Post
Here you go, here is a complete example. This example does "anticipate" the next tick (as I mentioned above) so it does not fall behind during the 2 second regen delay.

I just altered my Stamina Bar tutorial code to smooth out the status bar updates. It was a tutorial for beginners so the bar is pretty ugly but it updates very smoothly. You can find it on my dropbox here:
Circonians Stamina Bar Tutorial Addon (with Smooth Updates)

I added some comments about what I did & why, you really only need to worry about the file CirconianStaminaBar.lua, lines 15-68 are the part that update the status bar. This is from a tutorial I wrote so theres probably other comments in there too. If you try to replicate it & have problems or have trouble understanding some other part of the code look at the tutorial: Circonians Stamina Bar Tutorial

Also note I intentionally did not smooth out the drops/loss of stats. I agree to making it look nice, but I figured when you get hit for xxx dmg, that health is gone immediately so the bar should reflect that. Although you could easily alter the code to smooth out the stat losses as well.
Thank you so much for this response! I tested it out and it's close to what I want, maybe a little slower in regenerating then what I was hoping but I guess I can only get so much! Again thank you! This is definately going to help a lot!

EDIT:

I changed the constants to...
Lua Code:
  1. local UPDATE_INTERVAL = 5;
  2. local UPDATES_PER_INTERVAL = 125;
Which is 625 ( a little ahead of the bar but not by much ) and it seems to be faster with less error.
and oddly enough it seems to work a little better.

Last edited by Capadillo : 07/05/15 at 04:45 AM.
  Reply With Quote
07/06/15, 04:51 PM   #5
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
Originally Posted by Capadillo View Post
Thank you so much for this response! I tested it out and it's close to what I want, maybe a little slower in regenerating then what I was hoping but I guess I can only get so much! Again thank you! This is definately going to help a lot!

EDIT:

I changed the constants to...
Lua Code:
  1. local UPDATE_INTERVAL = 5;
  2. local UPDATES_PER_INTERVAL = 125;
Which is 625 ( a little ahead of the bar but not by much ) and it seems to be faster with less error.
and oddly enough it seems to work a little better.
I looked at it again, your right it was a little slow. The character window says you regain stats every 2 seconds "in combat" I just assumed it was the same time interval for out of combat (but a different amount of regen), I guess thats not the case.

I wonder if they have different regen time intervals in & out of combat or if the character sheet tooltip is wrong. Have you tried it while in combat to see if it still matches up with the default stat bars while in combat?
  Reply With Quote
07/06/15, 08:38 PM   #6
Capadillo
Join Date: Feb 2014
Posts: 12
Originally Posted by circonian View Post
I looked at it again, your right it was a little slow. The character window says you regain stats every 2 seconds "in combat" I just assumed it was the same time interval for out of combat (but a different amount of regen), I guess thats not the case.

I wonder if they have different regen time intervals in & out of combat or if the character sheet tooltip is wrong. Have you tried it while in combat to see if it still matches up with the default stat bars while in combat?
That was my assumption before as well. I noticed that outside of combat the regen rate is double, so maybe it ticks half the time as well. About to test it!

EDIT: Added in this...
Lua Code:
  1. local divis = IsUnitInCombat(control.unitTag) and 1 or 1.77; -- higher the # faster the regen
and then changed the SetValue update to...
Lua Code:
  1. statusBar:GetValue() + (regen / (UPDATES_PER_INTERVAL / divis)))

It's about a close as i can eyeball. Odd number though. It would be an update every 1129.9ms.

Assuming I have all that right, I just need to throw in a check for if we were healed and do a forced update for that as well. Though, having had looked, I'm not sure there is an event or function that only fires when we get healed.

EDIT2: Could it be the difference in latency?

EDIT3: Here's a link to my current version: DropBox. The main issue I have left over is when you're healed (i test by healing myself) the health bar sometimes jumps forward (like it should) then back again. Have a look and see what you think of the progress if you want!

Last edited by Capadillo : 07/07/15 at 03:12 AM.
  Reply With Quote
07/07/15, 12:02 PM   #7
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
Originally Posted by Capadillo View Post
EDIT2: Could it be the difference in latency?
Could what be? Do you mean the time interval difference on regen in & out of combat? No, since the real stat bar updates in large chunks the latency would have to be insane to have any effect & it would correct itself the first time the bar updated.

Originally Posted by Capadillo View Post
EDIT3: The main issue I have left over is when you're healed (i test by healing myself) the health bar sometimes jumps forward (like it should) then back again. Have a look and see what you think of the progress if you want!
I glanced at the code, and tried it out, it looks nice btw I like it....but I did not notice what you are describing. When I healed myself it jumped forward like it should and the only time it ever jumped back was if I got hit right after I healed.

However, I only tested it for a minute or two. If it is doing what you say its happening in this part of the code:
Lua Code:
  1. local currentValue = control.statusBar:GetValue();
  2. control.statusBar:SetMinMax(0, maxPower);
  3. -- Show damage taken or healing taken straight away!
  4. if( currentValue > currentPower )then
  5.     control.statusBar:SetValue(currentPower);
If the currentValue of the bar somehow gets higher than your actual current power, then it resets the bars value, which would make the bar jump back down. How far it would jump would depend on the difference between the currentValue & currentPower. That is the way I wrote the code in my example & it should do that to keep the bar in sync with your real power value. Although we can change "how" it keeps the bars value in check...later/below.
If the bar is getting out of sync somehow and the currentValue of the bar is getting to large ( > currentPower) then that is the problem. That code is just whats making it jump, but its not the problem.

Again I did not notice the problem, but my guess would be this:
Lua Code:
  1. -- If we have been healed?
  2. if( control.powerType == POWERTYPE_HEALTH and self.forceHealthUpdate ~= nil )then
  3.     currentValue = currentValue + self.forceHealthUpdate
  4.     self.forceHealthUpdate = 0;
  5. end
  6. -- If we had to heal...
  7. local POWERTYPE_REGEN = GetPlayerRegen(control.powerType);
  8. local COMBAT_MODIFIER = IsUnitInCombat(control.unitTag) and 1 or 1.75;
  9. control.statusBar:SetValue(currentValue + POWERTYPE_REGEN / (TICKS_PER_UPDATE / COMBAT_MODIFIER));
Remember the way its written, the bar is continuously filling in (as long as your missing health) to make it smooth and it starts filling in the bar BEFORE you actually gain the health....um this seems a little complicated & lengthy to explain, let me try an example.

You have 100 max health, you get hit for 20 dmg. Your code drops the bar to 80 and immediately starts filling in the bar. So as time goes on, you have 81 health, 82, 83, then you heal yourself for 10 health, this code sees that and saves it in self.forceHealthUpdate
Warning: Spoiler


Your update function is still ticking away and the next time it ticks it sees that self.forceHealthUpdate +10 value and adds it to your health bars currentValue:
Lua Code:
  1. -- If we have been healed?
  2. if( control.powerType == POWERTYPE_HEALTH and self.forceHealthUpdate ~= nil )then
  3.     currentValue = currentValue + self.forceHealthUpdate
  4.     self.forceHealthUpdate = 0;
  5. end
Now your health bars currentValue is at 94 health (+1 from the next regen tick & +10 from the heal)...BUT If the game hasn't actually registered a health regen tick yet (remember your health bar is filling in immediately and not waiting for a power Update Event to tell it that health was gained)....then as far as the game is concerned you only have 90 health: The original 80 (from after you took damage) + 10 from the heal.

Now, you caught the heal in your CombatEvent function...but when you gain health the power update event fires also which your PowerUpdate function is registered to:
Lua Code:
  1. EM:RegisterForEvent(self.name, EVENT_POWER_UPDATE, function(...) self:PowerUpdate(...); end);

So now PowerUpdate fires because the EVENT_POWER_UPDATE also fired and this code runs:
Lua Code:
  1. function TAB:PowerUpdate(_, unitTag, _, powerType, currentPower, maxPower)
  2.     -- Do we watch for this unitTag & powerType?
  3.     if( not POWER_INFO[unitTag] or not POWER_INFO[unitTag][powerType] ) then return false; end
  4.     -- Look for any controls that match this pattern.
  5.     local control = _G['TAB'..ucfirst(unitTag)..ucfirst(POWER_INFO[unitTag][powerType])];
  6.     -- If it exists.
  7.     if( control )then
  8.         local currentValue = control.statusBar:GetValue();
  9.         control.statusBar:SetMinMax(0, maxPower);
  10.         -- Show damage taken or healing taken straight away!
  11.         if( currentValue > currentPower )then
  12.             control.statusBar:SetValue(currentPower);
  13. ...
  14. end

and the key piece of this, finally....is that remember your bar registers you as having 94 health, while the game says you only have 90 health (because the game hasn't run an update for a health "regen")
Lua Code:
  1. if( currentValue > currentPower )then
  2.             control.statusBar:SetValue(currentPower);
and that code would force the bars value to jump back down because the currentValue is greater than the currentPower.
Also, if your bar is updating to fast and getting to far ahead of your actual health from when you altered those interval values and you heal yourself this code could also cause the bar to jump back down.



The best thing I can think of atm:

If your bar is getting to far ahead of the actual values, you could do something like this to limit how far ahead the bar can go. This would prevent it from getting more than 1 regen tick worth of stat ahead of the actual power value, regardless of what your intervals are set at:
Lua Code:
  1. -- If we had to heal...
  2. local POWERTYPE_REGEN = GetPlayerRegen(control.powerType);
  3. local COMBAT_MODIFIER = IsUnitInCombat(control.unitTag) and 1 or 1.75;
  4.  
  5. -- Since we want to allow the bar to fill in "up to" an amount of
  6. -- POWERTYPE_REGEN ahead of the actual power, we make this the actual power + POWERTYPE_REGEN
  7. local actualPower = GetUnitPower("player", control.powerType) + POWERTYPE_REGEN
  8. currentValue = currentValue + POWERTYPE_REGEN / (TICKS_PER_UPDATE / COMBAT_MODIFIER)
  9.  
  10. -- Take the minimum of the two, this will make sure our bars value never jumps ahead
  11. -- more than POWERTYPE_REGEN of the players actual power value.
  12. local newValue = zo_min(currentValue, actualPower)
  13. control.statusBar:SetValue(newValue);

But if its being caused by the heals problem/example I gave then it could still be forced to jump back down...so you could also do something like this:
Lua Code:
  1. if( control )then
  2.     local currentValue = control.statusBar:GetValue();
  3.     control.statusBar:SetMinMax(0, maxPower);
  4.     if powerType == POWERTYPE_HEALTH then
  5.         if oldHealth > currentPower then
  6.             control.statusBar:SetValue(currentPower);
  7.             control.label:SetText(zo_round(currentValue/maxPower*100)..'%');
  8.         end
  9.         oldHealth = currentPower
  10.        
  11.     elseif currentValue > currentPower then
  12.         control.statusBar:SetValue(currentPower);
  13.         control.label:SetText(zo_round(currentValue/maxPower*100)..'%');
  14.     end
  15. ...
Each time you have a power update event, check to see if its for health & save that health amount in some variable. Then do your "if" check against the saved "oldHealth" value, instead of the stat bars current value. This would prevent the status bar from jumping back down because its ticking ahead of the games regen ticks. Don't forget to initialize oldHealth in your OnLoad.

But if you do this fix, you probably need to do both of the fixes I mentioned because this code here would no longer prevent the bar from ticking to far ahead of your stats actual value!! so we need both fixes. But then this may also require you to readjust your interval regen values.

Changes on my dropbox: TinyAttributeBar

I did also notice one strange thing, If I run...at first the bar updates immediately as I'm running. If you keep sprinting though eventually the bar stops updating until you stop running & the first stamina regen power update event fires. Apparently it appears that (for stamina at least) GetPlayerStat(..) returns 0 if you sprint for to long (it doesn't take long). I don't know if this is a game bug or if its intentional, like you get tired and stop regaining stamina until you stop sprinting.
  Reply With Quote
07/08/15, 02:00 AM   #8
Capadillo
Join Date: Feb 2014
Posts: 12
Hmm, none of that seemed to help with the issue I was having unfortunately.

I did however take some time and rewrote most of the two functions. I found a solution that I feel may be problematic later on, but it seems to work. No backtracking of the health bar at all from what I can see. It seems to have fixed the stamina bug as well.

Should be the same link but just in case: It's been updated!. Again, thank you for responding!
  Reply With Quote

ESOUI » Developer Discussions » General Authoring Discussion » Smoothly Animated Status Bar... Help!


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off