Generic notify system from Storyboards
Posted: 05 Aug 2020, 19:29
Hi guys,
We have a "notify" system which allows us to trigger notifies from Storyboards. The notify is basically a custom class which derives from FrameworkElement and has a boolean dependency property called "Trigger". It is false by default, and we trigger it to true during a storyboard. It's very similar to this idea: viewtopic.php?t=1844
Because these triggers may be called multiple times, I have to reset the value to false in order to get the animation to change the value again, e.g. if we play the storyboard 2 times in a row I need to reset it to false after the first trigger to get the 2nd trigger to retrigger it again.
This is where the m_fLastTriggerRequest comes in. It's a bit of a hack, but basically each time I get the dependency changed event callback through, I set the value to false, clear the animation, and only actually call InternalTrigger() if there's been substantial time between the last time it was set:
Now... This is quite a bit of a hack, but it works. I have a single keyframe in a storyboard which sets Trigger to true at the time we want to trigger the animation. We get the dependency property changed event through, because we haven't tried to trigger it yet the elapsed time is greater than 0.1 seconds so we trigger it. We then set the value back to false and clear the animation.
Then, each time we update the animation clocks the animation tries to change the value back to true again. However, each time this happens, we get the callback but we don't call InternalTrigger because the time elapsed is shorter than 0.1 seconds.
This goes on until the storyboard ends. We can then retrigger the storyboard at a later stage, and it'll retrigger the notify again at the desired time because the time elapsed will be > 0.1 seconds.
This works until we create a looping animation. The storyboard never ends and TriggerProperty always wants to be set to true by the animation.
Is there any way around this? Are there any alternative ways I can solve this? My only solution I can think of at the moment is to add another keyframe at the end of the storyboard to set Trigger dependency property back to false, so the animation has enough time to stop calling my DependencyChangedEvent delegate so the elapsed time can get > 0.1 seconds. However, this means that I have to tell all our designers / artists about this caveat, and it's not very intuitive. Is there a better way around all this? I've searched online and I can't see anyone trying to do anything similar to this using WPF / XAML, but this is common in games, we often want to trigger VFX / SFX from the UI, and it's nice to be able to just trigger things at a certain point in the animation.
Cheers,
-Steven
We have a "notify" system which allows us to trigger notifies from Storyboards. The notify is basically a custom class which derives from FrameworkElement and has a boolean dependency property called "Trigger". It is false by default, and we trigger it to true during a storyboard. It's very similar to this idea: viewtopic.php?t=1844
Code: Select all
class CustomTrigger : public Noesis::FrameworkElement {
private:
static void OnCustomTriggerPropertyValueChanged(Noesis::DependencyObject *pDependcyObject, const Noesis::DependencyPropertyChangedEventArgs &args);
void InternalTrigger();
f32 m_fLastTriggerRequest;
public:
///////////////////////////////////////////////////
// Dependency Property Interface
static const Noesis::DependencyProperty *TriggerProperty;
bool GetTrigger() const;
void SetTrigger(bool bTrigger);
NS_DECLARE_REFLECTION(CustomTrigger, Noesis::FrameworkElement);
};
This is where the m_fLastTriggerRequest comes in. It's a bit of a hack, but basically each time I get the dependency changed event callback through, I set the value to false, clear the animation, and only actually call InternalTrigger() if there's been substantial time between the last time it was set:
Code: Select all
/*static */void CustomTrigger::OnCustomTriggerPropertyValueChanged(DependencyObject *pDependcyObject, const DependencyPropertyChangedEventArgs &args)
{
if (args.prop == TriggerProperty)
{
SelfClass* pThis = DynamicCast<SelfClass*>(pDependcyObject);
if (pThis != nullptr)
{
if ((*(bool*)args.newValue) == true)
{
f32 fCurrentRequest = GetCurrentTime();
// Test for some certain elapsed time
if ((fCurrentRequest - pThis->m_fLastTriggerRequest) > 0.1f)
{
pThis->InternalTrigger();
}
pThis->m_fLastTriggerRequest = fCurrentRequest;
pThis->ClearAnimation<bool>(TriggerProperty);
pThis->SetTrigger(false);
}
}
}
}Then, each time we update the animation clocks the animation tries to change the value back to true again. However, each time this happens, we get the callback but we don't call InternalTrigger because the time elapsed is shorter than 0.1 seconds.
This goes on until the storyboard ends. We can then retrigger the storyboard at a later stage, and it'll retrigger the notify again at the desired time because the time elapsed will be > 0.1 seconds.
This works until we create a looping animation. The storyboard never ends and TriggerProperty always wants to be set to true by the animation.
Is there any way around this? Are there any alternative ways I can solve this? My only solution I can think of at the moment is to add another keyframe at the end of the storyboard to set Trigger dependency property back to false, so the animation has enough time to stop calling my DependencyChangedEvent delegate so the elapsed time can get > 0.1 seconds. However, this means that I have to tell all our designers / artists about this caveat, and it's not very intuitive. Is there a better way around all this? I've searched online and I can't see anyone trying to do anything similar to this using WPF / XAML, but this is common in games, we often want to trigger VFX / SFX from the UI, and it's nice to be able to just trigger things at a certain point in the animation.
Cheers,
-Steven