-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a Graph data structure to Godot #3848
Comments
What game use are you using for your project? |
@fire I've updated the writeup to indicate game uses. I've also asked the author of the original proposal, @willnationsdev, to chime in. Thanks! |
Procedural city here, I end up doing the graph by hand just to be able to tell which intersection connects to which (for mapgen purposes, not actual pathfinding). So there are definite game uses |
@godotgamedeveloper I recommend prototyping a resource api and proposing the api here. |
Isn't graph just class GNode:
var name_maybe_idk: String
class Edge
var node1: GNode
var node2: GNode
var weight_etc: float
var nodes: Array
var edges: Array ? Plus some methods, but that depends on what you need. Could definitely be a plugin. |
Iirc, the original proposal ultimately did not go anywhere because all general purpose solutions can easily be made via script/plugins and all performance-optimized graphs have to be tailored for their specific use cases with custom implementations. Thus, making a low-level implementation in core that can be generally used for all games isn't really conceivable. Edit: now, GUI nodes for connectable nodes as a graph would be useful, but I already have a separate proposal for that. |
Considering the Astar implementation, which is already in core, needs some sort of a graph too, it makes more sense to expose that graph to scripts instead of requiring plugins? |
Such a module exists, it's called Goost, and I'm a maintainer/creator of it. 😉 This is certainly a feature worth to be implemented (added to the list of approved proposals in goostengine/goost#7 now), even if it won't cover all possible use cases. For very basic single path graphs, you can also use a
The methods that you omit are the actual meat behind graph data structures, more than any other data structure that you could stumble upon, in my experience. I've done some procedural generation myself using single path graphs (racing game), generated minimum spanning tree for generating caves and so on (in GDScript). Those algorithms are not really specific, but of course could easily become specific.
If we come up with an implementation which could be useful for at least 30-40% of use cases, that's a win in my book! But of course, that goes against Godot's development philosophy somewhat. Therefore, implementing a data structure like this is likely better implemented in https://github.com/goostengine/goost, but of course that's up to Godot core developers to decide. At any rate, I think it's better that we first implement such a data structure in Goost as an experimental feature first, see if it works in practice, and then let Godot core developers reconsider this proposal. 🙂 But before starting to code, this actually requires quite serious design work. I'd study existing Graph APIs and come up with an API suitable for solving procedural generation specifically. Just posting this so people know about such possibility. I'm not completely proficient with graph theory, unfortunately, but I'm certainly interested. |
As a city building game developer, I had to roll out my own implementation of graph system. But I made it as similar as Godot's AStar Graph implementation, (except some features that are very specific to my game). You can find it here. I think that just exposing the underlying graph data structure of the AStar should be good enough for most purposes. |
The notion of vertices and edges are implementation details of AStar itself. The API previously proposed in godotengine/godot#15647 looks like a collection of separate classes that extend I'm leaning towards using separate classes for graph/vertex/edge as well. The most prominent advantage that I could think of right now is that it's much easier to navigate the graph:
By the way, |
By the way, when people try to use AStar for non-pathfinding purposes as a general-purpose graph or try to mimic it in some way, this already signifies that we need a dedicated solution (not necessarily a general-purpose one, if that's a concern). Here's to illustrate my point. I recall there was a discussion for decoupling state machine logic into a general-purpose In fact, when I stumble upon some general-purpose solution (like, a library which handles various degeneracies in input), I feel good because I no longer need to worry about validating or fixing my input (heck, it might not be in my control what kind of input I have), Of course, this almost always has a cost of a lower performance, but again, based on specific use case, this is could be acceptable. I feel discussions like this tend to derail from the original proposal, so feel free to chime in a meta proposal: #3864. |
Should a graph allow self-loops and multiple edges? Some call it a pseudograph, but some recognize it as a general graph, and call a graph without self-loops and multiple edges just as a simple graph. Whatever the naming convention, does it make sense to impose some kind of constraints to a graph, or should it be users' responsibility? I think that a general-purpose graph should be allowed to have any kind of structure in my opinion, but maybe we could add several properties like |
This is the way to go imho. Properties (or methods) that allow optional specialized things, but the core should be general. |
I have created an initial implementation of a graph, see goostengine/goost#172 (still work-in-progress), I'm interested in early feedback since it's about coming up with core API.
I took some inspiration from it, and adapted to some naming conventions in Godot. I think it's time to actually solve some concrete problem with current implementation, and see how it works out. |
Still working on goostengine/goost#172, API so far:
|
Implemented Kruskal's algorithm for finding minimum spanning tree in goostengine/goost#172: Or a maximum spanning tree, all you have to do is to inverse the weights for each edge: The performance is acceptable for moderate number of edges (takes 100 msec given 10000 edges). For the particular case of generating Euclidean minimum spanning tree (like above), this could be improved with Delaunay triangulation for general case. I've seen https://github.com/rakai93/godot_voronoi module that allows to do something like this, and apparently uses a graph structure as well. So at some point it would be probably better to find a way to do Delaunay triangulation while retaining the structure, like in https://github.com/hiulit/Delaunator-GDScript. Note that I had to fork Godot's Also added serialization support for I'd say the current implementation is quite usable already. |
@Xrayez: I have a GDScript delaunay implementation around, but no idea how performant my shoddy job is. One general graph thing I would request is finding loops. Python NetworkX library does it and I always planned to implement it using NetworkX as reference, for my procedural city, but never got around to it ;) |
I recommend using the delaunay triangulation in godot math. |
I'm sure there has to be a way to do this with current API. The solution depends on whether you'd like to:
Note that trees should not contain cycles by definition. Therefore, you can run There's an answer at StackOverflow which suggests to use Johnson's algorithm, which NetworkX appears to implement.
Note that there exist the geometric Delaunay triangulation I've exposed previously in godotengine/godot#29127. I think the output of |
I think of the three options you mentioned,
is the best since it allows achieving 1 and 2 also (if there's more than 1 cycle found, you have a cycle) Good point about trees not containing cycles, though - I might try that trick. |
Implemented Dijsktra's algorithm in goostengine/goost#172 (via This actually constructs an entire shortest path tree, from which you can collect all shortest path starting from any source root: The main use case is certainly procedural generation and simple path finding. I think this won't be efficient to use compared to
Jonhson's algorithm uses Dijsktra's algorithm as a subroutine, so I guess this could eventually help to implement a method to find cycles in the graph.
but again, this doesn't seem to be a trivial problem to solve, akin to finding longest path in the graph which is NP-hard. I may be wrong, though. I found a snippet if someone would be willing to implement: https://github.com/qpwo/python-simple-cycles/blob/master/johnson.py |
The
This was actually more or less straightforward to implement: The naive implementation would involve generating a complete graph (every possible edge between vertices) and generating MST from that. Using Delaunay triangulation first, I confirm that this produces identical trees, and is much faster to generate (up to x100 faster or more depending on point count). func delaunay_mst():
var points = generate_points()
var I = Geometry.triangulate_delaunay_2d(points)
graph = Graph.new()
var V = []
for p in points:
V.push_back(graph.add_vertex(p))
for idx in range(0, I.size(), 3):
var tri = [V[I[idx + 0]], V[I[idx + 1]], V[I[idx + 2]]]
for i in tri.size():
var a = tri[i]
for j in range(i + 1, tri.size()):
var b = tri[j]
if not graph.has_edge(a, b):
var w = a.value.distance_to(b.value)
graph.add_edge(a, b, w)
return graph.minimum_spanning_tree() See example project at: |
While working on manifold I found https://github.com/haasdo95/graphlite. References |
Bump bump. I find myself needing a heap/priority queue. I understand AStar uses one since godotengine/godot#28925 Also would be nice to have documented somewhere what data structure Astar uses for neighbors (and potentially exposed so that we can use it for Dijkstra/BFS) |
Since now goost isn't maintained anymore for godot4, would need either extract goost graph into a module or extension, create one from scratch, or make a wrapper from an other library. Is there someone who have tried the last solution ? I have seen librairies like BGL, Melon that could be good candidates for a more general purpose graph (I would also be curious about a benchmark beetween this solutions and the legacy AStar) |
Tried to make a pure GDScript version of the goost graph for godot 4 but it's complicated. the source being in C++, there's a lot of things I don't really understand. I'm still searching online if there's something someone made already but, well, no luck so far. I guess i'll have to implement that code myself. (painfully ...) |
Describe the project you are working on
Just reopening, in the GIP tracker, an old proposal that was closed in the main tracker with the migration to the GIP tracker.
I have conducted a search in the GIP tracker and did not see any similar proposal listed. And as far as I'm aware, this feature has not been implemented. The old proposal was by @willnationsdev and is at:
godotengine/godot#15647
Sample game uses can include modeling any of the following:
( https://godotengine.org/qa/108704/best-approach-to-model-a-road-network
https://www.reddit.com/r/godot/comments/pm4mao/how_to_auto_tile_a_topdown_conveyor_belt/ )
Describe the problem or limitation you are having in your project
As noted in the old proposal, there can sometimes be a need to get access to a graph or tree data structure in the script code without having to rely on Control nodes like Tree or GraphNode and without having to rely on the Astar class. Astar is use-case oriented in that it is basically a graph for the sole purpose of pathfinding,
A graph data structure can have more uses than the Astar class, such as when no pathfinding is necessary. Furthermore, using Control nodes for this might be more computationally expensive than rolling a custom implementation in GDScript.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
As noted in the old proposal, Godot could benefit from providing a Reference object type that gives access to a graph data structure of vertices and edges. Both undirected edges and directed edges should be supported. The same goes for both unlabeled and labeled edges, as well as for both unlabeled and labeled vertices.
The advantages of exposing this to scripting could be pretty awesome for making data structures in projects. You could create scripts that use it as a graph or convert it into a binary search tree, a splay tree, or whatever you want.
An example from the Python world would be NetworkX: https://github.com/networkx/networkx
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
It can be like how TreeItems work with the Tree, we could have a DataGraph (Reference, facade), DataGraphVertex (Object 0+ labels & a Variant meta), and DataGraphEdge (Object with 1 label & a Variant meta). This format would keep it as flexible as possible.
If this enhancement will not be used often, can it be worked around with a few lines of script?
Rolling a custom implementation of a graph data structure in GDScript would involve more than just a few lines of code.
Is there a reason why this should be core and not an add-on in the asset library?
As for the placement, how about we create a module that is dedicated to providing generic data structure and algorithm extensions to the core? That would open things up for successive additions of a similar nature and give the DataGraph a comfortable place to live outside of core.
For instance, we can create a module specifically for "data_structures" and having a subdirectory for a DataGraph class and its associated classes.
The text was updated successfully, but these errors were encountered: