Home
  • Manual
  • Node Index
  • API
  • Changelog
Search Results for

    Show / Hide Table of Contents
    • Tutorials
      • Creating your first map generator
        • 1. Creating your first map graph
        • 2. Running a graph at runtime
        • 3. Adding more nodes
        • 4. Object placement
      • Basic map types
        • • Cave-like maps
        • • Room-based maps
        • • Sample-based maps
        • • Combining map types
      • More Tutorials
        • • Generating prefabs
        • • Tile/prefab/tilemap set weights
        • • Syncing named color sets
        • • Groups
        • • Notes & comments
        • • Sub graphs
        • • Tilemap Sets
        • • Optimization tips
        • • Static seed (daily challenges)
        • • Running a graph asynchronously or multi-threaded
        • • Object pooling
        • • Tips for rule tiles
        • • Saving and loading maps
      • Tutorials for coders
        • • Running graphs from scripts
        • • Output parameters
        • • Custom nodes
        • • Custom node views
        • • Adding a constant node type
        • • Custom styling for the graph editor
        • • Dependencies for custom nodes
    • Sample Project
    • Troubleshooting
    • Support

    Adding dependencies for custom nodes

    Inside your custom node you might want access to certain dependencies. Inside the node you can use the Get method to get certain dependencies, for example calling Get<Rng>() to get the random number generator.

    Previously, you needed to create your own graph class inheriting from the MapGraphGraph to be able to add your own dependencies. This was rather inconvenient and therefore version 1.27 introduces the ability to add dependencies without needing to create your own graph class.

    There are multiple ways to register your own dependencies.

    Adding a dependency in the Map Graph Runner inspector

    If the dependency you want to add is a MonoBehaviour in the scene, you can add it this way.

    You can add a new entry here: Dependencies on Runner component

    Assigning a GameObject to this entry, will allow you to pick a MonoBehaviour on that GameObject to use as a dependency.

    You are also able to select the type under which the component is registered. This will be either its own type (by default) or one of the interfaces the class implements.

    Component dependency

    The type is important, because it's the type used for getting the dependency inside your custom node. In case above you'd get the dependency by calling Get<YellowProvider>.

    Component dependency registered under interface

    However, here you'd get the component by calling Get<IColorProvider>. Why this is important will become clear later, after discussing the other methods for adding dependencies.

    Adding a dependency to the graph asset

    If the dependency is a ScriptableObject, you can add it in a very similar way, but instead of adding it to a runner component, you add it to the graph asset directly.

    This method has a big advantage over the other methods; where the other types of dependencies, except for global, are only available when the graph is being processed, these types of dependencies can be used outside of that context.

    This can be useful, for example, if you want to use these dependencies in your custom node views.

    Say you want to create a node similar to the Named Color node, but instead of getting data from a Named Color set, you want to get data from your own ScriptableObject instead, this would allow you to do that.

    Getting the dependency inside a node is the same as with any of the other methods, namely by calling the Get method.

    In order to get the dependency outside of that, you can call the Get method on the graph object.

    Inside a custom node view, that would look something like this:

    [ScriptNodeView(typeof(MyNode))]
    public class MyNodeView : ScriptNodeView
    {
        public MyNodeView(MyNode node, ScriptGraphView graphView) : base(node, graphView)
        {
            ...
            var myDependency = Graph.Get<MyDependency>(); 
            ...
        }
    }
    

    Please note: If you have the graph open in the editor, whenever you add, remove or modify a dependency on a graph, you'll need to reload the graph to see this change, by selecting the "Reload Window" option from the graph editor window's ellipsis menu, or by pressing F5.

    Passing dependencies to the Run call

    Another way to add dependencies is by simply passing them to the runner component when calling Run through a script, like this:

    public class RunWithDependencies : MonoBehaviour
    {
        [SerializeField] private ScriptGraphRunner graphRunner;
    
        public void Start()
        {
            // Create the dependency.
            var redProvider = new RedProvider();
            
            // Create a dependency container.
            var dependencyContainer = new DependencyContainer();
            
            // Register the dependency under the interface type IColorProvider
            dependencyContainer.Register<IColorProvider>(() => redProvider);
            
            // Pass the dependency container to the Run method.
            graphRunner.Run(dependencyContainer);
        }
    }
    

    Like with the example of adding the dependency through the inspector, registering the dependency under the IColorProvider interface allows us to get the dependency in the custom node code using a Get<IColorProvider> call.

    Like the Run method, a dependency container can also be passed to the RunAsync and RunSync methods.

    Adding dependencies globally

    The final method allows for registering dependencies globally, so that they are always available inside your custom nodes, without having to assign them explicitly in the runner's inspector or passing them to the Run method.

    Here's how:

    [InitializeOnLoad]  // This makes sure this code gets executed on load.
    public static class DependencyInitializer
    {
        static DependencyInitializer()
        {
            // Create the dependency.
            var blueProvider = () => new BlueProvider();
            
            // Assign the dependency to the global dependency container using this static
            // method on the ScriptGraphProcessor class.
            ScriptGraphProcessor.RegisterGlobalDependency<IColorProvider>(() => blueProvider);
        }
    }
    

    In order to access a global dependency outside a node, you can access the static Get method on the ScriptGraphProcessor class. Which would look something like this:

    [ScriptNodeView(typeof(MyNode))]
    public class MyNodeView : ScriptNodeView
    {
        public MyNodeView(MyNode node, ScriptGraphView graphView) : base(node, graphView)
        {
            ...
            var myDependency = ScriptGraphProcessor.Get<MyDependency>();
            ...
        }
    }
    

    Using the dependency inside your custom node

    Now that you know how to add your own dependencies, you can use them inside your custom nodes. Simply call the Get method in the custom node's code with the type the dependency is registered under as a type argument, like so:

    // Get the dependency.
    var colorProvider = Get<IColorProvider>();
    
    // Use the dependency!
    var color = colorProvider.GetColor();
    

    Which method to use

    If you have a dependency that you want to be available everywhere, without having to assign it separately for each runner component or every Run call, it's easiest to register it to the global dependency container.

    If you have a MonoBehaviour that you want to register as a dependency, but you don't want to write a custom script for it, use the inspector method.

    If you're calling Run from a script, you can just pass the dependencies to the method call.

    If you have a ScriptableObject that you need access to outside of processing a graph, you'll want to assign it to the graph asset.

    You don't have to pick just one method though, you can use the different methods together, depending on the situation. Being able to override dependencies is a reason to use one method over the other.

    Overriding dependencies (order)

    So you might have been wondering why you'd ever need to register a dependency under one of its interface types instead of its own type. The reason is, that it allows you to replace dependencies in certain scenarios.

    The dependencies are added (and override each other) in the following order:

    • Global dependencies
    • Dependencies assigned on the graph
    • Dependencies assigned in the inspector
    • Dependencies passed to the Run method

    This means that assigning a dependency in the inspector can override one assigned as a global dependency, when registered to the same type. And that dependencies passed to the Run method overrides both of the other methods.

    Following the example from earlier, say by default the BlueProvider is registered as a global dependency under the type IColorProvider, but for a specific graph you prefer to use the YellowProvider as the IColorProvider. You can override the default by assigning the YellowProvider in that graph's runner. Now the YellowProvider will be returned when calling Get<IColorProvider> when running the graph through that particular runner component, but the BlueProvider will be used everywhere else.

    You can do the same if you want to override a particular dependency for a specific Run call. For example, by registering the RedProvider as the IColorProvider by passing it as an argument, overrides both other methods, for that specific Run call only.

    Multithreaded execution compatibility

    Whenever you use multithreaded execution, it can occur that multiple nodes try to use the same dependency at the same time, which might cause strange and hard to diagnose issues, if your dependency is not thread-safe. An easy way to make your dependency thread-safe is by wrapping it inside a ThreadLocal instance, like so:

    var colorProvider = new ThreadLocal<IColorProvider>(() => new BlueProvider());
    container.Register(() => colorProvider.Value);
    

    This will make sure that each thread has their own instance of the dependency.

    In This Article
    Back to top Generated by DocFX