Thread Tools Display Modes
01-26-10, 02:51 PM   #1
Amenity
Guest
Posts: n/a
"Pulsating" effect...what's the proper method?

I have frame "frame". I want it to fade from alpha "1" to alpha "0", then back to alpha "1" and repeat until I say otherwise to make a "pulsing" effect.

I tried doing this in a manner like so, and...well...yeah, infinite loops are bad.

lua Code:
  1. while myvariable = 1 do
  2.     alpha = frame:GetAlpha()
  3.     if alpha = 1 then
  4.         UIFrameFadeOut(frame, 1, 1, 0)
  5.     elseif alpha = 0 then
  6.         UIFrameFadeIn(frame, 1, 0, 1)

Hello, Mr. Client Crash.
  Reply With Quote
01-26-10, 02:58 PM   #2
d87
A Chromatic Dragonspawn
 
d87's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2006
Posts: 163
Post

Code:
local SHINE_FADE_IN = 0.15;
local SHINE_FADE_OUT = 0.15;
local TICK_PERIOD = 0.35;

local ShineFadeOut = function(frame)
    local fadeInfo = {};
        fadeInfo.mode = "OUT";
        fadeInfo.timeToFade = SHINE_FADE_OUT;
        fadeInfo.finishedFunc = function(self) self.animating = false end;
        fadeInfo.finishedArg1 = frame;
    UIFrameFade(frame, fadeInfo);
end
local ShineFadeIn = function(frame)
    if not frame.animating then
        local fadeInfo = {};
        fadeInfo.mode = "IN";
        fadeInfo.timeToFade = SHINE_FADE_IN;
        fadeInfo.finishedFunc = function(arg1) ShineFadeOut(arg1); end;
        fadeInfo.finishedArg1 = frame;
        frame.animating = true
        UIFrameFade(frame, fadeInfo);
    end
end

f.Shine = function(self)
        ShineFadeIn(self.shinetex)
    end

f.OnUpdate = function (self, time)
        self.OnUpdateCounter = (self.OnUpdateCounter or 0) + time
        if self.OnUpdateCounter < TICK_PERIOD then return end
        self.OnUpdateCounter = 0
        
        self:Shine()
    end
  Reply With Quote
01-26-10, 03:00 PM   #3
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
Well did a quick search and there isn't much around on that subject so perhaps they hit a wall.

So far all I have seen is people utilising it on Enter and Leave scripts to do the different Fading functions. So how about this.

If you have an Update function that you call every so often perhaps you can put it in there to fadein if it has faded out or to fade out if it has faded in. Then keep a variable to store the last value updated. It's not perfect but might be the only way you can deal with it and even throwing the cycle in every event you watch may be a good idea too. The combination of the two functions may be enough to make it look passable at least.


Edit: Or what d87 said
__________________
  Reply With Quote
01-26-10, 03:16 PM   #4
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Pretty elegant IMO:
lua Code:
  1. frame.mult = 1
  2. frame.alpha = 1
  3. frame:SetScript("OnUpdate", function(self, elapsed)
  4.     self:SetAlpha(self.alpha)
  5.     self.alpha = self.alpha - elapsed*self.mult
  6.     if self.alpha < 0 and self.mult > 0 then
  7.         self.mult = self.mult*-1
  8.         self.alpha = 0
  9.     elseif self.alpha > 1 and frame.mult < 1 then
  10.         self.mult = self.mult*-1
  11.     end
  12. end)
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-26-10, 03:32 PM   #5
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
*book marks post so I can come back and ste... erm I mean utilise the code*

Originally Posted by nightcracker View Post
Pretty elegant IMO:
lua Code:
  1. frame.mult = 1
  2. frame.alpha = 1
  3. frame:SetScript("OnUpdate", function(self, elapsed)
  4.     self:SetAlpha(self.alpha)
  5.     self.alpha = self.alpha - elapsed*self.mult
  6.     if self.alpha < 0 and self.mult > 0 then
  7.         self.mult = self.mult*-1
  8.         self.alpha = 0
  9.     elseif self.alpha > 1 and frame.mult < 1 then
  10.         self.mult = self.mult*-1
  11.     end
  12. end)
__________________
  Reply With Quote
01-26-10, 03:39 PM   #6
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
You'll want to hide the frame doing the OnUpdate when it's done, else you'll be needlessly using CPU.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
01-26-10, 03:42 PM   #7
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Xrystal View Post
*book marks post so I can come back and ste... erm I mean utilise the code*
Feel free to use it(I'm pretty amazed I came up with the mult*-1 trick lol).
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-26-10, 03:46 PM   #8
Amenity
Guest
Posts: n/a
Originally Posted by nightcracker View Post
Feel free to use it(I'm pretty amazed I came up with the mult*-1 trick lol).
Wow guys...thank you for the quick responses! Working perfectly now.

(Also bookmarking as a reference page)
  Reply With Quote
01-26-10, 03:55 PM   #9
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Amenity View Post
Wow guys...thank you for the quick responses! Working perfectly now.

(Also bookmarking as a reference page)
If this is going to be a reference, then I'll add something new, a variable to determine speed of the pulse in seconds(don't set it to 0, dividing by 0 FTW):

lua Code:
  1. local speed = 1
  2. frame.mult = 1
  3. frame.alpha = 1
  4. frame:SetScript("OnUpdate", function(self, elapsed)
  5.     elapsed = elapsed*(1/speed)
  6.     self:SetAlpha(self.alpha)
  7.     self.alpha = self.alpha - elapsed*self.mult
  8.     if self.alpha < 0 and self.mult > 0 then
  9.         self.mult = self.mult*-1
  10.         self.alpha = 0
  11.     elseif self.alpha > 1 and frame.mult < 1 then
  12.         self.mult = self.mult*-1
  13.     end
  14. end)
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-26-10, 04:08 PM   #10
Amenity
Guest
Posts: n/a
Originally Posted by Torhal View Post
You'll want to hide the frame doing the OnUpdate when it's done, else you'll be needlessly using CPU.
Just as a quick test, I set it to pulse my Minimap (lulz) and it's using so little CPU time that it could simply just be an error. By comparison, the tool I used to measure this used 24 times as much CPU time as the pulse addon.

Originally Posted by nightcracker View Post
If this is going to be a reference, then I'll add something new, a variable to determine speed of the pulse in seconds(don't set it to 0, dividing by 0 FTW):

lua Code:
  1. local speed = 1
  2. frame.mult = 1
  3. frame.alpha = 1
  4. frame:SetScript("OnUpdate", function(self, elapsed)
  5.     elapsed = elapsed*(1/speed)
  6.     self:SetAlpha(self.alpha)
  7.     self.alpha = self.alpha - elapsed*self.mult
  8.     if self.alpha < 0 and self.mult > 0 then
  9.         self.mult = self.mult*-1
  10.         self.alpha = 0
  11.     elseif self.alpha > 1 and frame.mult < 1 then
  12.         self.mult = self.mult*-1
  13.     end
  14. end)
Hm, I'll have to give that a go. In the previous incarnation, I was just adjusting the value of frame.mult to adjust the speed.
  Reply With Quote
01-26-10, 04:28 PM   #11
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Amenity View Post
Just as a quick test, I set it to pulse my Minimap (lulz) and it's using so little CPU time that it could simply just be an error. By comparison, the tool I used to measure this used 24 times as much CPU time as the pulse addon.



Hm, I'll have to give that a go. In the previous incarnation, I was just adjusting the value of frame.mult to adjust the speed.
Woops, I actually made a mistake in the script, and you are right, it should be enough to change frame.mult.

FINAL VERSION(I hope):

lua Code:
  1. frame.mult = 1
  2. frame.alpha = 1
  3. frame:SetScript("OnUpdate", function(self, elapsed)
  4.     self:SetAlpha(self.alpha)
  5.     self.alpha = self.alpha - elapsed*self.mult
  6.     if self.alpha < 0 and self.mult > 0 then
  7.         self.mult = self.mult*-1
  8.         self.alpha = 0
  9.     elseif self.alpha > 1 and self.mult < 0 then
  10.         self.mult = self.mult*-1
  11.     end
  12. end)
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.

Last edited by nightcracker : 02-06-10 at 08:32 AM.
  Reply With Quote
01-26-10, 06:44 PM   #12
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Originally Posted by Amenity View Post
Just as a quick test, I set it to pulse my Minimap (lulz) and it's using so little CPU time that it could simply just be an error. By comparison, the tool I used to measure this used 24 times as much CPU time as the pulse addon.
<snip>
Profilers will use a huge amount of CPU because of everything they have to do for measuring every AddOn you're running.

Anyway: Saying "it's using so little CPU" and then ignoring that is rather dismissive - people running 200+ AddOns which do this are suddenly using a metric ****-ton of CPU for no reason whatsoever. If you aren't currently generating a pulse, the frame should be hidden so its OnUpdate will not be run.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
01-26-10, 07:13 PM   #13
Amenity
Guest
Posts: n/a
Originally Posted by Torhal View Post
Profilers will use a huge amount of CPU because of everything they have to do for measuring every AddOn you're running.
Well, yeah. Just using it as a frame of reference.

Anyway: Saying "it's using so little CPU" and then ignoring that is rather dismissive - people running 200+ AddOns which do this are suddenly using a metric ****-ton of CPU for no reason whatsoever. If you aren't currently generating a pulse, the frame should be hidden so its OnUpdate will not be run.
I agree that this is good coding practice, yes. My point is that for 99% of the people out there, having a frame that pulses indefinitely is not going to be an issue (as is the intent of my usage).

**EDIT:

Just discovered something while we're on the topic of performance that I thought should be part of this:

You can set this thing to absolutely ridiculous speeds (I've personally tried it up to 150, WELL beyond my refresh rate, just to see what would happen). The only upper limit here is your computers' crash threshold. BE CAREFUL WHEN DOING THIS. The reason it was/is using so little CPU is because apparently Blizz uses GPU time for this. On my machine at 1440x900, Ultra settings w/ some cVar tweaks, fullscreen windowed, and a frame pulsing at 150, I hit around 50%-ish GPU usage on average (peak no higher than 65%) with dual 5700-series ATis.

At any "normal" speed (say, 0.3 to 10.0 at the fastest) it's negligible, though. I'm also pulsing a pretty big frame (1024x256). Really, if you need something faster than 10...don't. You're gonna give kids seizures.

Last edited by Amenity : 01-27-10 at 01:32 AM.
  Reply With Quote
02-05-10, 09:27 AM   #14
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Check blackbox (http://wow.curseforge.com/addons/blackboxlua/)

You may want to look at the animation system.

Blizzard uses this kind of stuff for the Glyph window.

http://wowprogramming.com/utils/xmlb...rd_GlyphUI.lua

Look out for "PulseGlow".

The cool stuff is that you can "PLAY" and "STOP" those animations as you wish.

I cannot recommend onUpdate stuff anymore, the animations are much more valuable imo since they eat nearly no cpu usage instead of those nasty onupdates.

I'm using the animation system for rFrameRotater. Since you can "LOOP" animations it will start and never stop. (If you say so.)

Very simple rotate-function that I'm using to rotate stuff.
Code:
  function a:rotateme(texture,width,height,scale,anchorframe,framelevel,texr,texg,texb,alpha,duration,side,blendmode,point,pointx,pointy)

    local h = CreateFrame("Frame",nil,anchorframe)
    h:SetHeight(height)
    h:SetWidth(width)             
    h:SetPoint(point,pointx,pointy)
    h:SetScale(scale)
    h:SetFrameLevel(framelevel)
  
    local t = h:CreateTexture()
    t:SetAllPoints(h)
    t:SetTexture("Interface\\AddOns\\rFramerotater\\media\\"..texture)
    t:SetBlendMode(blendmode)
    t:SetVertexColor(texr,texg,texb,alpha)
    h.t = t
    
    local ag = h:CreateAnimationGroup()
    h.ag = ag
    
    local a1 = h.ag:CreateAnimation("Rotation")
    if side == 0 then
      a1:SetDegrees(360)
    else
      a1:SetDegrees(-360)
    end
    a1:SetDuration(duration)
    h.ag.a1 = a1
    
    h.ag:Play()
    h.ag:SetLooping("REPEAT")  

  end

So sth. like this could be it, don't know the exact syntax

http://wowprogramming.com/docs/widgets/Alpha/SetChange

Code:
    local h = CreateFrame("Frame",nil,UIParent)
    h:SetHeight(100)
    h:SetWidth(100)             
    h:SetPoint("CENTER",0,0)
    h:SetScale(1)
    h:SetFrameLevel(1)
  
    local t = h:CreateTexture()
    t:SetAllPoints(h)
    t:SetTexture(1,1,1,1)
    h.t = t

    local ag = h:CreateAnimationGroup()
    h.ag = ag
    
    local a1 = h.ag:CreateAnimation("Alpha")
    --go from 1 to 0
    a1:SetChange(-1)
    a1:SetDuration(5)

    local a2 = h.ag:CreateAnimation("Alpha")
    --go from 0 to 1
    a2:SetChange(1)
    a2:SetDuration(5)

    h.ag.a1 = a1
    h.ag.a2 = a2
    
    h.ag:Play()
    h.ag:SetLooping("REPEAT")
how can this lua code stuff be done?

With the animation system you can do this in just one animationgroup.

- move (translate)
- rotate
- scale
- change alpha

You can even add a path the frame will move on.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 02-05-10 at 10:00 AM.
  Reply With Quote
02-05-10, 11:28 AM   #15
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
Cool, I was wondering the use for animation barring prettifying movement around the screen. But if it's the better option than onUpdate it may be the way to go.
__________________
  Reply With Quote
02-05-10, 12:01 PM   #16
Nemnacill
A Kobold Labourer
AddOn Author - Click to view addons
Join Date: Dec 2005
Posts: 1
I'm not sure about how it would work in lua, but any pulsating effect can be achieved with a simple sinus function.

pseudo code:
var sequence = 0;
sequence ++;
AlphaProperty = sin(sequence);

this would result in the AlphaProperty going from 0 to 1, then from 1 to 0 and so on. google 'math sin lua' for more info

Good luck
  Reply With Quote
02-05-10, 01:01 PM   #17
Hati-EK
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 20
http://www.wowwiki.com/API_UIFrameFlash

UIFrameFlash(frame, fadeInTime, fadeOutTime, flashDuration, showWhenDone, flashInHoldTime, flashOutHoldTime)

if you call it more than once(while it's active) - you'll need something that blocks this calls: (atleast for UIFrameFadeIn it makes it nearly go to infinite time - if called unnecessary times (even if you use :GetAlpha()) - you would need something like spareTime=totaltime-(GetAlpha()/totaltime))

(Note this is for UIFrameFadeIn, and not UIFrameFlash)
Code:
local fade_in = {}
local fade_out= {}

--improved UIFrameFadeIn(frame,time,startAlpha,endAlpha,~type)
function lib:UIFrameFadeIn(frame,t,startAlpha,endAlpha,typ)
	local now=GetTime()
	local fname = frame:GetName()
	if typ=='in' then
		local IsInTable,num = self:inTable(fname,fade_in)
		if not IsInTable then
			UIFrameFadeIn(frame,t,startAlpha,endAlpha)
			--tinsert(frame,{icon_name,endTime})
			tinsert(fade_in,{fname,GetTime()+t})
		else
			if now>fade_in[num][2] then
				tremove(fade_in,num)
			end
		end
	elseif typ=='out' then
		local IsInTable,num = self:inTable(fname,fade_out)
		if not IsInTable then
			UIFrameFadeIn(frame,t,startAlpha,endAlpha)
			tinsert(fade_out,{fname,GetTime()+t})
		else
			if now>fade_in[num][2] then
				tremove(fade_out,num)
			end
		end
	end
end
source: LibCooldownIcons-1.0
(Note inTable returns boolean,index of value in table(numeric))

dunno if you can 'calculate' the Pulse-time or want to fix it to some number

Last edited by Hati-EK : 02-05-10 at 08:03 PM.
  Reply With Quote
02-06-10, 12:25 AM   #18
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Nemnacill View Post
I'm not sure about how it would work in lua, but any pulsating effect can be achieved with a simple sinus function.

pseudo code:
var sequence = 0;
sequence ++;
AlphaProperty = sin(sequence);

this would result in the AlphaProperty going from 0 to 1, then from 1 to 0 and so on. google 'math sin lua' for more info

Good luck
True, but that would require an onupdate function. And an onupdate function gets called at every frame. So if you run at 120 FPS your calling a sin function 120 times per second. Not that efficient as running my way(look above).
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
02-06-10, 02:59 AM   #19
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Originally Posted by nightcracker View Post
<snip>So if you run at 120 FPS your calling a sin function 120 times per second.<snip>
Not true.

Code:
do
	local last_update = 0
	local updater = CreateFrame("Frame", nil, UIParent)

	updater:Hide()
	updater:SetScript("OnUpdate",
			  function(self, elapsed)
				  last_update = last_update + elapsed

				  if last_update >= 0.25 then
					  RunSinFunction()
					  last_update = 0
				  end
			  end)
end
That executes the sin function every quarter of a second.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
02-06-10, 04:25 AM   #20
Hati-EK
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 20
Originally Posted by Torhal View Post
Not true.

Code:
do
	local last_update = 0
	local updater = CreateFrame("Frame", nil, UIParent)

	updater:Hide()
	updater:SetScript("OnUpdate",
			  function(self, elapsed)
				  last_update = last_update + elapsed

				  if last_update >= 0.25 then
					  RunSinFunction()
					  last_update = 0
				  end
			  end)
end
That executes the sin function every quarter of a second.
Code:
last_update = last_update -0.25
is more precise and does not skip seconds (even if this would just be some miliseconds) than reseting the whole value to 0 - (ie last_update could be 0.25182965 - so you would missing 0.00182965 - if you have this ~1000 times there's something missing

Last edited by Hati-EK : 02-06-10 at 04:27 AM.
  Reply With Quote

WoWInterface » Developer Discussions » Graphics Help » "Pulsating" effect...what's the proper method?

Thread Tools
Display Modes

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