how to create a dynamic 2d color changing hex grid

alt text

in the unity gui I would like to create a hex grid map such as the one in the link , but each hex can change their fill color based on information in an array. Would it be better for me to do several draw calls with texture with custom hex texture images Graphics.DrawTexture(Rect(10, 10, 100, 100), aTexture); and set the color for the texture(not sure if i can). any help is appreciated.

Creating the hex grid out of individual game objects is problematic. That is 1024 game object for a 32 x 32 grid array and 4096 for a 64 x 64 array. And if you change the color of these game object using the Material.color property, the resulting game objects will not batch. The potential result would be 4000+ draw calls. From a performance standpoint, especially if you are targeting mobile, things would get ugly.

As an alternate, you can build a hex array using a mesh and use a vertex shader. I don’t believe there are any vertex shaders included in the built-in shaders, but there are several in the Unity Wiki:

[http://wiki.unity3d.com/index.php?title=Shaders][1]

In addition, for this task, you may want to use a very simple vertex shader that does not have any texture. Put this shader in your project:

Shader "Custom/Vertex Colored" {
Properties {
}
    SubShader {
            Pass {
                    ColorMaterial AmbientAndDiffuse
            }
    } 
}

I’m posting a script below. To use:

  • Create a new material and use the shader above.
  • Attach the script to an empty game object
  • Drag and drop the material using that shader onto the ‘material’ variable of the script.

#pragma strict

var columns  	= 10; 
var rows 		= 10;
var cellRadius 	= 0.5;
var material : Material;

private var colors : Color[];
private var mesh : Mesh;


function Start () {
	gameObject.AddComponent(MeshRenderer);
	renderer.material = material;

	CreateHexMesh();

	// Create a colors array for the mesh
	colors = new Color[columns * rows * 7];
	for (var i = 0; i < colors.Length; i+=7) {
		var color = Color(Random.value, Random.value, Random.value, 1.0);
		for (var j = 0; j < 7; j++) {
			colors[i+j] = color;
		}
	}
	mesh.colors = colors;
		
	SetColor(1,1,Color.red);
	SetColor(rows-1, columns-1, Color.black);
}


// Creates a mesh of hex objects 
function CreateHexMesh() {
	var mf = gameObject.AddComponent(MeshFilter);
	mesh = new Mesh();
	mf.mesh = mesh;
	
	var vertSpacing = 2.0 * Mathf.Cos(30.0 * Mathf.Deg2Rad) * cellRadius;
	var horzSpacing = cellRadius + Mathf.Sin(30.0 * Mathf.Deg2Rad) * cellRadius;
	
	var currPos = Vector3(-columns / 2.0 * horzSpacing, rows / 2.0 * vertSpacing, 0.0); 
	
	// Layout vertices for a single hex cell
	var hexVerts : Vector3[] = new Vector3[7];
	hexVerts[0] = Vector3.zero;
	var v = Vector3.right * cellRadius;
	
	for (var i = 1; i < 7; i++) {
		hexVerts *= v;*

_ v = Quaternion.AngleAxis(60.0, -Vector3.forward) * v;_

  • }*

  • // Create the vertices*
    _ var vertices = new Vector3[rows * columns * 7];_

  • var currVert = 0;*

  • for (i = 0; i < columns; i++) {*

  •  for (var j = 0; j < rows; j++) {*
    
  •  	for (var k = 0; k < hexVerts.Length; k++) {*
    
  •  		vertices[currVert++] = hexVerts[k] + currPos;*
    
  •  	}*
    
  •  	currPos.y -= vertSpacing;*
    
  •  }*
    
  •  currPos.x += horzSpacing;*
    

_ currPos.y = rows / 2.0 * vertSpacing;_

  •  if (i % 2 == 0)* 
    
  •  	currPos.y -= vertSpacing / 2.0;*
    
  • }*

  • mf.mesh.vertices = vertices;*

  • //Create the triangles*

  • var curr = 0;*
    _ var triangles : int = new int[rows * columns * 6 * 3];_
    _ for (i = 0; i < columns * rows * 7; i += 7) {_

  •  for (j = 0; j < 6; j++) {*
    
  •  	if (j != 5) {*
    
  •  		triangles[curr++] = i;*
    
  •  		triangles[curr++] = i+j+1;*
    
  •  		triangles[curr++] = i+j+2;*
    
  •  	}*
    
  •  	else {*
    
  •  		triangles[curr++] = i;*
    
  •  		triangles[curr++] = i+j+1;*
    
  •  		triangles[curr++] = i+1;*
    
  •  	}*
    
  •  }*
    
  • }*

  • mf.mesh.triangles = triangles;*

  • mf.mesh.colors = colors;*

  • mf.mesh.RecalculateBounds();*

  • mf.mesh.RecalculateNormals();*
    }

function SetColor(row : int, col : int, color : Color) {

  • if (row < 0 || row >= rows) return;*
  • if (col < 0 || col >= columns) return;*

_ var base = col * rows * 7 + row * 7;_

  • var colors = mesh.colors;*
  • for (var i = base; i < base + 7; i++)*
    _ colors = color;_

* mesh.colors = colors;*
}
Note the function called SetColor(). This function can be used to set the color of any hex square based on its row/col position.

_*[1]: http://wiki.unity3d.com/index.php?title=Shaders*_

Here is a new script that is largely based on the previous script. It uses UV coordinates and a texture atlas to color the cells rather than a vertex shader. So step 1 is to create a new material. You can use Unlit/Texture as the shader, and use this for the texture of the material:

[22112-hexatlas2.png*_|22112]

Here is the script. It no longer uses colors array in the mesh, but now it uses the uv array.

#pragma strict

var columns  	= 10; 
var rows 		= 10;
var cellRadius 	= 0.5;
var material : Material;

var uvRows = 2;  // Number of rows in the texture
var uvCols = 2;  // Number of cols in the texture

private var colors : Color[];
private var mesh : Mesh;

private var uvVerts : Vector2[] = new Vector2[6];
private var uvRadius : float;
private var uvDefault : Vector2;

function Start () {
	uvRadius = 1.0 / uvCols / 2.0;
	uvDefault = Vector2(uvRadius, uvRadius);
	gameObject.AddComponent(MeshRenderer);
	renderer.material = material;

	CreateHexMesh();
	
	// Set all the cells to different colors
	for (var i = 0; i < rows; i++) {
		for (var j = 0; j < columns; j++) {
			SetUV(i,j,i%2,j%2);
		}
	}
}

// Creates a mesh of hex objects 
function CreateHexMesh() {
	var mf = gameObject.AddComponent(MeshFilter);
	mesh = new Mesh();
	mf.mesh = mesh;
	
	var vertSpacing = 2.0 * Mathf.Cos(30.0 * Mathf.Deg2Rad) * cellRadius;
	var horzSpacing = cellRadius + Mathf.Sin(30.0 * Mathf.Deg2Rad) * cellRadius;
	
	var currPos = Vector3(-columns / 2.0 * horzSpacing, rows / 2.0 * vertSpacing, 0.0); 
	
	// Layout vertices for a single hex cell
	var hexVerts : Vector3[] = new Vector3[6];
	hexVerts[0] = Vector3.zero;
	var v = Vector3.right;
	
	for (var i = 0; i < 6; i++) {
		hexVerts _= v * cellRadius;_

uvVerts = v * uvRadius;
_ v = Quaternion.AngleAxis(60.0, -Vector3.forward) * v;_
* }*

* // Create the vertices*
_ var vertices = new Vector3[rows * columns * 6];
* var currVert = 0;*
* for (i = 0; i < columns; i++) {*
* for (var j = 0; j < rows; j++) {*
* for (var k = 0; k < hexVerts.Length; k++) {*
* vertices[currVert++] = hexVerts[k] + currPos;*
* }*
* currPos.y -= vertSpacing;*
* }*
* currPos.x += horzSpacing;*
currPos.y = rows / 2.0 * vertSpacing;_

* if (i % 2 == 0)*
* currPos.y -= vertSpacing / 2.0;*
* }*

* mf.mesh.vertices = vertices;*

* //Create the triangles*
* var curr = 0;*
_ var triangles : int = new int[rows * columns * 4 * 3];
for (i = 0; i < columns * rows * 6; i += 6) {_

* triangles[curr++] = i+0;*
* triangles[curr++] = i+1;*
* triangles[curr++] = i+5;*

* triangles[curr++] = i+5;*
* triangles[curr++] = i+1;*
* triangles[curr++] = i+4;*

* triangles[curr++] = i+4;*
* triangles[curr++] = i+1;*
* triangles[curr++] = i+2;*

* triangles[curr++] = i+2;*
* triangles[curr++] = i+3;*
* triangles[curr++] = i+4;*
* }*

* mf.mesh.triangles = triangles;*

* // Setup a based set of UV coordinates*
* var uvs : Vector2[] = new Vector2[vertices.Length];*
* for (i = 0; i < vertices.Length; i += 6) {*
* for (j = 0; j < 6; j++) {*
* uvs[i+j] = uvVerts[j] + uvDefault;*
* }*
* }*

* mesh.uv = uvs;*
* mesh.colors = colors;*
* mesh.RecalculateBounds();*
* mesh.RecalculateNormals();*
}

// Sets a particular cell to a particular cell on the atlas
function SetUV(row : int, col : int, atlasRow : int, atlasCol : int) {
* if (row < 0 || row >= rows) return;*
* if (col < 0 || col >= columns) return;*
* if (atlasRow < 0 || atlasRow >= uvRows) return;*
* if (atlasCol < 0 || atlasCol >= uvCols) return;*

* var uvs = mesh.uv;*

_ var base = col * rows * 6 + row * 6;
var offset = Vector2(atlasCol * uvRadius * 2.0 + uvRadius, atlasRow * uvRadius * 2.0 + uvRadius);
* for (var i = 0; i < 6; i++) {*
uvs[base + i] = uvVerts + offset;
* }
mesh.uv = uvs;
}*_

And here is the result:
[22113-hex_atlas_example.jpg|22113]*
Note this atlas can be expanded to more colors. You can edit the width of the black to change the thickness of the lines. You could even add symbols. While the output is regular, the SetUV() allows you to set any specific cell to any entry in the atlas.
_*
*