200 lines
7.5 KiB
C#
200 lines
7.5 KiB
C#
#if INVECTOR_PLAYABLESANIMATOR
|
|
using System.Collections.Generic;
|
|
using InvectorCore.Playables;
|
|
using UnityEngine;
|
|
|
|
namespace Invector.vEventSystems
|
|
{
|
|
/// <summary>
|
|
/// PlayablesAnimator counterpart of <see cref="vAnimatorEvent"/>. Fires named events at a
|
|
/// normalized time, on state enter, or on state exit.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Where the Animator version keeps its own subscriber list per behaviour instance, this one
|
|
/// routes through the PDM's named state event bus (<c>PlayablesAnimator.TriggerStateEvent</c>),
|
|
/// so a listener subscribes to the animator once instead of to each behaviour. Use the
|
|
/// <c>RegisterEvent</c> / <c>RemoveEvent</c> extensions below, which mirror the Animator API.
|
|
/// </remarks>
|
|
public class vAnimatorEventPDM : PlayablesStateBehaviour
|
|
{
|
|
[System.Serializable]
|
|
public class vAnimatorEventTrigger
|
|
{
|
|
public enum vAnimatorEventTriggerType
|
|
{
|
|
NormalizedTime, EnterState, ExitState
|
|
}
|
|
|
|
public string eventName = "New Event";
|
|
public vAnimatorEventTriggerType eventTriggerType = vAnimatorEventTriggerType.NormalizedTime;
|
|
public float normalizedTime;
|
|
|
|
[System.NonSerialized] private int loopCount;
|
|
|
|
public void Init()
|
|
{
|
|
loopCount = 0;
|
|
}
|
|
|
|
/// <summary>True when playback has just crossed this trigger's point in the current loop.</summary>
|
|
public bool ShouldFire(float stateNormalizedTime)
|
|
{
|
|
float clamped = Mathf.Clamp(stateNormalizedTime, 0f, loopCount + 1f);
|
|
if (clamped < loopCount + normalizedTime)
|
|
return false;
|
|
|
|
loopCount++;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public List<vAnimatorEventTrigger> eventTriggers = new List<vAnimatorEventTrigger>();
|
|
|
|
[System.NonSerialized] private bool hasNormalizedEvents;
|
|
|
|
public bool HasEvent(string eventName)
|
|
{
|
|
return eventTriggers != null && eventTriggers.Exists(e => e.eventName.Equals(eventName));
|
|
}
|
|
|
|
public override void OnStateEnter(in PlayablesStateContext context)
|
|
{
|
|
if (eventTriggers == null)
|
|
return;
|
|
|
|
hasNormalizedEvents = false;
|
|
for (int i = 0; i < eventTriggers.Count; i++)
|
|
{
|
|
vAnimatorEventTrigger trigger = eventTriggers[i];
|
|
if (trigger == null)
|
|
continue;
|
|
|
|
if (trigger.eventTriggerType == vAnimatorEventTrigger.vAnimatorEventTriggerType.EnterState)
|
|
{
|
|
Fire(context, trigger.eventName);
|
|
}
|
|
else if (trigger.eventTriggerType == vAnimatorEventTrigger.vAnimatorEventTriggerType.NormalizedTime)
|
|
{
|
|
hasNormalizedEvents = true;
|
|
trigger.Init();
|
|
if (trigger.ShouldFire(context.normalizedTime))
|
|
Fire(context, trigger.eventName);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnStateUpdate(in PlayablesStateContext context)
|
|
{
|
|
if (!hasNormalizedEvents || eventTriggers == null)
|
|
return;
|
|
|
|
if (!context.IsLooping && context.normalizedTime > 1f)
|
|
return;
|
|
|
|
for (int i = 0; i < eventTriggers.Count; i++)
|
|
{
|
|
vAnimatorEventTrigger trigger = eventTriggers[i];
|
|
if (trigger == null
|
|
|| trigger.eventTriggerType != vAnimatorEventTrigger.vAnimatorEventTriggerType.NormalizedTime)
|
|
continue;
|
|
|
|
if (trigger.ShouldFire(context.normalizedTime))
|
|
Fire(context, trigger.eventName);
|
|
}
|
|
}
|
|
|
|
public override void OnStateExit(in PlayablesStateContext context)
|
|
{
|
|
if (eventTriggers == null)
|
|
return;
|
|
|
|
for (int i = 0; i < eventTriggers.Count; i++)
|
|
{
|
|
vAnimatorEventTrigger trigger = eventTriggers[i];
|
|
if (trigger != null
|
|
&& trigger.eventTriggerType == vAnimatorEventTrigger.vAnimatorEventTriggerType.ExitState)
|
|
Fire(context, trigger.eventName);
|
|
}
|
|
}
|
|
|
|
private static void Fire(in PlayablesStateContext context, string eventName)
|
|
{
|
|
if (context.Animator != null && !string.IsNullOrEmpty(eventName))
|
|
context.Animator.TriggerStateEvent(eventName, in context);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Invector
|
|
{
|
|
using System;
|
|
using PlayablesStateContext = InvectorCore.Playables.PlayablesStateContext;
|
|
|
|
/// <summary>
|
|
/// <see cref="vAnimatorEventExtencion"/> equivalent for a <see cref="PlayablesAnimator"/>.
|
|
/// </summary>
|
|
public static class vPlayablesAnimatorEventExtension
|
|
{
|
|
// One relay per (animator, callback) pair so RemoveEvent can unsubscribe the exact delegate
|
|
// that RegisterEvent added — the animator's event carries the name as an argument, so the
|
|
// name filter lives in the relay rather than in the subscription.
|
|
private static readonly Dictionary<
|
|
PlayablesAnimator,
|
|
Dictionary<vEventSystems.vAnimatorEvent.OnTriggerEvent,
|
|
PlayablesAnimator.PlayablesNamedStateEventHandler>> relays = new();
|
|
|
|
/// <summary>
|
|
/// Add an event listener for <paramref name="eventName"/> fired by
|
|
/// <see cref="vEventSystems.vAnimatorEventPDM"/> or by an authored PDM state event.
|
|
/// </summary>
|
|
public static void RegisterEvent(
|
|
this PlayablesAnimator animator,
|
|
string eventName,
|
|
vEventSystems.vAnimatorEvent.OnTriggerEvent onTriggerEventAction)
|
|
{
|
|
if (animator == null || onTriggerEventAction == null || string.IsNullOrEmpty(eventName))
|
|
return;
|
|
|
|
RemoveEvent(animator, eventName, onTriggerEventAction);
|
|
|
|
PlayablesAnimator.PlayablesNamedStateEventHandler relay =
|
|
(string firedName, in PlayablesStateContext context) =>
|
|
{
|
|
if (string.Equals(firedName, eventName, StringComparison.Ordinal))
|
|
onTriggerEventAction(firedName);
|
|
};
|
|
|
|
if (!relays.TryGetValue(animator, out var byCallback))
|
|
{
|
|
byCallback = new Dictionary<
|
|
vEventSystems.vAnimatorEvent.OnTriggerEvent,
|
|
PlayablesAnimator.PlayablesNamedStateEventHandler>();
|
|
relays[animator] = byCallback;
|
|
}
|
|
|
|
byCallback[onTriggerEventAction] = relay;
|
|
animator.OnPlayablesStateEvent += relay;
|
|
}
|
|
|
|
/// <summary>Remove a listener added by <see cref="RegisterEvent"/>.</summary>
|
|
public static void RemoveEvent(
|
|
this PlayablesAnimator animator,
|
|
string eventName,
|
|
vEventSystems.vAnimatorEvent.OnTriggerEvent onTriggerEventAction)
|
|
{
|
|
if (animator == null || onTriggerEventAction == null)
|
|
return;
|
|
|
|
if (!relays.TryGetValue(animator, out var byCallback)
|
|
|| !byCallback.TryGetValue(onTriggerEventAction, out var relay))
|
|
return;
|
|
|
|
animator.OnPlayablesStateEvent -= relay;
|
|
byCallback.Remove(onTriggerEventAction);
|
|
if (byCallback.Count == 0)
|
|
relays.Remove(animator);
|
|
}
|
|
}
|
|
}
|
|
#endif
|