Hello I’m trying to reference scene objects from my Timeline Markers. In timeline clips this can be achieved using Exposed Reference:
public class MyClip : PlayableAsset
{
public ExposedReference<Transform> example;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
Transform t = example.Resolve(graph.GetResolver());
}
}
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:
public class MyMarker : Marker, INotification
{
[SerializeField] private ExposedReference<Transform> transform;
public PropertyName id { get; }
public Transform Transform => transform.Resolve(); // not able to get resolver
}
public class MyMarkerReceiver : MonoBehaviour, INotificationReceiver
{
public void OnNotify(Playable origin, INotification notification, object context)
{
if (notification is MyMarker myMarker)
{
Transform transformFromMarker = myMarker.Transform;
}
}
}
Am I missing something or do I need to use entirely different method?
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.
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.
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.
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?
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.
// Your marker Inherits this
public interface IExecutableNotification
{
void Execute(IExposedPropertyTable resolver, object context);
}
// Attach this to your output Object; can deal with all markers inherited from IExecutableNotification
public class NotificationExecutor : MonoBehaviour, INotificationReceiver
{
public void OnNotify(Playable origin, INotification notification, object context)
{
if (notification is IExecutableNotification executableNotification) {
executableNotification.Execute(origin.GetGraph().GetResolver(), context);
}
}
}
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.
If it’s an Editor-only thing, then you can use TimelineEditor.inspectedDirector, then use playableDirector.GetGenericBinding() to retrieve the bounded object.
public class TestMarker1 : Marker
{
public override void OnInitialize(TrackAsset track)
{
#if UNITY_EDITOR
if (TimelineEditor.inspectedDirector) {
var boundedObject = TimelineEditor.inspectedDirector.GetGenericBinding(track);
if (boundedObject) {
// do something with boundedObject infos...
}
}
#endif
}