What are shapes?
Shapes come in two varieties.
Implicit shapes are objects that can tell whether a point is inside the shape or not.
Explicit shapes are implicit shapes that also know their bounds, and can produce all the points inside it.
You can also see a shape as a set of points.
Where are shapes used in Grids 2?
You can do grid geometry using grid points and matrices without reference to any grid. But usually, game grids are finite, and some grid points are inside the grid, and some are not. We use grid shapes to handle this concept. When you construct a grid in code, you have to give the constructor an explicit shape. Behind the scenes this is used to allocate the memory for your cells.
Grids also implement the IExplicitShape interface; in this sense grids are explicit shapes – you can ask whether a point is inside the grid or not, you can get the bounds of your grid, and you can get a list of all points inside the grid. This means any algorithm that takes a shape can also take a grid, which is very useful for algorithm design.
How are bounds represented?
Bounds are represented through 3 types of object depending on whether the shape is 1D, 2D or 3D. For 1D, we use the GridInterval class. GridInterval represents a sequence of integers between two values. GridRect is just like Unity’s Rect class, but works with grid points instead of Vector2. It represents an axis-aligned rectangle in grid space. Similarly, GridBounds is like Unity’s Bounds object, but works with grid points instead of Vector3.
All three these objects are explicit shapes too, and this is useful to remember when designing algorithms. In particular, you can iterate over the points of a bounds object, which is neater than using nested loops and creating grid points on the fly.
What operations are supported on shapes?
Shapes (both implicit and explicit) support basic set operations:
- Union (the resulting shape contains the points of all input shapes).
- Intersect (the resulting shape contains the points that are in all input shapes).
- Subtract (the resulting shape contains the points of one shape that are not in another).
There is also a product operation that takes two input shapes, A and B, and a scaling vector (a grid point). All the points of A are scaled by this vector, and added to each point of B. The resulting set of points are the ones inside this new shape.
What does the shape node editor do exactly?
It allows you to build an explicit shape in the editor. You can do the same in code, but it is often easier to experiment to get the right shape in the editor. Grid builder use explicit shapes to construct grids, but you can also use them for other purposes.
To make a new shape graph, you can Shape X Graph from Assets | Create | Grids in the menu or context menu in the project window (where X is 1 ,2 or 3 depending on the number of dimensions you are working with). To edit the graph, select it, and click on the Edit button in the inspector. To build the shape graph, follow these steps:
- Add the nodes you need to make the shape you require, and set their fields to the correct values. To remove a node, click on the “Remove” button on the node. To remove all nodes, click the “Clear” button at the top of the node editor tool bar.
- Add one output node.
- Link them together, with the output node at the end of the sequence. To link the output of node A to the input of node B, click on the “Add link” button on node A, and click on node B. To remove a link, click on the red dot on the link.
- In complicated graphs, you may want to rename the nodes so it is easier to know what is going on. You can do this by clicking on the node name and typing a new name. (Since Grids 2.1)
- While tweaking the node graph, it may be useful to skip over a node temporarily. You can do this by clicking on the “Disable” button. This will make the node simply route its input to its output without processing. (Since Grids 2.1)
What does the Recompute button do?
It lets all nodes recompute and save values for internal use. This is specifically designed for nodes with a random element, to allow nodes to select new random values. (In general, you don’t want these to be recomputed every time the shape is accessed). Currently, all nodes are deterministic, so this feature is not used. We may introduce random nodes in the future, or you may want to add your own random nodes.
What do all the nodes do exactly?
Nodes come in several varieties.
The output node marks the resulting shape of the graph.
Operator nodes take other shapes as inputs, and perform the operations described earlier. The Union and Intersection nodes can take any number of inputs, while the subtract and product nodes take to inputs each.
Primitive nodes describe basic shapes such as stars or parallelograms.
For 3D shapes, there are additional nodes (since Grids 2.2) that construct 3D shapes from 2D shapes.
- Simple Layer Shape takes a 2D shape, and repeat it on a specified number of layers.
- Multi-Layer Shape takes a list of 2D shapes, and puts each one on a separate layer.
- Orthogonal Projection takes 3 2D shapes, each representing the projection of the shape on one of the planes (XY, XZ and YZ).
Can I make my own shape nodes?
Yes!
If your shape is primitive (it does not require other shapes as input), you can extend a new class from PrimitiveShapeNode (where T is int, GridPoint2 or GridPoint3), and override the Generate method.
If you implement a shape operator, you can extend from AggregateShapeNode<T> and override the Aggregate method.
In either case, you need to add the ShapeNode attribute to your class. It takes two parameters, a string and an integer. The first is a name. If you want to group the node with other nodes you can give it a name like this: “Group Name/Node Name”. The integer parameter denotes the number of dimensions of the shape, and should correspond to the type T referred to above.
Both the Generate and Aggregate methods simply needs to return a shape. To build a shape in code usually takes three steps:
- Make an implicit shape.
- Make a bounds object that will contain the shape.
- Call ToExplicit on the implicit shape and feed it the bounds.
In some cases, you can work with explicit shapes directly. There are many functions that help with shape construction; explore the documentation of ExplicitShape and ImplicitShape,