2026-03-14 03:13:10 -04:00
using System ;
using System.Collections.Generic ;
2025-06-03 02:42:28 -04:00
#if ! ( UNITY_DASHBOARD_WIDGET | | UNITY_WEBPLAYER | | UNITY_WII | | UNITY_WIIU | | UNITY_NACL | | UNITY_FLASH | | UNITY_BLACKBERRY ) // Disable under unsupported platforms.
#if ! UNITY_2019_1_OR_NEWER
#define AK_ENABLE_TIMELINE
#endif
#if AK_ENABLE_TIMELINE
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
Technology released in source code form as part of the game integration package .
The content of this file may not be used without valid licenses to the
AUDIOKINETIC Wwise Technology .
Note that the use of the game engine is subject to the Unity ( R ) Terms of
Service at https : //unity3d.com/legal/terms-of-service
License Usage
Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
this file in accordance with the end user license agreement provided with the
software or , alternatively , in accordance with the terms contained
in a written agreement between you and Audiokinetic Inc .
2026-03-14 03:13:10 -04:00
Copyright ( c ) 2025 Audiokinetic Inc .
2025-06-03 02:42:28 -04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/// @brief Defines the behavior of a \ref AkTimelineEventPlayable within a \ref AkTimelineEventTrack.
/// \sa
/// - \ref AkTimelineEventTrack
/// - \ref AkTimelineEventPlayable
public class AkTimelineEventPlayableBehavior : UnityEngine . Playables . PlayableBehaviour
{
private float currentDuration = - 1f ;
private float currentDurationProportion = 1f ;
2026-03-14 03:13:10 -04:00
private bool eventIsPlaying
{
get
{
return playingId ! = 0 ;
}
}
2025-06-03 02:42:28 -04:00
private float previousEventStartTime ;
2026-03-14 03:13:10 -04:00
private uint playingId = 0 ;
2025-06-03 02:42:28 -04:00
private const uint CallbackFlags = ( uint ) ( AkCallbackType . AK_EndOfEvent | AkCallbackType . AK_Duration ) ;
private void CallbackHandler ( object in_cookie , AkCallbackType in_type , AkCallbackInfo in_info )
{
if ( in_type = = AkCallbackType . AK_EndOfEvent )
{
2026-03-14 03:13:10 -04:00
StopEvent ( ) ;
2025-06-03 02:42:28 -04:00
}
else if ( in_type = = AkCallbackType . AK_Duration )
{
var estimatedDuration = ( in_info as AkDurationCallbackInfo ) . fEstimatedDuration ;
currentDuration = estimatedDuration * currentDurationProportion / 1000f ;
}
}
#if UNITY_EDITOR
private static bool CanPostEvents
{
get { return UnityEditor . SessionState . GetBool ( "AkTimelineEventPlayableBehavior.CanPostEvents" , true ) ; }
set { UnityEditor . SessionState . SetBool ( "AkTimelineEventPlayableBehavior.CanPostEvents" , value ) ; }
}
[UnityEditor.InitializeOnLoadMethod]
private static void DetermineCanPostEvents ( )
{
2026-03-14 03:13:10 -04:00
if ( UnityEditor . AssetDatabase . IsAssetImportWorkerProcess ( ) )
{
return ;
}
2025-06-03 02:42:28 -04:00
UnityEditor . Compilation . CompilationPipeline . assemblyCompilationFinished + = ( string text , UnityEditor . Compilation . CompilerMessage [ ] messages ) = >
{
if ( ! UnityEditor . EditorApplication . isPlaying )
2026-03-14 03:13:10 -04:00
{
CanPostEvents = true ;
}
2025-06-03 02:42:28 -04:00
} ;
UnityEditor . EditorApplication . playModeStateChanged + = ( UnityEditor . PlayModeStateChange playMode ) = >
{
if ( playMode = = UnityEditor . PlayModeStateChange . ExitingEditMode )
2026-03-14 03:13:10 -04:00
{
CanPostEvents = true ;
}
if ( playMode = = UnityEditor . PlayModeStateChange . EnteredEditMode )
{
2025-06-03 02:42:28 -04:00
CanPostEvents = true ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
} ;
}
#endif
[System.Flags]
private enum Actions
{
None = 0 ,
Playback = 1 < < 0 ,
Retrigger = 1 < < 1 ,
DelayedStop = 1 < < 2 ,
Seek = 1 < < 3 ,
FadeIn = 1 < < 4 ,
FadeOut = 1 < < 5
}
private Actions requiredActions ;
private const int scrubPlaybackLengthMs = 100 ;
public AK . Wwise . Event akEvent ;
public float eventDurationMax ;
public float eventDurationMin ;
public float blendInDuration ;
public float blendOutDuration ;
public float easeInDuration ;
public float easeOutDuration ;
public AkCurveInterpolation blendInCurve ;
public AkCurveInterpolation blendOutCurve ;
public UnityEngine . GameObject eventObject ;
public bool retriggerEvent ;
public bool StopEventAtClipEnd ;
public bool PrintDebugInformation = false ;
private bool IsScrubbing ( UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info )
{
#if UNITY_EDITOR
if ( ! UnityEngine . Application . isPlaying )
{
return info . evaluationType = = UnityEngine . Playables . FrameData . EvaluationType . Evaluate ;
}
#endif
var previousTime = UnityEngine . Playables . PlayableExtensions . GetPreviousTime ( playable ) ;
var currentTime = UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
var computedDelta = System . Math . Abs ( currentTime - previousTime ) ;
// Unfortunately, we can't use info.seekOccurred, because it is always true.
// When time is explicitely set using playable.time, deltaTime is zero, evaluationType is Evaluate, and
// either previous time or current time is non-zero
// However, if time is added to playable.time (for example, playable.time += 1;), evaluationType remains
// Playing.
2026-03-14 03:13:10 -04:00
return ( info . deltaTime = = 0 & & ( previousTime > = 0 | | currentTime > = 0 ) ) | | ( computedDelta > info . deltaTime ) ;
2025-06-03 02:42:28 -04:00
}
void PrintInfo ( string FunctionName , UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info )
{
if ( PrintDebugInformation )
{
var previousTime = UnityEngine . Playables . PlayableExtensions . GetPreviousTime ( playable ) ;
var currentTime = UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
var computedDelta = System . Math . Abs ( currentTime - previousTime ) ;
UnityEngine . Debug . Log ( $"{FunctionName}: prevTime={previousTime}; curTime={currentTime}; computedDelta={computedDelta}; evalType={info.evaluationType}; deltaTime={info.deltaTime}; playState={info.effectivePlayState}; timeHeld={info.timeHeld}; speed={info.effectiveSpeed}; parentSpeed={info.effectiveParentSpeed}" ) ;
}
}
public override void PrepareFrame ( UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info )
{
base . PrepareFrame ( playable , info ) ;
PrintInfo ( "PrepareFrame" , playable , info ) ;
if ( akEvent = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
var shouldPlay = ShouldPlay ( playable ) ;
if ( IsScrubbing ( playable , info ) & & shouldPlay )
{
requiredActions | = Actions . Seek ;
2026-03-14 03:13:10 -04:00
if ( playingId = = 0 )
2025-06-03 02:42:28 -04:00
{
requiredActions | = Actions . Playback ;
#if UNITY_EDITOR
if ( ! UnityEngine . Application . isPlaying )
{
// If we've explicitly set the playhead, only play a small snippet.
requiredActions | = Actions . DelayedStop ;
}
#endif
CheckForFadeInFadeOut ( playable ) ;
}
}
2026-03-14 03:13:10 -04:00
else if ( shouldPlay & & playingId = = 0 & & ( requiredActions & Actions . Playback ) = = 0 )
2025-06-03 02:42:28 -04:00
{
// The clip is playing but the event hasn't been triggered. We need to start the event and jump to the correct time.
requiredActions | = Actions . Retrigger ;
CheckForFadeInFadeOut ( playable ) ;
}
else
{
CheckForFadeOut ( playable , UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ) ;
}
}
private const float alph = 0.05f ;
public override void OnBehaviourPlay ( UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info )
{
PrintInfo ( "OnBehaviourPlay" , playable , info ) ;
base . OnBehaviourPlay ( playable , info ) ;
if ( akEvent = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
2026-03-14 03:13:10 -04:00
if ( ! ShouldPlay ( playable ) )
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
requiredActions | = Actions . Playback ;
if ( IsScrubbing ( playable , info ) )
{
#if UNITY_EDITOR
if ( ! UnityEngine . Application . isPlaying )
{
// If we've explicitly set the playhead, only play a small snippet.
requiredActions | = Actions . DelayedStop ;
}
#endif
}
else if ( GetProportionalTime ( playable ) > alph )
{
// we need to jump to the correct position in the case where the event is played from some non-start position.
requiredActions | = Actions . Seek ;
}
CheckForFadeInFadeOut ( playable ) ;
}
public override void OnBehaviourPause ( UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info )
{
PrintInfo ( "OnBehaviourPause" , playable , info ) ;
base . OnBehaviourPause ( playable , info ) ;
if ( eventObject ! = null & & akEvent ! = null & & StopEventAtClipEnd )
{
StopEvent ( ) ;
}
}
public override void ProcessFrame ( UnityEngine . Playables . Playable playable , UnityEngine . Playables . FrameData info , object playerData )
{
PrintInfo ( "ProcessFrame" , playable , info ) ;
base . ProcessFrame ( playable , info , playerData ) ;
if ( akEvent = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
var obj = playerData as UnityEngine . GameObject ;
if ( obj ! = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
eventObject = obj ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
if ( eventObject = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
if ( ( requiredActions & Actions . Playback ) ! = 0 )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
PlayEvent ( ) ;
2026-03-14 03:13:10 -04:00
}
else if ( ( requiredActions & Actions . Seek ) ! = 0 )
{
2025-06-03 02:42:28 -04:00
SeekToTime ( playable ) ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
2026-03-14 03:13:10 -04:00
if ( ( requiredActions & Actions . Retrigger ) ! = 0 )
{
2025-06-03 02:42:28 -04:00
RetriggerEvent ( playable ) ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
if ( ( requiredActions & Actions . DelayedStop ) ! = 0 )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
StopEvent ( scrubPlaybackLengthMs ) ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
2026-03-14 03:13:10 -04:00
if ( ( requiredActions & Actions . FadeOut ) ! = 0 )
{
2025-06-03 02:42:28 -04:00
TriggerFadeOut ( playable ) ;
2026-03-14 03:13:10 -04:00
}
if ( ( requiredActions & Actions . FadeIn ) ! = 0 )
{
TriggerFadeIn ( playable ) ;
}
2025-06-03 02:42:28 -04:00
requiredActions = Actions . None ;
}
/ * * Check the playable time against the Wwise event duration to see if playback should occur .
* /
private bool ShouldPlay ( UnityEngine . Playables . Playable playable )
{
var previousTime = UnityEngine . Playables . PlayableExtensions . GetPreviousTime ( playable ) ;
var currentTime = UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
#if UNITY_EDITOR
// In editor, do not automatically play the event if the cursor is already in the section.
if ( ! UnityEditor . EditorApplication . isPlaying )
{
if ( previousTime = = 0.0 & & System . Math . Abs ( currentTime - previousTime ) > 1.0 )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return false ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
#endif
2026-03-14 03:13:10 -04:00
if ( previousTime > currentTime )
{
return true ;
}
2025-06-03 02:42:28 -04:00
if ( retriggerEvent )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return true ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
// If max and min duration values from metadata are equal, we can assume a deterministic event.
2026-03-14 03:13:10 -04:00
if ( eventDurationMax . Equals ( eventDurationMin ) & & ! eventDurationMin . Equals ( - 1f ) )
{
2025-06-03 02:42:28 -04:00
return currentTime < eventDurationMax ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
currentTime - = previousEventStartTime ;
2026-03-14 03:13:10 -04:00
var maxDuration = currentDuration . Equals ( - 1f ) ? ( float ) UnityEngine . Playables . PlayableExtensions . GetDuration ( playable ) : currentDuration ;
2025-06-03 02:42:28 -04:00
return currentTime < maxDuration ;
}
private void CheckForFadeInFadeOut ( UnityEngine . Playables . Playable playable )
{
var currentClipTime = UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
if ( blendInDuration > currentClipTime | | easeInDuration > currentClipTime )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
requiredActions | = Actions . FadeIn ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
CheckForFadeOut ( playable , currentClipTime ) ;
}
private void CheckForFadeOut ( UnityEngine . Playables . Playable playable , double currentClipTime )
{
var timeLeft = UnityEngine . Playables . PlayableExtensions . GetDuration ( playable ) - currentClipTime ;
if ( blendOutDuration > = timeLeft | | easeOutDuration > = timeLeft )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
requiredActions | = Actions . FadeOut ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
private void TriggerFadeIn ( UnityEngine . Playables . Playable playable )
{
var currentClipTime = UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
var fadeDuration = UnityEngine . Mathf . Max ( easeInDuration , blendInDuration ) - currentClipTime ;
if ( fadeDuration > 0 )
{
2026-03-14 03:13:10 -04:00
AkUnitySoundEngine . ExecuteActionOnPlayingID ( AkActionOnEventType . AkActionOnEventType_Pause , playingId , 0 , blendInCurve ) ;
AkUnitySoundEngine . ExecuteActionOnPlayingID ( AkActionOnEventType . AkActionOnEventType_Resume , playingId , ( int ) ( fadeDuration * 1000 ) , blendInCurve ) ;
2025-06-03 02:42:28 -04:00
}
}
private void TriggerFadeOut ( UnityEngine . Playables . Playable playable )
{
var fadeDuration = UnityEngine . Playables . PlayableExtensions . GetDuration ( playable ) - UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
2026-03-14 03:13:10 -04:00
AkUnitySoundEngine . ExecuteActionOnPlayingID ( AkActionOnEventType . AkActionOnEventType_Stop , playingId , ( int ) ( fadeDuration * 1000 ) , blendOutCurve ) ;
2025-06-03 02:42:28 -04:00
}
private void StopEvent ( int transition = 0 )
{
2026-03-14 03:13:10 -04:00
if ( playingId = = 0 )
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
2026-03-14 03:13:10 -04:00
AkUnitySoundEngine . ExecuteActionOnPlayingID ( AkActionOnEventType . AkActionOnEventType_Stop , playingId ) ;
playingId = 0 ;
2025-06-03 02:42:28 -04:00
}
private bool PostEvent ( )
{
#if UNITY_EDITOR
if ( ! CanPostEvents )
{
2026-03-14 03:13:10 -04:00
playingId = AkUnitySoundEngine . AK_INVALID_PLAYING_ID ;
2025-06-03 02:42:28 -04:00
}
#endif
2026-03-14 03:13:10 -04:00
if ( playingId = = 0 )
2025-06-03 02:42:28 -04:00
{
2026-03-14 03:13:10 -04:00
playingId = akEvent . Post ( eventObject , CallbackFlags , CallbackHandler , null ) ;
return playingId ! = 0 ;
2025-06-03 02:42:28 -04:00
}
2026-03-14 03:13:10 -04:00
return false ;
2025-06-03 02:42:28 -04:00
}
private void PlayEvent ( )
{
if ( ! PostEvent ( ) )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
currentDurationProportion = 1f ;
previousEventStartTime = 0f ;
}
private void RetriggerEvent ( UnityEngine . Playables . Playable playable )
{
if ( ! PostEvent ( ) )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
currentDurationProportion = 1f - SeekToTime ( playable ) ;
previousEventStartTime = ( float ) UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) ;
}
private float GetProportionalTime ( UnityEngine . Playables . Playable playable )
{
// If max and min duration values from metadata are equal, we can assume a deterministic event.
if ( eventDurationMax = = eventDurationMin & & eventDurationMin ! = - 1f )
{
// If the timeline clip has length greater than the event duration, we want to loop.
return ( float ) UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) % eventDurationMax / eventDurationMax ;
}
var currentTime = ( float ) UnityEngine . Playables . PlayableExtensions . GetTime ( playable ) - previousEventStartTime ;
2026-03-14 03:13:10 -04:00
if ( currentTime < 0 )
{
return - 1f ;
}
2025-06-03 02:42:28 -04:00
var maxDuration = currentDuration = = - 1f ? ( float ) UnityEngine . Playables . PlayableExtensions . GetDuration ( playable ) : currentDuration ;
// If the timeline clip has length greater than the event duration, we want to loop.
return currentTime % maxDuration / maxDuration ;
}
// Seek to the current time, taking looping into account.
private float SeekToTime ( UnityEngine . Playables . Playable playable )
{
var proportionalTime = GetProportionalTime ( playable ) ;
if ( proportionalTime > = 1f ) // Avoids Wwise "seeking beyond end of event: audio will stop" error.
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return 1f ;
2026-03-14 03:13:10 -04:00
}
if ( proportionalTime < 0f )
{
return 0f ;
}
2025-06-03 02:42:28 -04:00
#if UNITY_EDITOR
if ( ! CanPostEvents )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return proportionalTime ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
#endif
2026-03-14 03:13:10 -04:00
if ( playingId ! = 0 )
{
AkUnitySoundEngine . SeekOnEvent ( akEvent . Id , eventObject , proportionalTime , false , playingId ) ;
}
2025-06-03 02:42:28 -04:00
return proportionalTime ;
}
}
/// @brief A playable asset containing a Wwise event that can be placed within a \ref AkTimelineEventTrack in a timeline.
/// @details Use this class to play Wwise events from a timeline and synchronize them to the animation. Events will be emitted from the GameObject that is bound to the AkTimelineEventTrack.
/// \sa
/// - \ref AkTimelineEventTrack
/// - \ref AkTimelineEventPlayableBehavior
public class AkTimelineEventPlayable : UnityEngine . Playables . PlayableAsset , UnityEngine . Timeline . ITimelineClipAsset
{
public AK . Wwise . Event akEvent = new AK . Wwise . Event ( ) ;
[UnityEngine.SerializeField]
private AkCurveInterpolation blendInCurve = AkCurveInterpolation . AkCurveInterpolation_Linear ;
[UnityEngine.SerializeField]
private AkCurveInterpolation blendOutCurve = AkCurveInterpolation . AkCurveInterpolation_Linear ;
public float eventDurationMax = - 1f ;
public float eventDurationMin = - 1f ;
[System.NonSerialized]
public UnityEngine . Timeline . TimelineClip owningClip ;
[UnityEngine.SerializeField]
private bool retriggerEvent = false ;
public bool UseWwiseEventDuration = true ;
public bool PrintDebugInformation = false ;
[UnityEngine.SerializeField]
private bool StopEventAtClipEnd = true ;
UnityEngine . Timeline . ClipCaps UnityEngine . Timeline . ITimelineClipAsset . clipCaps
{
get { return UnityEngine . Timeline . ClipCaps . Looping | UnityEngine . Timeline . ClipCaps . Blending ; }
}
public override UnityEngine . Playables . Playable CreatePlayable ( UnityEngine . Playables . PlayableGraph graph , UnityEngine . GameObject owner )
{
var playable = UnityEngine . Playables . ScriptPlayable < AkTimelineEventPlayableBehavior > . Create ( graph ) ;
if ( akEvent = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return playable ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
var b = playable . GetBehaviour ( ) ;
b . akEvent = akEvent ;
b . blendInCurve = blendInCurve ;
b . blendOutCurve = blendOutCurve ;
b . PrintDebugInformation = PrintDebugInformation ;
if ( owningClip ! = null )
{
b . easeInDuration = ( float ) owningClip . easeInDuration ;
b . easeOutDuration = ( float ) owningClip . easeOutDuration ;
b . blendInDuration = ( float ) owningClip . blendInDuration ;
b . blendOutDuration = ( float ) owningClip . blendOutDuration ;
}
else
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
b . easeInDuration = b . easeOutDuration = b . blendInDuration = b . blendOutDuration = 0 ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
b . retriggerEvent = retriggerEvent ;
b . StopEventAtClipEnd = StopEventAtClipEnd ;
b . eventObject = owner ;
b . eventDurationMin = eventDurationMin ;
b . eventDurationMax = eventDurationMax ;
2026-03-14 03:13:10 -04:00
if ( eventDurationMin . Equals ( eventDurationMax ) & & eventDurationMax . Equals ( 0f ) )
{
b . eventDurationMin = - 1f ;
b . eventDurationMax = - 1f ;
eventDurationMin = - 1f ;
eventDurationMax = - 1f ;
}
2025-06-03 02:42:28 -04:00
return playable ;
}
#if UNITY_EDITOR
2026-03-14 03:13:10 -04:00
[UnityEditor.CustomEditor(typeof(AkTimelineEventPlayable), true)]
2025-06-03 02:42:28 -04:00
public class Editor : UnityEditor . Editor
{
private AkTimelineEventPlayable m_AkTimelineEventPlayable ;
private UnityEditor . SerializedProperty akEvent ;
private UnityEditor . SerializedProperty retriggerEvent ;
private UnityEditor . SerializedProperty UseWwiseEventDuration ;
private UnityEditor . SerializedProperty PrintDebugInformation ;
private UnityEditor . SerializedProperty StopEventAtClipEnd ;
private UnityEditor . SerializedProperty blendInCurve ;
private UnityEditor . SerializedProperty blendOutCurve ;
public void OnEnable ( )
{
m_AkTimelineEventPlayable = target as AkTimelineEventPlayable ;
if ( m_AkTimelineEventPlayable = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
akEvent = serializedObject . FindProperty ( "akEvent" ) ;
retriggerEvent = serializedObject . FindProperty ( "retriggerEvent" ) ;
UseWwiseEventDuration = serializedObject . FindProperty ( "UseWwiseEventDuration" ) ;
PrintDebugInformation = serializedObject . FindProperty ( "PrintDebugInformation" ) ;
StopEventAtClipEnd = serializedObject . FindProperty ( "StopEventAtClipEnd" ) ;
blendInCurve = serializedObject . FindProperty ( "blendInCurve" ) ;
blendOutCurve = serializedObject . FindProperty ( "blendOutCurve" ) ;
}
public override void OnInspectorGUI ( )
{
serializedObject . Update ( ) ;
UnityEngine . GUILayout . Space ( UnityEditor . EditorGUIUtility . standardVerticalSpacing ) ;
using ( new UnityEditor . EditorGUILayout . VerticalScope ( "box" ) )
{
UnityEditor . EditorGUILayout . PropertyField ( akEvent , new UnityEngine . GUIContent ( "Event: " ) ) ;
UnityEditor . EditorGUILayout . PropertyField ( blendInCurve ) ;
UnityEditor . EditorGUILayout . PropertyField ( blendOutCurve ) ;
}
using ( new UnityEditor . EditorGUILayout . VerticalScope ( "box" ) )
{
UnityEditor . EditorGUILayout . PropertyField ( UseWwiseEventDuration , new UnityEngine . GUIContent ( "Use Wwise Event Duration: " , "The clip duration is set to the duration of the Wwise Event" ) ) ;
if ( ! UpdateClipInformation ( m_AkTimelineEventPlayable . owningClip , m_AkTimelineEventPlayable . akEvent , serializedObject , UseWwiseEventDuration . boolValue ) )
{
UnityEditor . EditorGUILayout . HelpBox ( string . Format ( "The duration of the Wwise event \"{0}\" has not been determined. Playback for this event may be inconsistent. " +
"Ensure that the event is associated with a generated SoundBank!" , m_AkTimelineEventPlayable . akEvent . Name ) , UnityEditor . MessageType . Warning ) ;
}
if ( ! UseWwiseEventDuration . boolValue )
{
var StopEventAtClipEndValue = StopEventAtClipEnd . boolValue ;
var retriggerEventValue = retriggerEvent . boolValue ;
UnityEditor . EditorGUILayout . PropertyField ( StopEventAtClipEnd , new UnityEngine . GUIContent ( "Stop Event At End Of Clip: " ) ) ;
UnityEditor . EditorGUILayout . PropertyField ( retriggerEvent , new UnityEngine . GUIContent ( "Loop: " , "When checked, an event will loop until the end of the clip." ) ) ;
if ( retriggerEvent . boolValue & & ! StopEventAtClipEnd . boolValue )
{
if ( ! retriggerEventValue )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
StopEventAtClipEnd . boolValue = true ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
else if ( StopEventAtClipEndValue )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
retriggerEvent . boolValue = false ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
}
}
using ( new UnityEditor . EditorGUILayout . VerticalScope ( "box" ) )
{
UnityEditor . EditorGUILayout . PropertyField ( PrintDebugInformation ) ;
}
serializedObject . ApplyModifiedProperties ( ) ;
}
private static void UpdateProgressBar ( int index , int count )
{
float progress = ( float ) index / count ;
UnityEditor . EditorUtility . DisplayProgressBar ( "Wwise Integration" , "Fixing clip durations of AkTimelineEventPlayables..." , progress ) ;
}
[UnityEditor.InitializeOnLoadMethod]
public static void SetupSoundbankSetting ( )
{
2026-03-14 03:13:10 -04:00
if ( UnityEditor . AssetDatabase . IsAssetImportWorkerProcess ( ) )
{
return ;
}
string [ ] settingsToEnable = { "SoundBankGenerateEstimatedDuration" } ;
AkUtilities . ToggleBoolSoundbankSettingInWproj ( settingsToEnable , AkWwiseEditorSettings . WwiseProjectAbsolutePath , true ) ;
2025-06-03 02:42:28 -04:00
UnityEditor . EditorApplication . delayCall + = UpdateAllClips ;
2026-03-14 03:13:10 -04:00
WwiseProjectDatabase . SoundBankDirectoryUpdated + = UpdateAllClips ;
2025-06-03 02:42:28 -04:00
}
private static void UpdateAllClips ( )
{
var guids = UnityEditor . AssetDatabase . FindAssets ( "t:AkTimelineEventPlayable" , new [ ] { "Assets" } ) ;
if ( guids . Length < 1 )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
return ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
var processedGuids = new System . Collections . Generic . HashSet < string > ( ) ;
for ( var i = 0 ; i < guids . Length ; i + + )
{
UpdateProgressBar ( i , guids . Length ) ;
var guid = guids [ i ] ;
if ( processedGuids . Contains ( guid ) )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
continue ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
processedGuids . Add ( guid ) ;
var path = UnityEditor . AssetDatabase . GUIDToAssetPath ( guid ) ;
var objects = UnityEditor . AssetDatabase . LoadAllAssetsAtPath ( path ) ;
var instanceIds = new System . Collections . Generic . List < int > ( ) ;
foreach ( var obj in objects )
{
if ( obj = = null )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
continue ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
var id = obj . GetInstanceID ( ) ;
if ( ! instanceIds . Contains ( id ) )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
instanceIds . Add ( id ) ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
for ( ; instanceIds . Count > 0 ; instanceIds . RemoveAt ( 0 ) )
{
var id = instanceIds [ 0 ] ;
objects = UnityEditor . AssetDatabase . LoadAllAssetsAtPath ( path ) ;
foreach ( var obj in objects )
{
if ( obj & & obj . GetInstanceID ( ) = = id )
{
var playable = obj as AkTimelineEventPlayable ;
if ( playable )
{
var serializedObject = new UnityEditor . SerializedObject ( playable ) ;
var setClipDuration = serializedObject . FindProperty ( "UseWwiseEventDuration" ) . boolValue ;
UpdateClipInformation ( playable . owningClip , playable . akEvent , serializedObject , setClipDuration ) ;
serializedObject . ApplyModifiedProperties ( ) ;
}
break ;
}
}
}
}
UnityEditor . EditorUtility . ClearProgressBar ( ) ;
}
/// <summary>
/// The minimum clip duration. This value is set to 1/60 of a second which generally represents the time of 1 frame.
/// </summary>
private const double MinimumDurationInSeconds = 1.0 / 60 ;
/// <summary>
/// Updates the associated clip information and the event durations.
/// </summary>
/// <returns>Returns true if the Wwise event is found in the project data.</returns>
private static bool UpdateClipInformation ( UnityEngine . Timeline . TimelineClip clip , AK . Wwise . Event akEvent ,
UnityEditor . SerializedObject serializedObject , bool setClipDuration )
{
var clipDuration = MinimumDurationInSeconds ;
var maxDuration = - 1.0f ;
var minDuration = - 1.0f ;
AkUtilities . GetEventDurations ( akEvent . Id , ref maxDuration , ref minDuration ) ;
if ( maxDuration ! = - 1.0f )
{
serializedObject . FindProperty ( "eventDurationMin" ) . floatValue = minDuration ;
serializedObject . FindProperty ( "eventDurationMax" ) . floatValue = maxDuration ;
if ( maxDuration > clipDuration )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
clipDuration = maxDuration ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
if ( clip ! = null )
{
clip . displayName = akEvent . Name ;
if ( setClipDuration )
2026-03-14 03:13:10 -04:00
{
2025-06-03 02:42:28 -04:00
clip . duration = clipDuration ;
2026-03-14 03:13:10 -04:00
}
2025-06-03 02:42:28 -04:00
}
return maxDuration ! = - 1.0f ;
}
}
#endif //#if UNITY_EDITOR
}
#endif // AK_ENABLE_TIMELINE
#endif // #if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.