Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to resolve ExposedReference from Marker, or from INotificationReceiver?

Discussion in 'Timeline' started by jacekpotapczuk, Oct 13, 2021.

  1. jacekpotapczuk

    jacekpotapczuk

    Joined:
    Jan 18, 2020
    Posts:
    2
    Hello I'm trying to reference scene objects from my Timeline Markers. In timeline clips this can be achieved using Exposed Reference:

    Code (CSharp):
    1. public class MyClip : PlayableAsset
    2. {
    3.     public ExposedReference<Transform> example;
    4.  
    5.     public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    6.     {
    7.         Transform t = example.Resolve(graph.GetResolver());
    8.     }
    9. }
    10.  
    In Markers that's not directly possible, because you don't have access to PlayableGraph, so you can't get get Resolver to resolve the ExposedReference. Here is an example code:

    Code (CSharp):
    1. public class MyMarker : Marker, INotification
    2. {
    3.     [SerializeField] private ExposedReference<Transform> transform;
    4.     public PropertyName id { get; }
    5.  
    6.     public Transform Transform => transform.Resolve(); // not able to get resolver
    7. }
    8.  
    9.  
    10. public class MyMarkerReceiver : MonoBehaviour, INotificationReceiver
    11. {
    12.     public void OnNotify(Playable origin, INotification notification, object context)
    13.     {
    14.         if (notification is MyMarker myMarker)
    15.         {
    16.             Transform transformFromMarker = myMarker.Transform;
    17.         }
    18.     }
    19. }
    Am I missing something or do I need to use entirely different method?
     
  2. DavidGeoffroy

    DavidGeoffroy

    Unity Technologies

    Joined:
    Sep 9, 2014
    Posts:
    542
    You can use Playable.GetGraph and combine it with lazy initialization to resolve it on the first notification.
    origin.GetGraph().GetResolver().


    Built-in markers are meant to be a turnkey solution, which is why the Playable creation code is not exposed.

    If you absolutely want to resolve the transform at creation, you will need to do your own management using
    TimeNotificationBehaviour
    , but that might be a bit overkill for this situation.
     
    aoikiwi and jacekpotapczuk like this.
  3. jacekpotapczuk

    jacekpotapczuk

    Joined:
    Jan 18, 2020
    Posts:
    2
    That's exactly what I was looking for. Thank you.
     
  4. ncho

    ncho

    Joined:
    Feb 1, 2014
    Posts:
    96
    So I have read about this topic for hours and I am still highly confused. What I want to do:

    In a custom marker, get a list of transforms that's on the bound object of the track the marker is on. I could figure out how to get to all bound objects that are tied to the Director component but how can I get the one on whose track the marker is on? Shouldn't this be the most common use case?

    This is so needlessly complicated it's making my head spin.
     
  5. Yuchen_Chang

    Yuchen_Chang

    Joined:
    Apr 24, 2020
    Posts:
    107
    How about create and add a INotificationReceiver on the bound Object? (Such as,
    MyMarkerReceiver : MonoBehaviour, INotificationReceiver
    )
    When time goes through the marker, marker is sent to every INotificationReceiver component on the object the track is bound to.
     
  6. ncho

    ncho

    Joined:
    Feb 1, 2014
    Posts:
    96
    I already have this implemented, but I also want to get information that's on the bound object into the custom Marker, which presents you with the usual barrier between scene objects and ScriptableObjects, sothe whole idea of binding objects in the Timeline seems designed to work around that, except you should be able to do something like Marker.parent.ResolveBoundObject() and yet can't. What's the point of binding objects to tracks if you can't easily work with them?
     
  7. tsukimi

    tsukimi

    Joined:
    Dec 10, 2014
    Posts:
    53
    I think it's designed to be like animation events, and animation events only contains datas.
    Maybe you can recall from your INotificationReceiver, then you can define processes in the marker.

    Code (CSharp):
    1.  
    2. // Your marker Inherits this
    3. public interface IExecutableNotification
    4. {
    5.     void Execute(IExposedPropertyTable resolver, object context);
    6. }
    7.  
    8. // Attach this to your output Object; can deal with all markers inherited from IExecutableNotification
    9. public class NotificationExecutor : MonoBehaviour, INotificationReceiver
    10. {
    11.     public void OnNotify(Playable origin, INotification notification, object context)
    12.     {
    13.         if (notification is IExecutableNotification executableNotification) {
    14.             executableNotification.Execute(origin.GetGraph().GetResolver(), context);
    15.         }
    16.     }
    17. }
     
    Last edited: Jul 21, 2023
  8. ncho

    ncho

    Joined:
    Feb 1, 2014
    Posts:
    96
    Thanks but the problem with this approach is that you need a signal to be sent to the object first - I would need this data at marker creation time to grab data for the inspector. I tried an approach with overriding OnInitialize but could not get that to work.
     
  9. tsukimi

    tsukimi

    Joined:
    Dec 10, 2014
    Posts:
    53
    If it's an Editor-only thing, then you can use TimelineEditor.inspectedDirector, then use playableDirector.GetGenericBinding() to retrieve the bounded object.
    Code (CSharp):
    1.     public class TestMarker1 : Marker
    2.     {
    3.         public override void OnInitialize(TrackAsset track)
    4.         {
    5.  
    6. #if UNITY_EDITOR
    7.             if (TimelineEditor.inspectedDirector) {
    8.                 var boundedObject = TimelineEditor.inspectedDirector.GetGenericBinding(track);
    9.                 if (boundedObject) {
    10.                     // do something with boundedObject infos...
    11.                 }
    12.             }
    13. #endif
    14.         }