Customizable Game Grid

SOLO
Deliverone FutureGames Game Project 1
Category

Solo Projects

Year

2020-2021

Project Duration

During free time over several months

Context

Unity Asset Store Tool

Tools

Unity, C#

Features

Grid System, Object Spawner

TL;DR

The customizable game grid is a tool for Unity which I made during my free time over a period of several months. I wanted to see how far I could go with it and also to learn tooling in Unity as well as writing really polished code.

Project Breakdown

Introduction

The customizable game grid is a tool for Unity, it can be used to make grids that can be visualized using lines (both in editor and during runtime). Objects can also be spawned on the grid with the included object spawner using a variety of different spawning modes. In specific spawning modes objects can be drawn on the grid, which makes the creation of grid-based maps super quick.

Examples of usages:

  • Quick creation of static or procedural grid based maps with the object spawner.
  • Basis for path-finding algorithms like A*.
  • Basis for grid based movement in RTS games etc.
  • Grid based building in village games or sandbox games etc.

I started developing the customizable game grid during the data structures and algorithms course at FG since I needed a grid for the game I was making as the final assignment. When the course was over I decided to keep developing it. To see how I worked when my intention is to create a super polished product, perhaps something I can release on the asset store. I’m quite happy with the end result, it’s a very advanced system that can be used for many different applications. Here is a walkthrough video, to get an idea of how the grid is used:

The Code

This is the class diagram for the customizable game grid, there are a fair few classes and scripts that work together to make the tool tick (the top row is the legend description while the bottom is code that doesn’t depend on anything else).

The Grid2D

The core of the grid is the Grid2D class itself. It’s a raw C# class that can be constructed in any MonoBehavior script. It is designed to be as light weight as possible for if you want to construct grids during runtime etc. It takes in the size of the grid, scale of each grid point, axes it should be aligned to, origin position, origin rotation etc. This is how the standard constructor looks like which assumes the origin of the grid is 0,0,0 and rotation is Quaternion.identity:

[code lang="csharp"]
public enum Axes
{
    XY,
    XZ,
    ZY
}
 
///
/// Empty constructor assumes the grid's origin position is 0,0,0 and rotation is Quaternion.identity
/// 
public Grid2D(Axes axes = Axes.XZ, int width = 1, int length = 1, float scaleWidth = 1f, float scaleLength = 1f,
    bool calculateGridLines = true, bool customizeLineWidth = false, float customLineWidth = 0f)
{
    Generate(axes, width, length, scaleWidth, scaleLength, calculateGridLines, customizeLineWidth, customLineWidth);
}
 
///
/// Takes grid values and then calculates the grid based on those values
/// 
public void Generate(Axes axes = Axes.XZ, int width = 1, int length = 1, float scaleWidth = 1f, float scaleLength = 1f, 
    bool calculateGridLines = true, bool customizeLineWidth = false, float customLineWidth = 0f)
{
    gridAxes = axes;
    gridPointWidth = width;
    gridPointLength = length;
     
    this.scaleWidth = scaleWidth;
    this.scaleLength = scaleLength;
     
    this.calculateGridLines = calculateGridLines;
    this.customizeLineWidth = customizeLineWidth;
    this.customLineWidth = customLineWidth;
     
    CalculateGrid();
}
[/code]

The CalculateGrid method will loop over all the grid points and calculate their local space position (based on what the origin of the grid is, which might be a fixed position or a transform), Matrix4x4’s are used to figure out the grid points world space position. After the grid points are calculated the grid line indices are also calculated (used when drawing the grid lines), they are either lines for 0 width or quads for a custom width. The indices are also in local space, this makes the grid movable without recalculating the grid. There are methods to set the grid’s origin which will update any necessary transform references or matrices etc. Then there are many utility methods for transforming grid points, drawing grid lines, etc. Here is a list of them:

  • DrawGridLines – which will draw the grid lines based on the grid vertex array using GL (this only works at runtime).
  • DrawGridLinesEditor – same as previous, but using Gizmos instead (this only works in editor)
  • DrawGridCornersInEditor – draws gizmo spheres on the corners (this only works in editor)
  • DrawGridIndices – draws the index of each grid point as 3d text using handles (this only works in editor)
  • GetAdjacentIndex – given a current index and a direction (enum with members such as PositiveX, NegativeY etc.) it will give the adjacent index in that direction or -1 if the direction is not valid.
  • GetWorldPointFromIndex – gives the position of the given grid point index in world space.
  • GetGridPointsWorld – returns an array of all grid points world space position.
  • GetClosestIndex – returns the closest grid point index given a world space position (calculated completely using raw math).
  • IsPointOccupied – checks if the grid point is occupied.
  • SetPointOccupied – sets the grid point to be occupied with a given input C# object that should occupy that point.
  • ResetPointOccupied – sets the grid point to not be occupied if it previously was.
  • CalculateObjectRotation – returns the rotation an object would have if its up vector would be parallel to the depth axis vector (Y for XZ axes etc).
  • GetMiddleIndex – Returns the middle index of the grid.
  • GridPointIndexToGridCoord – given a grid point index, will return the grid point coordinate of that point (Vector2Int).
  • GridCoordToGridPointIndex – given a grid point coordinate, will return the grid point index of that point.

All of these methods make it easier to work with the grid.

The Grid2DMono

If the user of the grid prefers to generate the grid in the editor, that can also be done using the Grid2DMono MonoBehaviour script. Basically, it’s just a wrapper for the grid2d class and has many of the same utility methods forwarded, while other methods such as the draw methods are called in OnDrawGizmos/OnRenderObject etc. There is also a singleton version of it which makes it possible to get a static reference to it with Grid2DMonoSingleton.Instance. To be able to generate the grid when any properties are changed it utilizes a custom inspector that looks like this:

When a property (that requires a regeneration of the grid) is changed it will call CalculateGrid() in the internal Grid2D class.

The ObjectSpawner

The part of the customizable game grid I am most proud of is the object spawner which can be attached to a Grid2DMono (or it’s singleton counterpart) to spawn objects in a variety of ways that are aligned to each grid point. To get a better understanding of how the object spawner works I would suggest watching the tutorial/walkthrough video earlier in this post. This is how the inspector looks for the object spawner:

As you can see, this is also a custom inspector utilizing the editor library I wrote specifically for the grid (but has also been used in Dream Walker for example). I have a lot of spawning modes, here is a list of them:

  • SpecifiedIndices
  • SpecifiedCoordinates
  • AllIndices
  • RandomIndices
  • RangeOfRandomIndices
  • Checkered
  • CheckeredInverted
  • Corners
  • Edges
  • SingleStretchedOverEntireGrid
  • StretchedOverEdges

Utilizing these you can do a wide variety of things, from quickly making a chess board that can be scaled up procedurally to making maps that can have features that generates at a random grid point or a range of random grid points. You can also draw objects on the grid using either specified indices or specified coordinates to, for instance quickly make a maze etc.

This is easily my most advanced script because there is so many combinations to cover. I learned a lot about quaternion multiplication and other quaternion methods to handle all the rotational math. I also had to dig into the source code for Handles.Button to figure out how I could detect when the mouse is dragging over the button instead of just detecting a click. To make it work I had to copy most of the handles code and rewrite some of it and put it in the editor library.

I am very pleased with how the grid turned out, and I can say that I’ve learned a lot in the process. It has now been released on the asset store and I couldn’t be more proud.