Hi, I’m working on an editor tool using the experimental GraphView, where I create a ObjectField inside a node to reference an asset in my project. Due to my goal being to be able to integrate DOTS as much as possible into my project, I would like to store the reference to the asset as a struct, rather than creating a variable storing the asset directly.
I’ve stumbled upon the official documentation for the UnityObjectRef struct, which seemed to work at first, as I could assign the reference inside a GraphView, then save the containing object, the latter correctly referencing the asset.
However, I’ve noticed the reference changed when restarting the editor, as it seems the UnityObjectRef stores the asset’s instanceID, which is renewed by the editor and unreliable for persistence. Is there a way to make this ID consistent, or to be able to trace the data back to the asset?
I know I can just store the file path, and then call AssetDatabase.LoadAssetAtPath() to get the asset this way, but I would like to know if there is a way to make the UnityObjectRef work in this context.
Here are the scripts for the node, and the data struct itself :
using System;
using Herve.Editor.Models;
using Herve.Runtime.Models;
using Unity.Entities;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace Herve.Editor.Views
{
/// <summary>
/// Node acting as the start of a dialogue
/// </summary>
internal sealed class EndNode : BaseNode<EndNodeData>
{
#region Private fields
#endregion
#region Constructor
/// <summary>
/// Constructor
/// </summary>
/// <param name="position">The position of the node on the graph</param>
public EndNode(Vector2 position) : base(EditorConstants.END_NODE_LABEL, position)
{
this.Data = new EndNodeData(Guid.NewGuid().ToString(), position, EndNodeType.End, default);
USSBuilder.AddUSSClass(this, "endNode");
this.CreatePorts();
this.CreateFields();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="data">The data to assign to the node</param>
public EndNode(EndNodeData data) : base(EditorConstants.END_NODE_LABEL, data.Position)
{
this.Data = data;
USSBuilder.AddUSSClass(this, "endNode");
this.CreatePorts();
this.CreateFields();
}
#endregion
#region Protected methods
/// <inheritdoc/>
protected override void CreatePorts()
{
GraphBuilder.AddPort(this, "Input", Direction.Input, Port.Capacity.Multi);
}
/// <inheritdoc/>
protected override void CreateFields()
{
Box box = UIBuilder.NewBox(this.mainContainer);
UIBuilder.NewEnumField(box, this.Data.EndNodeType, this.OnEndNodeTypeValueChangedCallback);
UIBuilder.NewObjectField(box, this.Data.NextDialogue.Value, this.OnNextDialogueValueChangedCallback);
}
#endregion
#region Private methods
/// <summary>
/// Called when the value of the enum is changed
/// </summary>
/// <param name="type">The type of action to execute once this node is reached</param>
private void OnEndNodeTypeValueChangedCallback(EndNodeType type)
{
this.Data = new EndNodeData(this.Data.NodeGUID, this.Data.Position, type, this.Data.NextDialogue);
}
/// <summary>
/// Called when the value of the object field is changed
/// </summary>
/// <param name="dialogue">The new dialogue to register</param>
private void OnNextDialogueValueChangedCallback(DialogueAssetSO dialogue)
{
UnityObjectRef<DialogueAssetSO> reference = new();
reference.Value = dialogue;
this.Data = new EndNodeData(this.Data.NodeGUID, this.Data.Position, this.Data.EndNodeType, reference.Value);
}
#endregion
}
}
/// <summary>
/// The data of a StartNode instance
/// </summary>
[Serializable]
public struct EndNodeData : INodeData
{
#region Properties
/// <inheritdoc/>
[field: SerializeField]
[field: ReadOnly]
public FixedString64Bytes NodeGUID { get; private set; }
/// <inheritdoc/>
[field: SerializeField]
public float2 Position { get; private set; }
/// <summary>
/// The type of action to execute once this node is reached
/// </summary>
[field: SerializeField]
public EndNodeType EndNodeType { get; private set; }
/// <summary>
/// The next dialogue (if any) to play once the current one is over
/// </summary>
[field: SerializeField]
public UnityObjectRef<DialogueAssetSO> NextDialogue { get; private set; }
#endregion
}