Extending a node with SO

I have a node that takes in an SO to extends/change is functionality. The idea is to generate my word from these nodes and alter what they are by plugging in different SO. Could be a navigation node to a prop that pools prefab objects. My question is when I generate all these nodes will the SO properties act like a static variable, will changing a SO property result in changing it for all the nodes?. Also im not clear how to set this up and access it. My failed attempt below

using UnityEngine;
using System.Collections;


[System.Serializable]
public class Node  {
    
    public Vector3 worldPosition;              // World pos
    public int gridX;                                   // Grid X pos
    public int gridY;                                   // Grid Y pos
    [SerializeField]public NodeAttrs[] nodeAttrs; // Takes an SO to extend functionality 
    
    // Constructor
    public Node( Vector3 _worldPos, int _gridX, int _gridY) {
        worldPosition = _worldPos;
        gridX = _gridX;
        gridY = _gridY;
    }


}

My SO trying to extend base node

[CreateAssetMenu(menuName="ScriptableObject Assets/NodeAttributes/NodeAttrAStar")]
public class NodeAttrAStar : NodeAttrs {
    [SerializeField]private int _obstaclePenalty;        
    public int obstaclePenalty{
        get {return _obstaclePenalty; }
        set {_obstaclePenalty = value; }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class NodeAttrs : ScriptableObject {

}

Maybe add in the NodeAttrs[ ] in as a param for the constructor, and the in your grid when you are populating it with nodes just create your so and pass it to the constructor for the node.

I don’t see anything wrong with the code you’ve posted. What exactly isn’t working?

That depends if the nodes point to the same SO. If they are, then, naturally, changing the SO will change it in all nodes that reference that SO.

When I create my Grid of nodes I have no reference to the SOs. I need to somehow create an instance of the SO for each node. Im not sure how to access the obstaclePenalty property per node. Getting the non SO attributes seem simple

        foreach (Node n in grid) {
            Debug.Log (n.gridX +" "+n.gridY);
        }

Here is the create grid

    void CreateGrid() {
        grid = new Node[gridSizeX, gridSizeY];
        Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.forward * gridWorldSize.y / 2;

        for (int x = 0; x < gridSizeX; x++) {
            for (int y = 0; y < gridSizeY; y++) {
                Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.forward * (y * nodeDiameter + nodeRadius);
                grid [x, y] = new Node (worldPoint, x, y);
            }
        }
    }

Do you have an example I can look at

I having trouble accessing the SO properties, maybe because I no reference set per node. Not sure how to do that

just do something like this?

    void CreateGrid() {
        grid = new Node[gridSizeX, gridSizeY];
        Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.forward * gridWorldSize.y / 2;

        for (int x = 0; x < gridSizeX; x++) {
            for (int y = 0; y < gridSizeY; y++) {
                Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.forward * (y * nodeDiameter + nodeRadius);
                NodeAttrs[] N_Astar = new NodeAttrs[]{ScriptableObject.CreateInstance<NodeAttrAStar>()};
                grid [x, y] = new Node (worldPoint, x, y, N_Astar);
            }
        }
    }

Thanks Jister this seems to work. One more question. Is it possible to instance all the SO in the constructor by looping over the different SO connected to [SerializeField]public NodeAttrs[ ] nodeAttrs; and not passing an argument

How do I find the SO name to instance or can I just somehow instance the whole list of SO

you would need all the subclasses of NodeAttrs class which you could get with something like this:

Assembly.GetAssembly(typeof(Attrs)).GetTypes().Where(t => t.IsSubclassOf(typeof(Attrs)))

but it’s not very optimal.
maybe it would be better to use some kind of Factory Method

public class AttrsFactory
{
    public static Attrs Create(AttrsType a)
    {
        return (Attrs)ScriptableObject.CreateInstance(a.ToString());
    }
}

with the code above you would use an enum with all the types of Attrs listed. this way you could iterate over the enum and create all the Attrs types you need.

Thanks for the help on this, I took a step back a thought what if I have one SO per type of node and attrs as MonoBehaviour. Problem now is why cant I drag in my NodeAttributeBase to NodeBase::nodeAttributeBase in the editor ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName="ScriptableObject Assets/Test/NodeBase")]
public class NodeBase : ScriptableObject {

    [SerializeField]private NodeAttributeBase[] _nodeAttributes;
    public NodeAttributeBase[] nodeAttributeBase{get {return _nodeAttributes;}}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class NodeAttributeBase: MonoBehaviour {
   
    public Vector3 worldPosition;
    public int gridX;                                 
    public int gridY;                                 

}

maybe tell us what the end goals is, so we can think along :wink:
so i get you want a grid of node and the node’s can hold a SO for functionality.
first of why a SO?
then you wanted a list of SO’s per node. Why? to choose between the SO functionalities? So If you change to one SO/Node, which node has what SO? and why?
if all these things are clear, I bet there’s an easy way to do it :wink:

OK let me try to explain what im trying to achieve. Here is my dream workflow.

1)I create an empty base node from my node menu as a SO.

2)I create attributes that I want to connect to the empty base node from my nodeAttributs menu as SO. eg prefab name or Astar variables or pool functionality.

3)I connect my SO attributes to my base node in the Unity editor designing my Node.

4)I connect my SO node to my grid script that instance the node to my game world.

I dont have to use SO I just lack knowledge as to what the best approach is.This is the dream but struggling to get there :frowning:

I

ive tried a couple of things but they all feel clumsy to me, would you recommend dropping SO all together

A ScriptableObject is basically for anything that you need to serialize and save to disk. Keeping them or dropping them is a question of whether you need that functionality or not.

What’s the difference between a node and the attributes that you want to put on them? Why isn’t it possible just to code the node with those attributes to begin with?

Ok reading this, I’m thinking you are making a custom editor with nodes your can drag and drop and connect?
something like:

if so you need the SO’s if not you don’t need them.

The nodes represent anything in my game and depending on the attributes plugged in it behave differently eg I have a grid of nodes, when i attach my prefab attribute and pool attribute they form my game terrain. When i attach my astart attribute the node is use for navigation ect.

So they are essentially my building blocks

i think i would just use an Interface IAttributable or an Abstract class Attribute or even just a Base class with a virtual void Execute() orso…
if your not making something like i showed above, there is no need for a SO in your case i think.