How to create a dynamic 2d color changing hex grid (95645)

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.

You say 'dynamic'. Does this mean it can grow? If so in what directions? Will you have borders like in the diagram? What is the maximum size of the grid? How will you map this grid to the array?

sorry by dynamic I just mean the colors are changing throughout the game. it is a map of sorts. this grid will be 32x32 or 64x64 so the array will be the same size and store data about the hexes.the borders would be nice but not completely needed.

2 Answers

2

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*_

P.S. In thinking about it, I could have created each hex cell out of four triangles and not used a center point. The solution above was the first one that came to mind.

> I just wanted to reconfirm that I > would need to create my own minimap > camera for your solution to show up on > the HUD for the 2d GUI correct? I don't know enough about HexTech, your game, or the setup to give you a good answer to this question. Does your HexTech have a minimap camera? If so, you likely could use theirs.

> I don't know enough about HexTech, > your game, or the setup to give you a > good answer to this question. Does > your HexTech have a minimap camera? If > so, you likely could use theirs. hex tech's mini-map function uses an image file with hexes that can't be manipulated. I need to be able to create my own to display similar to the theirs but I need to be able to manipulate the color or texture here is an image from it: ![alt text][1] [1]: http://forum.unity3d.com/attachment.php?attachmentid=38209&d=1347038765

The solution is a bit ugly, but you could color the texture used in their mini-map. The labels are the biggest problem to that solution, and it would be far easier to put a circle of color touching the sides of the hex cell rather than fill the cell. Can you put your own texture into their mini-map?

Hmm is there a different function to color on top like that? I do not need the text but I do need the hexes to be completely filled and well represented. There is also no function to draw a texture on the minimal. It is just a premade image from Photoshop that moves around as you view the map.

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.
_*
*

thanks for everything robert, I will definitely try this out. I learned so much :O!