Casting Hex Grid over Terrain

I’m interested in trying to make a hex grid mold over the shape of a terrain gameobject. But I’m not entirely sure how I would make the hexagons align with the terrain’s shape. Where would I find values to make the hexagons align with the terrain’s shape?

Maybe this might be of some help

To get the terrain height at a specific position, use Terrain.SampleHeight( worldPosition : Vector3 );


EDIT : CODE HAS BEEN REMOVED. IT WAS FLAWED AND DIDN’T WORK

Check the other solution below : Casting Hex Grid over Terrain - Unity Engine - Unity Discussions


As this question has come up a few times, I have spend some time working on a solution. Be aware this is a WIP and could use a lot of improvement.

How does it work?

Similar to my above answer, several meshes are created at runtime. The number of meshes depends on the size of the hexagon grid to be created, as a mesh can only have a maximum vertex count of 65000. The Y position of each vertex is calculated by using Terrain.SampleHeight at the vertex position. Then an offset is added to help counter Z fighting. A much better solution to this would be to use an overlay shader (I am currently still trying to find/make one).

The demo also includes a couple of functions for changing the uv values of a hexagon that the mouse is raycasting to. This should help to show how the uvs can be modified for a specific hexagon given a world position.

I think the demo explains it better than I can (even though I wrote it! explaining is not my forte). Make sure you import into a new empty project (I won’t be responsible for lost/damaged projects).

Link (as UnityForums has a max file size of 4MB) : Download Demo [link removed]

move camera with WASD/arrows, zoom camera with Mouse ScrollWheel.

I have attached the scripts also. To setup :

Create a new scene
Create an empty gameObject, name it HexChunk
Attach the HexChunk script to this object (MeshFilter and MeshRenderer components should automatically be added)
Drag HexChunk object from the scene to the Hierarchy to create a prefab
Delete HexChunk from the scene
Create an empty gameObject, name it HexWorld
Attach the HexWorld script to the HexWorld object
Create a terrain
Drag and drop HexChunk into the HexWorld Inspector
Drag and drop terrain into the HexWorld Inspector
Attach the TerrainRaycaster script to the HexWorld object
Hit Play =]

I should be able to explain more as questions focus my thoughts.

2 Likes

Is there a way you can select and highlight an individual Hex tile via Raycasting or by making the individual Hex tiles gameobjects? I would like to be able to select and highlight an individual Hex tile.

I have modified HexWorld and TerrainRaycaster to demonstrate this functionality. For every hexagon, a value to define the uv coordinates for the texture is now stored in an array. when the mouse leaves a hexagon, it checks if there is a value in this array that is not for default or highlighted, and if so reassigns the correct uv ‘index’. So if you left-click on a hexagon, it will remain green, right-click on a hexagon to make it red. Middle-click to return the hexagon to default.

Edit : Removed scripts. All the latest versions of this demo are in my first post (3rd message in this thread).

I noticed a rather large delay with the Hexagon tile detecting the mouse cursor. I tried clicking and moving the mouse around but the hexagon tile still wouldn’t detect it immediately. Any idea what would be causing that delay?

Yes, that edit was kinda rushed as I was tired. There is a timer in the RaycastTerrain function so the mesh wasn’t being modified every frame for the indicator. I just threw the mouse click inputs in there when they should be checked every frame. That is was the ‘lag’ noticed. Now I have modified the raycaster script to check for mouse click inputs every frame. It also now returns the data array coordinates of the selected hexagon.Since there has been a few changes, I recommend re-downloading all the latest scripts / project.

A new project and the updated scripts are in the second link. I have removed all other scripts/links to avoid confusion. All the latest versions of this demo are in my first post (3rd message in this thread).

Hi I just did this a few days ago. I’m not sure how you are making your grid but each of my hex objects are a plane with a hex texture on it. I looked at this for help. Check out the third to last comment. Grid drawing problem - Unity Engine - Unity Discussions

   pathNode.cell = GameObject.CreatePrimitive(PrimitiveType.Plane); // Hex GameOjbect
pathNode.cell.renderer.material.SetTexture(0,"YOUR TEXTURE HERE");

   //THIS LAYS OUT THE CELLS OVER THE TERRAIN
  Mesh mesh = ((MeshFilter)pathNode.cell.GetComponent(typeof(MeshFilter))).mesh as Mesh;
                Vector3[] vertices = mesh.vertices;
                Vector3 position = new Vector3(pathNode.cell.transform.position.x + (pathNode.cell.transform.localScale.x * 10 / 2), 1000, pathNode.cell.transform.position.z + (pathNode.cell.transform.localScale.z * 10 / 2));
                float xStep = pathNode.cell.transform.localScale.x;
                float zStep = pathNode.cell.transform.localScale.z;
                int squaresize = 10 + 1;
                for (int n = 0; n < squaresize; n++)
                {
                    for (int i = 0; i < squaresize; i++)
                    {
                        if (Physics.Raycast(position, -Vector3.up, out hit, 1000.0F))
                        {
                            vertices[(n * squaresize) + i].y = Terrain.activeTerrain.SampleHeight(position);
                            position.x -= xStep;
                        }
                    }
                    position.x += (((float)squaresize) * xStep);
                    position.z -= zStep;
                }

                mesh.vertices = vertices;
                mesh.RecalculateBounds();
                pathNode.cell.transform.position += new Vector3(0, 0.3f, 0);

1 Like

Ok, I couldn’t see the mouse cursor affecting the HexWorld chunks but that was due to the HexWorld still Generating chunks and after it had finished I noticed my mouse cursor was being detected by the HexWorld. So I attempted to make my own adjustments in TerrainRaycaster but I’m getting an error saying hexworld doesn’t contain definition for ‘Application’ what’s wrong with my code?

    void Update()
    {
            if ( !hexWorld.Application.isEditor ){
            cameraTx.camera.enabled = false;
            }
            if(hexWorld.Application.isEditor ){
            cameraTx.camera.enabled = true;
            }
           
        CheckForMouseClick();

        ScrollCamera();

        UpdateIndicator();
    }

Application.isEditor is a Unity command, not a member of HexWorld.

This returns true if the project is being run in the editor.

As the size and amount of meshes being created is quite large, it takes a while to be generated. Watch the Scene window and you can see them being created. Ideally this would be done before the player can start moving around so it wouldn’t be noticed. That’s also why they are generated with a yield between each mesh (not using yield will cause the project to freeze while generating). You can check the boolean isInitialized in HexWorld script to check if all the meshes have been created (a debug appears in the console when this is true).

If you don’t want to use the camera scrolling functionality, just delete cameraTx and ScrollCamera() from the script (perhaps I should have made ScrollCamera a complete separate script).

But to fix what you have :

void Start()
{
   if ( !Application.isEditor ) {
     cameraTx.camera.enabled = false;
   }
   if ( Application.isEditor ) {
     cameraTx.camera.enabled = true;
   }
}

void Update()
{
   if ( Application.isEditor ) {
     ScrollCamera();
   }
    
   CheckForMouseClick();

   UpdateIndicator();
}

I was thinking about a different kind of hexagon generation that might be less resource consuming. What if instead of generating all the Hexagon chunks at once you only generate the Hexagon chunks that are within the view of the camera. The hexagon chunks would load/unload depending on where the camera is viewing and how much the camera is able to view.

Hi,

Sorry to revive a slightly old thread, but I’ve just been picking through this code and I’m a bit confused.

Specifically, why halfHexRadius is used when calculating the hex vertex coordinates in the HexChunk class:

hexVertices[0] = Vector3.zero;
hexVertices[1] = newVector3( halfHexRadius * -cos30, 0, halfHexRadius * sin30 );
hexVertices[2] = newVector3( halfHexRadius * 0, 0, halfHexRadius * 1f );
hexVertices[3] = newVector3( halfHexRadius * cos30, 0, halfHexRadius * sin30 );
hexVertices[4] = newVector3( halfHexRadius * cos30, 0, halfHexRadius * -sin30 );
hexVertices[5] = newVector3( halfHexRadius * 0, 0, halfHexRadius * -1f );
hexVertices[6] = newVector3( halfHexRadius * -cos30, 0, halfHexRadius * -sin30 );

and then when positioning the HexChunk prefabs why the hexRadius is used in full:

chunkPos.x = (float)x * (float)chunkSize * Mathf.Cos( 30f * Mathf.Deg2Rad ) * tileRadius;
chunkPos.y = 0;
chunkPos.z = (float)z * (float)chunkSize * 1.5f * Mathf.Sin( 30f * Mathf.Deg2Rad ) * tileRadius;

I don’t have any issues with the calculations (after a lot of playing!) but this change has confused me. As well as the fact that the z position is multiplied by 1.5.

Could anyone shed and light on this? Thanks!

Hi there, sorry for the late reply. It’s been a while since I wrote that, but from memory … :

why halfHexRadius is used when calculating the hex vertex coordinates

if you imagine the 2D array as a grid of squares, I wanted the hexagon to be centered in the center of each square.

+-+-+
|X|X|
+-+-+
|X|X|
+-+-+

without adding half the hex size, they would have aligned like so :

X-X-X
| | |
X-X-X
| | |
X-X-X

and then when positioning the HexChunk prefabs why the hexRadius is used in full

this is something different to generating an individual hextile. This is for aligning the whole chunk (relative to its chunk position in the world).

As well as the fact that the z position is multiplied by 1.5

I shall revisit this project and examine the code for a more descriptive and definitive answer. The 1.5 was an abbreviated calculation. It’s all to do with aligning hex tiles so they tesselate seamlessly.

Edit : I’ve also been working on a system that only generates chunks that are visible to the camera! This whole project will be reviewed and updated =]

Ok cool, let me know if there’s anything I can do to help with your project.

OK, very sorry for the late reply.

The mesh chunks are working fine, however I’m having a little trouble returning the data array index of the hexagon when given a world-space coordinate. I also havn’t factored in if the terrain isn’t positioned at the origin. I also havn’t put changing the texture UVs at the data array index (as it isn’t returning the correct value yet). Sorry, but I realy don’t have time to work on this atm.

At least you can see there are only 9 meshes, that align themselves around the target position. When they move, their data is updated (vertices, UVs). Much better.

Hopefully the forum will help complete this and get it working for the masses :slight_smile:

There is a new class, IntVector2 which is quite useful for storing 2D array indexes and returning them in a single variable.

I can only upload 4MB as an attachment, so you’ll have to add your own terrain after importing. Oh, and please import into a new blank project. The class names should be unique, but you never know, and I don’t want to break anyone’s project. (this is good advice, never import anything from the asset store etc into an existing project in case files get overwritten).


EDIT : CODE HAS BEEN REMOVED. IT WAS FLAWED AND DIDN’T WORK

Check the other solution : Casting Hex Grid over Terrain - Unity Engine - Unity Discussions

Hi, the script seems not to be available anymore?

Sorry Dan, but due to lack of help and interest (despite the number of views), I removed the code. I also see there is an asset in the asset store for sale that does the things I have written and given away for square and hex grids. And despite my contributions to the community, I have no credibility amongst my peers. With all that and negative feedback, there is no incentive to continue helping and providing the scripts I write for free anymore…

Poor you, I shed a tear for your lack of recognition and credibility. There are so many developers on the web I owe my skills to, who wrote tutorials and showed beginners how to do things just for want to share the knowledge. The fact that you have become so bitter about his that you have actually removed your code so other people looking to get started doing the same things you are cant even view your function to get a foothold on where to start is a testament to your selfish and greedy nature. I hope you come across a coding problem that you find impossible to solve and are met with equally salty response from people with more knowledge than yourself, so you may see how the programming world we live in is helped along and advanced by the generosity of the community. Good Day Sir.

Wow, thank you for taking the time to create an account just to write a hateful message to me.

Clearly you don’t know what you’re talking about. Did you acknowledge the 100s of answers on UA, the countless comments there and here, and the 100s of hours of video tutorials.
All this has taught me : for every 1000 views, at least 10 comments from copy-paste gurus saying “this doesn’t work”, and maybe, just maybe 1 comment of thanks. What’s the point when there is no support for the effort one puts in? Did you ever thank someone whose work you derived from?

All I can assume is you are the one who is salty for not being able to copy-paste someone else’s code.
Let me know when you’ve actually made a contribution rather than leeching, cursing and spitting on someone from the safety of your faceless keyboard.

Do you see ANY answers to this question? So why is the ownership only on me. What if my code was flawed and not very good? Which in fact it was : I was unable to resolve the issue of returning the correct array reference for the grid location of the mouse pointer, pretty much making it useless. Then I would also get a bunch of hate-filled comments saying “this doesn’t work” (again usually from people that just like to copy-paste and hit play).

As stated above there is an asset on the store that someone is making money from and has this functionality. Not me. Go support them, or give them your opinion on what you consider selfish.

Oh, you mean like the last 5 or so questions I have asked here? Been there, done that, read the book, ate the stew. Your wish for my misfortune has already been fulfilled in advance.

Thanks again for proving my lack of faith in humanity is warranted.

Yes, actually here is an example of a thread where the author has left his broken old code up for 4 years and post after post of people analyzing it and making their own assumptions collectively pushes the thread forwards. I also give thanks to the author on that thread in a post for being so helpful to the community, as I always do if somebodies work has pushed mine along.

http://forum.unity3d.com/threads/excavator-simulation.66871/page-3#post-2354970

If you see his responses over the 4 years you will see that anger and hatred is not the only way to respond to someone having the same difficulties as you, it is possible to work together to move forward. As it is your thread is now useless to anyone who comes across it. At least if your broken code was here we could start to pick through it and offer suggestions to each other as to why it doesn’t work, possible solutions and if not just seeing the way you were using and manipulating your code objects. Your decision to quote every sentence of mine and provide sarcastic comments to them shows your true colours: you would much rather try to take the upper hand then to address the current issue.

I wish you no ills, but as for the lack of faith in humanity I feel your violent reaction here has proven that more than anything.