private static void BindOrUnbindCallback(this AnimationClip clip, GameObject animEvtReceiverObject, float atPosition, Action callback, bool bind) { var actionWord = bind ? "register" : "unregister"; if (animEvtReceiverObject == null) { Debug.LogWarningFormat("Trying to {0} callback for null animation event receiver game object", actionWord); return; } if (callback == null) { Debug.LogWarningFormat("Trying to {0} null callback for animation clip", actionWord); return; } if (atPosition < 0.0f || atPosition > clip.length) { Debug.LogWarningFormat("Trying to {0} callback for position outside of clip timeline", actionWord); return; } var eventReceiver = animEvtReceiverObject.GetComponent <AnimationEventReceiver>(); if (bind) { if (eventReceiver == null) { eventReceiver = animEvtReceiverObject.AddComponent <AnimationEventReceiver>(); } // Use callback timeline position as unique identifier of the list of callbacks // called at that timeline position. Set timeline position as animation event parameter // to send it to AnimationEventReceiver to get list of callbacks at that position. eventReceiver.RegisterTimelineCallback(atPosition, callback); clip.AddEventIfNotExists(OnTimelineEventRaisedMethodName, atPosition, atPosition); } else { if (eventReceiver == null) { Debug.LogWarningFormat("Trying to unregister callback for game object without AnimationEventReceiver component"); return; } // Currently AnimationEventReceiver component is not removed when all callback for all positions // are removed. This is due to component being public which means it could be used by user code. var lastCallbackForPositionRemoved = eventReceiver.UnregisterTimelineCallback(atPosition, callback); if (lastCallbackForPositionRemoved) { clip.RemoveEvent(OnTimelineEventRaisedMethodName, atPosition, atPosition); } } }