Grids 2 is out: What changed
Grids 2 has finally been released in the asset store. As we explained before, it is a major upgrade from Grids 1. In this post we go over the most important list of changes from Grids 1.
One thing we have not completely taken into account is how much work it would be to convert the more than 70 examples we have for Grids 1 to Grids 2. Rather than delay the release even further, we decided to ship with a minimal set of examples, and add new ones gradually. The same goes for the tutorials; over the next few weeks we will add some tutorials, starting with the most common tasks. In the meantime, please ask us any questions on the Knowledge Base.
You may also want to read the Key Concepts (explained from a pure Grids 2 viewpoint).
Main changes from the previous version of Grids
Grids 2 has only 2 types of grid points. All 2D grid points (such as RectPoint, PointyHexPoint) have been replaced with GridPoint2, and all 3D grid points (LayeredPoint) with GridPoint3. All 1D grid points LinePoints use integers now instead. GridPoints are meant to be the integer counterparts of Vector2 and Vector3. Certain of the old functions (such as distance functions) are specific depending on the point type. Those still exist – now as static methods in classes such as RectPoint (which are static classes with no data).
There are only 3 types of built-in grids. Grid1, Grid2, Grid3. Grid1 is indexed with integers, Grid2 with GridPoint2, and Grid3 with GridPoint3. Grids don’t “know” whether they are hex grids or rect grids or any other type of grid. Grid1 corresponds to the LineGrids in Grids 1, and Grid3 corresponds roughly to LayeredGrids in Grids 1, but uses a “proper” 3D representation instead.
This also means there are no more spliced grids (such as specific grids for tri grids and rhom grids) or nested grids. Both these are now simply represented with grids that contain grids (or, in many cases, something even simpler).
Grids don’t know how neighbors are connected. In Grids 1, grids had a GetNeighbors method. On the one hand, it does not make sense for the new grid types to have these functions (since it depends on the grid type, which is not something the grid knows), and on the other hand we made the idea of “neighbor” more flexible (for example, you can define neighbors to be squares in a chessboard that can be reached by a knight). Functions that use neighbors take an extra parameter that specifies how neighbors should be calculated. For your own needs, you can define your own function, or use one of the ones provided (that correspond to the simple cases of direct neighbors in rect and hex grids).
Algorithms that work on lines and rays have been generalized. You can now use a more general description for a line (using maps described below) so that match algorithms can be used in more cases. As with neighbor definitions, line definitions are provided for simple cases.
The type arguments in the IGrid interface has been swapped. All grids still implement the IGrid interface. The new order reflect the order used by dictionaries (index type followed by contents type), which helps emphasize that grids are just special dictionaries with grid points as keys.
Maps have been generalized. Maps in Grids 1 were used to convert between grid points and world points. Maps work now more like the mathematical maps after which they have been named, and they can do general conversions on any type to any other type, much like the Func<t, tresult=””> class. The main difference is that maps are roughly invertible. They support Forward and Reverse methods. Maps can be composed and inverted. Because maps can be from one type to the same type, the indexing syntax does not work anymore. It is now necessary to call Forward and Reverse explicitly.
Concrete map types are hidden. All maps implement the IMap interface, and all concrete maps are hidden. To construct built in maps, you need to use the functions in the static Map class. This design reflect the design of the new generators. Dozens of small classes are needed to implement operations supported in Grids2. There is no need to know these classes (especially since inheritance is an especially poor choice to make a new map, and no map has any methods other than the interface methods).
Maps used with grids now convert between grid-space vectors and Unity world space. Before, maps only converted between grid points and world points. There was no concept of a grid point between (0, 0) and (1, 0). Now, there is a concept of “fractional” grid points (and we simply use the Vector classes to represent those). This makes it, for example, easy to build meshes, and work with vertices and edges of cells. For example, the point (1/3, 1/3) represents the north-east vertex of a pointy hex cell; you can now pop that into a map and find the world position of that vertex. Because of this unification of grid points and vectors, we dropped the concept of “edge” and “vertex” grids. We may revive it, but so far it looks like all algorithms can be implemented using coordinates directly.
We added float and integer matrices for 2D and 3D that work with vectors and grid points. Using linear transform can simplify many geometric algorithms. We added these specifically to allow implementation of various rect grid ideas for hexes, for example, septet-trees (similar to quad-trees, but for hexagonal grids) and hexagonal Perlin (or Perlin-related) noise.
Grid shapes has been reworked. Many grid algorithms only work on grid shapes, not on the actual data. In Grids1 it was necessary to build these algorithms with empty grids, but this is not always convenient (especially when the grid shapes are very big, or thousands are necessary). For this reason we wanted to separate the concept of a grid shape from the actual data structure “arranged” in that shape. Moreover, the old shape technology was very complicated and it had some very hard-to-fix (and luckily, hard to stumble upon) bugs. Finally, the old shape technology was very hidden and mysterious (even to us). It is not exactly clear how a shape gets turned into a grid.
The new technology addresses all these issues. There are now two types of shapes implicit shapes are objects that can tell whether a given grid point is inside the shape or not. An explicit shape is a shape that knows its bounds, and moreover can produce all the points inside the shape. Grids are explicit shapes that can hold a piece of data (the cell) at each point. It may sound a little abstract, but in practice it is simple to use these concepts – make a function that defines a shape, turn this into an implicit shape, turn this into an explicit shape by giving it bounds, and turn it into a grid. There are many common implicit shapes defined (and for most games, you should be able to set up shapes using the node editor instead).
We also plan to define some abbreviations for common scenarios in the future, but for now we are happy that the system is easy to extend in use in a wide variety of cases, without needing very deep knowledge of how the system works.
There are new classes for float and integer intervals, integer rectangles and integer bounds. This makes it useful for certain geometric algorithms. The discrete versions of these are explicit shapes, so they also work with other shape algorithms.
Maps and shapes can be built in the editor using a node system. This makes it easy to create and modify maps and shapes. Over the last two years or so, we made examples of grids warped into spheres, cylinders, torusses, helixes, and Mobius bands; in each case we had to make a custom map. It is now possible to make all these maps with the node editor.
GridBuilders have been reworked. There used to be a grid builder for each type of grid. Now, there is only one grid builder for each representation type. We ship with tile grid builders and mesh grid builders, that can be used for all types of grids. Grid builders themselves are much simpler too. Things like dimensions, shapes and so on are now handled by the node editors.
Grid input is now handled through an event system. In Grids 1, you could define OnClick methods in either your GridBehaviour or cells. In Grids 2, you can add a GridEventTrigger that allows you to hook up a method from any component. The event trigger is also the new home for the MousePosition property (which returns the mouse position in grid coordinates).
Mesh building technology has been unified. We made the mesh generation technology (which before only existed as examples and a few special cases) much more general. A mesh can now be built for any type of (periodic) grid; all you have to do is provide a map (which you need for any representation in any case), and how each cell should be constructed in terms of grid-space coordinates. For example, the vertices of a hex can be specified as (1/3, 1/3), (2/3, -1/3) and so on. The map takes care of how those are interpreted in world space. This means if your map transforms your grid onto some shape such as a torus, you don’t need to change the cell construction coordinates at all. It will just work.