Multiple Hex placement grid
Hello, Noob here. I have a 2d project where I want to create a hex grid for unit placement. Currently I have a script that creates a hex mesh and then uses that mesh to create a hex grid.
I also implemented a mouse over color change. Which worked great. Then I switched it from reacting to the mouse pointer to reacting to a single hex I moved with the mouse. Again, worked great.
But what I really want is for it to react to multiple hexes, as the units will have varying sizes. Smallest unit may be 1 hex but a larger unit might take 3 or 7 hexes of space. At my current level of Unity noobness I can't figure out how to do this, nor the best way to go about it.
Currently I am applying a script to each individual grid hex that checks for a raycast and changes the color. This was based on an example I found to do the mouse over.
My idea was to create a group of 'selection hexes' that matched the size of the unit and as I move it around it would highlight a legitimate location to place it (i.e. unit size 7 finds 7 empty hexes). No idea if this is the way to go about it and currently what I have doesn't work anyway. I just took my working single example and put it in a loop. But, again, it doesn't work, it just highlights 1 hex at a time.
Any advice on how to best implement this? Best practices?
Code attached to hexes in the grid:
using UnityEngine;
[RequireComponent(typeof(Collider))]
public class GridHex : MonoBehaviour {
Color normalColor;
GameObject[] selectors;
//public Transform selectionObject;
void Awake() {
selectors = GameObject.FindGameObjectsWithTag("selectortile");
//selectionObject = GameObject.Find("HexagonSelectorTile").transform;
}
void Start() {
normalColor = GetComponent<Renderer>().material.color;
}
void Update () {
foreach (GameObject selector in selectors) {
//Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Ray ray = new Ray(selectionObject.position, Vector3.forward);
Ray ray = new Ray(selector.transform.position, Vector3.forward);
RaycastHit hitInfo;
Debug.DrawRay(ray.origin, ray.direction, Color.green);
if (GetComponent<Collider>().Raycast(ray, out hitInfo, Mathf.Infinity)) {
GetComponent<Renderer>().material.color = UnityEngine.Color.red;
} else {
GetComponent<Renderer>().material.color = normalColor;
}
}
}
}
What it looks like:
Answer by Zypherr7 · Sep 30, 2015 at 01:01 AM
I see what you are trying to do and I may be able to help. There is a formula you can use that, when the width and length of the tile is entered, can calculate a world coordinate, into a hex based coordinate, and back again if you wished. I have actually built a method to do so, but it is slightly confusing just to enter into here. I am going to suggest you use the initial coordinate to find the hexs around it. The offset you need would be on the y, .75*height, and for the x, it would be width\2 every other tile.
Ok, so I had a little more time to work on this, so, if you wanted to find the six tiles around the original one, you would be looking at tiles relative to it either (-1,0), (1,0), (0,1), (1,1), (0,-1), (1,-1); or you would be looking for (-1,0), (1,0), (0,1), (-1,1), (0,-1), (-1,-1). It would just depend on the offset of the initial tile. If it was on a row with tiles further left, it would be the first one. If it was on a row with the tiles further to the right, it would be the second group. Based off of that, you would then have to find the position of the origin tile to find the ones around it. The code would be something like this.
vector2 tile1;
vector2 tile2;
vector2 tile3;
vector2 tile4;
vector2 tile5;
vector2 tile6;
//tile width and length
double tileWidth;
double tileHeight;
public void findSurroundingTiles(vector2 origin){
//to find tile left
tile1.x = orgin.x + (-1 * coordinateTransferX(tileWidth, tileHeight).x);
tile1.y = orgin.y + (0 * coordinateTransferX(tileWidth, tileHeight).y);
//to find tile above
tile2.x = orgin.x + (0 * coordinateTransferY(tileWidth, tileHeight).x);
tile2.y = orgin.y + (1 * coordinateTransferY(tileWidth, tileHeight).y);
}
//finds coordinate of tile on same offset compared to origin
public vector2 coordinateTransferX(double width, double height){
vector2 temp.x = width;
temp.y = height;
return temp;
}
//finds coordinate of tile on offset compared to origin
public vector2 coordinateTransferY(double width, double height){
vector2 temp.x = width * .75;
temp.y = height / 2;
return temp;
}
Now, this code should work for what you are trying to do, if you can find objects based on their location. Also, you have to take into fact that an offset may be left or right. I cannot think of anything that could fix that off the top of my head. Also, on a side note, this may not compile properly if copied and pasted directly into scripts because I wrote this directly into the comment, and may have messed up some syntax.
If you need any help you can email me at zypherr7@gmail.com, I will be glad to help with any questions.
Thanks @Zypherr7. Your previous comment had me thinking that ins$$anonymous$$d of the multiple raycast method I should just find the hex my mouse was over and then find the adjacent hexes. Yesterday I ran through this $$anonymous$$inesweeper tutorial to see how they did it. I not sure I'm a big fan of how they did their Grid data class, but it did make me realize I need a data class of some kind. Barring any other examples though I'll try to semi-clone what they did in that tut and modify it for hex and just basic detection.
Combined with your code example I'm sure I can hack something together, though I doubt the data class will follow any kind best practice. I'll report back on my progress...
Answer by huxley443 · Oct 05, 2015 at 11:38 PM
Well I got this working, but I think it's pretty ugly. I'm not sure I'm passing data back & forth in a good way. And I'm not happy using OnMouseOver & OnMouseExit. I tried some OnCollision events but couldn't make them work.
Anyway, there's a lot going on, but it boils down to this. In a Grid class I have:
public List<GridTile> GetAdjacentTiles(Vector2 pPosition) {
int x = (int)pPosition.x;
int y = (int)pPosition.y;
int w = gridWidthInHexes - 1;
int h = gridHeightInHexes - 1;
List<GridTile> Adjacents = new List<GridTile>();
if (x > 0) {
Adjacents.Add(hexes[x-1,y].GetComponent<GridTile>());
}
if (x < w) {
Adjacents.Add(hexes[x+1,y].GetComponent<GridTile>());
}
if (y > 0) { //the two above
if (y % 2 == 0) { //even row
Adjacents.Add(hexes[x, y - 1].GetComponent<GridTile>());
if (x != 0) Adjacents.Add(hexes[x - 1,y - 1].GetComponent<GridTile>());
} else { //odd rows
Adjacents.Add(hexes[x,y - 1].GetComponent<GridTile>());
if (x < w) Adjacents.Add(hexes[x + 1,y - 1].GetComponent<GridTile>());
}
}
if (y < h) { //the two below
if (y % 2 == 0) { //even row
Adjacents.Add(hexes[x, y + 1].GetComponent<GridTile>());
if (x != 0) Adjacents.Add(hexes[x - 1,y + 1].GetComponent<GridTile>());
} else { //odd rows
Adjacents.Add(hexes[x,y + 1].GetComponent<GridTile>());
if (x < w) Adjacents.Add(hexes[x + 1,y + 1].GetComponent<GridTile>());
}
}
return Adjacents;
}
And In a Tile class I have:
void OnMouseOver() {
this.IsSelected = true;
List<GridTile> adjecentTiles = gridMap.GetAdjacentTiles(myGridPosition);
foreach (GridTile gt in adjecentTiles) {
gt.IsSelected = true;
gt.SetVisual();
}
}
void OnMouseExit() {
this.IsSelected = false;
List<GridTile> adjecentTiles = gridMap.GetAdjacentTiles(myGridPosition);
foreach (GridTile gt in adjecentTiles) {
gt.IsSelected = false;
gt.SetVisual();
}
}
Which results in this as I move my mouse around: