I’m building something where entities need access to a graph for pathfinding that is implemented in a custom struct that contains a NativeMultiHashmap like so:
Entities that need a path then spawn an entity containing a PathRequest struct. I have a system that handles these request entities and then stores the result in a DynamicBuffer on the requesting entity. This all works fine.
However, I’m uncertain as to what is the best approach to pass the graph the pathfinding system should use, as entities can have different graphs they should navigate in.
I’ve come up with 2 solutions so far. My preferred solution would be storing the graph as a BlobAssetReference and setting it on a component on the request ‘event’ entity:
public struct Int2GraphRequest : IComponentData
{
public BlobAssetReference<Int2Graph> Graph { get; set; } // graph to perform search on
public Entity Entity { get; set; } // entity requesting the path
public Int2Node Start { get; set; }
public Int2Node Goal { get; set; }
}
The graph is constructed at startup and doesn’t change after that. A BlobAssetReference is created from it with BlobAssetReference.Create(…)
However, this thread: Hashmap in a blob asset
states that one cannot use NativeHashmap (and I presume also MultiHashmap) in a BlobAssetReference. I’ve tried this approach and it does seem to work. But I keep getting a Native Collection has not been disposed error even though I’m disposing the BlobAssetReference and the original struct when the game stops. My hunch is the BlobAssetReference doesn’t know about the internal NativeMultiHashmap that’s in the struct. (The struct itself implements IDisposable and disposes the hash map)
Can somebody shed light on this approach and if it’s a valid one?
My other approach would be storing the graph struct as persistent directly on the system that handles the requests. Different graphs would then be handled by creating a new system in a different world.
The system handling the requests can pass the graph struct directly to the job and no BlobAssetReference would be required. I’ve read however you should not store data in systems themselves. Any thoughts on this approach would be appreciated too!
The thread you linked already answered your questions. It’s not valid. And will throw you errors which will prevent you from using native collections inside blobs in future.
From Unity tests for blobs.
How about my second option? Basically looking for a way to ‘reference’ a complex struct in a request. But it seems like this is almost impossible to do without generating a new world (and thus a new system containing the struct). Surely I’m not the first person to encounter such a scenario.
For our pathfinding we have singleton entity with class (not struct) IComponentData with our custom Native container (with couple of our other nested custom native containers) and we have mechanism for semiautomatic managing dependency for this container, disposing etc., and all our pathfinding results (flow fields implemented as specific unsafe structs for better usage in job with parallelism) stored in this, with cleanup from time to time, with rebuilding and invalidating regions (on top Hierarchical A* level) etc. Only one system writes to this (pathfinding) and all other only read from this. In OnUpdate we getting container from singleton and just using it inside all our jobs with maximum parallel (where it’s possible and give you gains) processing and Burst.
Nice one. I was trying something alike found some strange behaviour when implementing IDisposable on the class. When I try to replace the component, Dispose() get’s called on the new component I’m setting. I was hoping it would only be called on the old component that is being replaced so the native containers can get cleaned up automatically. Is that something you’ve worked around or are you not using IDisposable at all?