Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Creating Custom Playables

Discussion in 'Timeline' started by ModLunar, Feb 8, 2018.

  1. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Okay so I'm wondering a few things about the Playables API. I wasn't sure if this is the right forum to post this in cause this is more specifically about Timeline.

    I'm trying to create a custom type of Playable of my own, so I can do cool stuff with custom PlayableGraphs :). They say I can do this by deriving from the class PlayableBehaviour, and then I can create custom playables using both my custom PlayableBehaviour class and the ScriptPlayable<T> struct, where T is my custom PlayableBehaviour type. But I'm having trouble understanding what should go where, and what actually happens in the graph and stuff. So here's what I'm wondering:

    -- -- --

    - Is there one and only one PlayableBehaviour instance for each one ScriptPlayable<T> instance? Or can there be multiple ScriptPlayable<T>s around connected to the same PlayableBehaviour instance?

    - If I want to store custom data for each instance of my custom "playable" type, where would I save that? Say if I have a PlayableGraph and I insert a bunch of these ScriptPlayable<T>s into the graph -- is there a place where each of those playable nodes can store per-instance data for each of them? Where would I then retrieve this data from later?

    - To my understanding, structs are used extensively in the Playables API because they have the advantage over classes by not allocating garbage collection memory. This makes it easier/more lightweight to be creating and destroying/rebuilding PlayableGraphs a lot, right? Does this mean that every "node" in a PlayableGraph has to be a struct? Like for example if I see there exists a class, UnityEngine.Timeline.ParticleControlPlayable, do I know right off the bat that data type will definitely not be in any graphs because it's a class?

    - If for that example, ParticleControlPlayables are not put into the graphs directly, what data type would be in the graph for them? Would they be ScriptPlayable<ParticleControlPlayable>s since ParticleControlPlayable is a PlayableBehaviour class?

    - I was thinking about how Playables/PlayableOutputs in PlayableGraphs are structs. But structs are value-types, so they don't go by references that could possibly reference the same instance like classes can. So how is it that I can call instance methods on Playables and PlayableOutputs and have the graph/other nodes see this change too?
     
    Last edited: Feb 8, 2018
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Let's see if I can clarify this, because while it's an elegant and GC-friendly design, it's can be confusing.

    On the C# side, when dealing with playables (graphs and outputs as well), they appear as structs. The structs are really handles to the actual playable. So, 'built-in' playables like AnimationClipPlayable, AudioMixerPlayable, etc... have a C++ implementation. So creating a graph with these playables can be done completely without allocating any GC memory.

    To write your own "playable", you attach a C# object to a plain-old vanilla playable. This C# script must be derive from PlayableBehaviour. So, for example ParticleControlPlayable is a playable behaviour.

    You use ScriptPlayable<ParticleControlPlayable> to create and access a playable that has a playable behaviour attached. When creating one, the memory for the playable follows the standard pattern (no GC), but there is a C# allocation for the PlayableBehaviour. The playable behaviour instance is freed (and GC will reclaim it) when the ScriptPlayable is destroyed (which may be when the graph is destroyed).

    A script playable is a playable with a behaviour attached.

    You can store data inside PlayableBehaviours. Right now there is only one behaviour per playable allowed. Each ScriptPlayable you create has an instance of your PlayableBehaviour.

    In the near-ish future there will be other types of C# playable behaviours.

    I hope that clears up more confusion that it creates :)
     
    wang37921 and ModLunar like this.
  3. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Hey seant, yes that really helps clear up a lot of my general confusion! xD Thank you for your help as always. It's great to have some (even just a little bit of) insight into how this connects with the C++ side, I appreciate it!


    Oh by the way I figured out what was happening! I was trying to test around with the two methods for ScriptPlayableOutputs, SetUserData(Object value), and SetReferenceObject(Object value). But in order to use those methods, I needed to inherit from Unity's Object class! So when my data class was a UnityEngine.Object, and I was trying to store in my PlayableBehaviour, it wouldn't be able to save for some reason. Once I took away that inheritance, it worked immediately and it was saved with my PlayableBehaviour.


    Now that that is all good, I'm wondering what is the point/difference of those two methods, SetUserData(..) and SetReferenceObject(..) for PlayableOutputs?
     
  4. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    What is passed to SetUserData is what gets passed to PlayableBehaviour::processFrame. This is what timeline uses to pass bindings from tracks to scripts.

    I think SetReferenceObject just holds the key from the binding system from the playable director. In the case of timeline, I think it holds a reference to the track.
     
    ModLunar likes this.
  5. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Ah I see. That's interesting and good to know :)