What is coming in Grids 2.0
The new version of Grids is a major upgrade, with many design changes. Our main goal is to make Grids more flexible, more robust, and less unwieldly. The way it is designed at the moment makes it a chore to write generic algorithms – something that we realized when we started to build other tools on top of it. It is also difficult to extend – writing more editor tools require us to write almost duplicate code for every grid type. The upgrade solves these problems, and has many additional benefits: it has less code, is more powerful, is more generic with fewer type parameters everywhere 🙂
We’re happy to announce that current Grids Pro users wont need to pay for the upgrade. For now, here are some of the most important changes of Grids 2.0.
Simplified grid points
In the new design, there are only three point types: GridPoint1 (that replaces line points), GridPoint2 (that replaces rect points, pointy hex points, and so on), and GridPoint3 (that replaces layered grid points).
These types are meant to be the integer counterparts of float, Vector2, and Vector3. GridPoint1 is, like LinePoint1, auto convertible to integer. This makes writing mathematical code much easier.
- There are a bunch of rounding operations to get grid points from vectors (for example, a rect round and a pointy hex round.
- We are adding many operations for different grid systems such as modified dot and perp dot products, vector norms, that work on vectors and grid points.
- We are adding matrices (2×2, 3×3) that can be multiplied with vectors and grid points.
The new design does not have an IGridPoint interface, and so there is no type constraints on grid points. This makes generic code a bit easier to write, and allows more flexibility in designing new grids.
Similarly, we are replacing all the different grid types we have now with three grid types that differ only by their dimension: Grid1, Grid2, Grid3. These are still generic in their cell type, and there is still an interface Igrid generic in the point type and cell type (so you can still define your own grids, and have our algorithms work on them).
We are making grids mimic dictionaries more closely:
- We swapped the type arguments of IGrid, which is now IGrid<TPoint,TCell>.
- We added a property Points (which is similar to IDictionary.Keys)and we replaced the Values property with Cells.
- Grids are now enumerable over PointCellPairs (instead of points). This makes the idiom foreach(var point in Grid) slightly longer: foreach(var point in Grid.Points), but we think the consistency with dictionaries makes code easier to read and more intuitive to grasp for new users.
Shape construction is the most complicated part of Grids 1.*. It is in fact so complex that there is a subtle bug that we have not been able to fix (fortunately, the bug is such an edge case that I don’t think it affected any users). It is also too brittle; extending it is quite complex and requires a deep understanding of how the library works. Furthermore, it’s design is very functional, making it hard to save the shape of a grid in a file and so constrains what can be done with level editing tools.
The new design has a more data-oriented procedural design, and is much easier to understand and extend. The shape of a grid is now encoded explicitly by underlying objects (and not implicitly with delegates). We also define some algorithms on shapes instead of grids, which makes it possible to write geometric code without needing an full-fledged grid. It also means that many algorithms do not need to know the cell type, so that their generic definitions are less intimidating, and that fewer casting gymnastics are necessary.
We added more generic primitive shapes, and defined many shapes using simpler math that is possible because of the simplications of grid points, and the fact that they are now unified with Vectors. This means out of the box there are many more shapes than before.
Maps convert between grid points and vectors. Separating the geometry of a grid from its topology has been a powerful concept that made many interesting things possible (mapping grids to various 3D shapes, for example). In Grids 2 we are taking that concept even further. Maps are now generic functions that convert one thing to another thing. In this they are similar to Func<T, U>. We are introducing the idea of forward maps and reverse maps, and combine these in “reversable maps”. Maps can now be composed, and are more data driven so that they too can be serialized, making it possible to make better editor tools.
Because maps can also now convert grid points to grid points, the index notation is no longer available (before, both forward and backward operations could be performed with index operations because the types were different). This is a slight inconvenience, but it does mean coordinate conversions can now be made part of your map, which makes writing algorithms for grids that use different coordinates much easier.
Lines and neighbors
We already started to make neighbors configurable in earlier versions of the library, but making neighbors a property of the grid makes many algorithms awkward to write. In addition, our design required neighbors to satisfy some mathematical properties that constrained grids to be periodic.
In Grids 2.0, we are replacing the idea of neighbors with that of “connected cells”. Many algorithms that work on neighbors now take a function that for each point return a set of connected points, and we make now assumptions about this list of points. As one example of the consequences of this change: you can now use the path finding algorithm to calculate knight’s tours on a chess board.
In similar spirit, we are replacing the idea of rays and lines with “point succession”. Algorithms that used the concept of rays now takes a forward map from grid points to grid points, and algorithms that used the concept of lines now take a reversible map from points to points. Our Words library already uses this idea in some algorithms. Because we make no assumptions about the sequence of points that result, it means many algorithms can be applied to grids such as triangular grids that did not have the concept of “lines” in the old sense.
All the changes above makes it possible for us to improve the editor tools. The biggest change is that you do not need separate grid builders for different grids (with the same dimension). In the editor, the only distinction between a hex and rect grid is the map you assign to do point conversion. This makes it possible to experiment with different grid types, and write code that does not rely on any particular grid type.
- There is now a node editor for shapes. This makes it very easy to make grids of custom shapes. We also made it trivial to add your own nodes.
- There is also a node editor for making maps. This means you can now use editor tools for creating arbitrary grids. As with shape nodes, adding custom nodes are trivial.
- We took out colors from grid builders, as this does not apply to many games. Cells can now be colored with separate grid behaviours.
- Similarly, input events are added to a grid using a separate grid behaviour. We have also made it possible to assign our own handlers (and to do this in the editor), so no need any more to implement specific “OnClick” methods.
The bad news
If you read everything above, you probably realize Grids 2.0 will not be backwards compatible. It’s been a tough decision, but the benefits of a design unconstrained by the past outweighs the downside. We will provide guidelines for upgrading after we upgraded all our examples and we fully understand what is involved.