Thread Tools Display Modes
10-10-11, 08:17 PM   #1
thelurkerbelow
A Murloc Raider
Join Date: May 2010
Posts: 5
Animation system/translation OnFinished issue

(Originally posted on the official WoW UI forum, but that place seems pretty dead.)

I have a textured frame, and I want to move it using an animation translation and have it remain in the location it's translated to until the animation function is called to move it again. The way I'm currently doing this is by having the translation move the frame by animamount, and then using an OnFinished script to SetPoint the frame when the animation is finished, like so:

Code:
TAG = AnimTestFrame:CreateAnimationGroup()
anim1 = TAG:CreateAnimation("Translation")
anim1:SetOffset(animamount,0)
anim1:SetDuration(1)
anim1:SetScript("OnFinished", function()
	local _,_,_,frameXpos = frame:GetPoint()
	frame:SetPoint("Center", frameXpos+animamount, 0)
end)
However, there's an issue with this. It seems that when the frame point is set it's still carrying over the translation offset for a split second. For example, let's say I'm translating the frame 50px to the right. It moves the 50px to the right, then the frame point is set 50px to the right before the animation has reset (making it appear 100px to the right for just a split second), then the animation resets and the frame is in the correct place 50px to the right of where it started. So basically, the OnFinished stuff seems to be firing before the animation is actually finished, and it makes the end result look like spastic jerky crap.

Really at a loss as to what to do. Any help is appreciated!
  Reply With Quote
10-11-11, 01:13 PM   #2
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
None of the things I tried completely eliminated that jump at the end of the animation, but I did get pretty close. First, Translation offsets aren't scaled, so you have to scale them manually or the animation won't end up exactly where you meant it to:
lua Code:
  1. anim1:SetOffset(animamount * AnimTestFrame:GetEffectiveScale(),0)

The frame will still twitch though. The animation's OnFinished script seems to fire before its effects get removed, so you can wait a few frames after OnFinished and then move your frame. You can do that by setting an OnUpdate script on your frame and having the OnUpdate function unregister itself after it gets called the desired number of times; I found a 2-frame delay worked best. Trouble is, fontstrings will still twitch noticeably even with this, and the required delay might depend on framerate.
  Reply With Quote
10-11-11, 02:02 PM   #3
thelurkerbelow
A Murloc Raider
Join Date: May 2010
Posts: 5
Originally Posted by Saiket View Post
Translation offsets aren't scaled, so you have to scale them manually or the animation won't end up exactly where you meant it to:
lua Code:
  1. anim1:SetOffset(animamount * AnimTestFrame:GetEffectiveScale(),0)
Yeah, there's some other odd behaviour there with scaling and end delays as well. This seems to sort that out. I was kinda hoping that end delays were applied after the animation finishes and needed to be manually set to 0 for some reason but that's not the case either.

Originally Posted by Saiket View Post
The frame will still twitch though. The animation's OnFinished script seems to fire before its effects get removed, so you can wait a few frames after OnFinished and then move your frame. You can do that by setting an OnUpdate script on your frame and having the OnUpdate function unregister itself after it gets called the desired number of times; I found a 2-frame delay worked best. Trouble is, fontstrings will still twitch noticeably even with this, and the required delay might depend on framerate.
It's massively dependent on framerate. For example, if I use:

Code:
/console SET MaxFPS "20"
and then run my little frame moving thingie, there's no visible jump at all (I'm guessing those stray frames end up getting dropped entirely). Thinking framerate limitation might be my solution, I popped this into the mix:

Code:
anim1:SetMaxFramerate(20)
But when I do that, the SetPoint stops working and the animation ends with the frame snapping back to its original location. **EDIT** Actually, the entire OnFinished script stops working. Doubly

I've seen addons where people have successfully animated moving frames before, so I know this is possible. Is this just an incorrect approach from the get-go? I've seen some anim system examples where they use two duplicate frames (one a "mover" and one a "static", swapping between hidden and show states to give the appearance of one moving frame) but for my application that's not practical (my end result is going to be working with a single frame generated by another addon, sort of like a plug-in).

Last edited by thelurkerbelow : 10-11-11 at 02:08 PM. Reason: Clarification of SetMaxFramerate weirdness
  Reply With Quote
10-11-11, 02:11 PM   #4
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
If you're not going to run the animation too frequently, I'd suggest just moving the frame from an OnUpdate handler and avoiding the animation system altogether. It'd certainly be easier to maintain than a bunch of workarounds for flaws in animations.
  Reply With Quote
10-11-11, 02:30 PM   #5
thelurkerbelow
A Murloc Raider
Join Date: May 2010
Posts: 5
Originally Posted by Saiket View Post
If you're not going to run the animation too frequently, I'd suggest just moving the frame from an OnUpdate handler and avoiding the animation system altogether. It'd certainly be easier to maintain than a bunch of workarounds for flaws in animations.
That's something I was hoping to avoid. In the end there are going to be 4 total moving frames and they'll be moving very frequently when in combat. If it can't be helped it can't be helped I suppose, but it's a disappointing limitation that the animation system can't be used to animate stuff to places and have it stay there. Maybe I'll try playing around with ludicrously long end delays. **EDIT** That's a dead-end street too.

Last edited by thelurkerbelow : 10-11-11 at 02:45 PM.
  Reply With Quote
03-15-14, 02:26 PM   #6
Foxthorn
A Deviate Faerie Dragon
 
Foxthorn's Avatar
Join Date: Apr 2009
Posts: 16
I realize that this will be a massive necro, but this is the only place on the internet where this issue (which still exists in WoW 5.4.7 in 2014) has actually been discussed. There's a couple other forum threads on other sites, but none of them got any responses. Putting the answer here should help anyone else who's encountered this problem and save them a lot of time and headache.

I seem to have figured out how to eliminate the last-frame flicker in OnFinished. All you have to do is attach an OnUpdate handler to the frame, then in that handler, set your new frame position and remove the handler. Essentially you're just waiting a single render frame to move the frame instead of moving it during the animation.

Example code:

Code:
animation.translate:SetDuration(3) -- 3 second duration
animation.translate:SetOffset(100, 0) -- Move 100 pixels
animation:Play() -- Play the animation

animation:SetScript("OnFinished", function(self) -- This event fires one frame too early, while it's still being animated!
    self:SetScript("OnUpdate", function(self) -- Add an OnUpdate handler, that runs only once, next frame, then is immediately removed
        self:SetPoint("CENTER", relativeToFrame, "CENTER", 100, 0) -- Set the frame's new position, or whatever you need to do after the animation
        self:SetScript("OnUpdate", nil) -- Remove the OnUpdate handler since we're now done with it
    end)
end)
  Reply With Quote
03-15-14, 02:37 PM   #7
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,860
Each time your OnFinished handler runs, it creates a new anonymous function to run in OnUpdate. If this will be happening more than once per game session, you should either create the function outside of the :SetScript call and assign OnUpdate to that function or nil, or create an invisible frame to run the OnUpdate on. (In OnFinished, show the frame, which starts its OnUpdate, and then hide the frame after it does that last adjustment.)
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
03-15-14, 07:54 PM   #8
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
In case you need a code example of what Seerah said:

Code:
-- Define the function to be used as an OnUpdate handler just once:
function animation:OnUpdate(elapsed)
        self:GetParent():SetPoint("CENTER", relativeToFrame, "CENTER", 100, 0) -- Set the frame's new position, or whatever you need to do after the animation
        self:SetScript("OnUpdate", nil) -- Remove the OnUpdate handler since we're now done with it
end

animation:SetScript("OnFinished", function(self) -- This event fires one frame too early, while it's still being animated!
    self:SetScript("OnUpdate", frame.OnUpdate) -- Add the OnUpdate handler, that runs only once, next frame, then is immediately removed
end)
Also, I'm guessing that what you posted was not your real code (seriously, writing some fake snippet is more work than just copying and pasting your real code, FFS), since your SetPoint call is acting on self, which in that scope refers to the animation itself, not the animation's parent frame, so you should have been getting an error and no functionality. I've fixed the above example so it can actually be used as-is.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.

Last edited by Phanx : 03-15-14 at 07:57 PM.
  Reply With Quote
03-16-14, 08:58 AM   #9
Foxthorn
A Deviate Faerie Dragon
 
Foxthorn's Avatar
Join Date: Apr 2009
Posts: 16
Originally Posted by Phanx View Post
Also, I'm guessing that what you posted was not your real code (seriously, writing some fake snippet is more work than just copying and pasting your real code, FFS), since your SetPoint call is acting on self, which in that scope refers to the animation itself, not the animation's parent frame, so you should have been getting an error and no functionality. I've fixed the above example so it can actually be used as-is.
Yeah, when I was tidying up my code for an example (not written from scratch, just genericizing variable names and the like) I ended up removing a line that was essential for making it work. Sorry!

Anyway, while we're on the subject of animations not working right, is there a reason why animation:GetSmoothProgress() apparently doesn't exist? It shows up on all the usual places, including wowprogramming.com, but I just get "attempt to call method 'GetSmoothProgress' (a nil value)" whenever I attempt to use it. I had to write a quick function to convert a linear progress (GetProgress()) to an ease-in-out progress, but it won't take delays into effect, so it's not perfect:

Code:
function LinearToEaseInOut(x)
    return x*x*(3-2*x)
end
EDIT: GetSmoothProgress() only works on individual Animations, not the AnimationGroup. So use animation.translation:GetSmoothProgress() instead, for example.

Last edited by Foxthorn : 03-16-14 at 05:38 PM. Reason: Answered my own question
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Animation system/translation OnFinished issue


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