Search Unity

Casting Hex Grid over Terrain

Discussion in 'Scripting' started by kenaochreous, Aug 27, 2014.

  1. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    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?
     
  2. furiouslol

    furiouslol

    Joined:
    Aug 19, 2014
    Posts:
    19
  3. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    To get the terrain height at a specific position, use Terrain.SampleHeight( worldPosition : Vector3 );

    http://docs.unity3d.com/ScriptReference/Terrain.SampleHeight.html

    --------------------------------------------------------------------------------------------------------------------------

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

    Check the other solution below : http://forum.unity3d.com/threads/casting-hex-grid-over-terrain.265026/#post-1761876

    --------------------------------------------------------------------------------------------------------------------------


    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.
     
    Last edited: Jul 6, 2015
    Mikael-H and atoi2008 like this.
  4. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    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.
     
    Last edited: Sep 2, 2014
  5. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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).
     
    Last edited: Sep 4, 2014
  6. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    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?
     
    Last edited: Sep 3, 2014
  7. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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).
     
  8. Nicholas G

    Nicholas G

    Joined:
    Sep 3, 2014
    Posts:
    6
    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. http://forum.unity3d.com/threads/grid-drawing-problem.6480/

    Code (CSharp):
    1.    pathNode.cell = GameObject.CreatePrimitive(PrimitiveType.Plane); // Hex GameOjbect
    2. pathNode.cell.renderer.material.SetTexture(0,"YOUR TEXTURE HERE");
    3.  
    4.    //THIS LAYS OUT THE CELLS OVER THE TERRAIN
    5.   Mesh mesh = ((MeshFilter)pathNode.cell.GetComponent(typeof(MeshFilter))).mesh as Mesh;
    6.                 Vector3[] vertices = mesh.vertices;
    7.                 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));
    8.                 float xStep = pathNode.cell.transform.localScale.x;
    9.                 float zStep = pathNode.cell.transform.localScale.z;
    10.                 int squaresize = 10 + 1;
    11.                 for (int n = 0; n < squaresize; n++)
    12.                 {
    13.                     for (int i = 0; i < squaresize; i++)
    14.                     {
    15.                         if (Physics.Raycast(position, -Vector3.up, out hit, 1000.0F))
    16.                         {
    17.                             vertices[(n * squaresize) + i].y = Terrain.activeTerrain.SampleHeight(position);
    18.                             position.x -= xStep;
    19.                         }
    20.                     }
    21.                     position.x += (((float)squaresize) * xStep);
    22.                     position.z -= zStep;
    23.                 }
    24.  
    25.                 mesh.vertices = vertices;
    26.                 mesh.RecalculateBounds();
    27.                 pathNode.cell.transform.position += new Vector3(0, 0.3f, 0);
    28.  
    29.  
     

    Attached Files:

    Last edited: Sep 4, 2014
    milox777 likes this.
  9. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    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?

    Code (CSharp):
    1.     void Update()
    2.     {
    3.             if ( !hexWorld.Application.isEditor ){
    4.             cameraTx.camera.enabled = false;
    5.             }
    6.             if(hexWorld.Application.isEditor ){
    7.             cameraTx.camera.enabled = true;
    8.             }
    9.            
    10.         CheckForMouseClick();
    11.  
    12.         ScrollCamera();
    13.  
    14.         UpdateIndicator();
    15.     }
     
  10. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    Application.isEditor is a Unity command, not a member of HexWorld.

    http://docs.unity3d.com/ScriptReference/Application-isEditor.html

    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 :

    Code (csharp):
    1. void Start()
    2. {
    3.    if ( !Application.isEditor ) {
    4.      cameraTx.camera.enabled = false;
    5.    }
    6.    if ( Application.isEditor ) {
    7.      cameraTx.camera.enabled = true;
    8.    }
    9. }
    10.  
    11. void Update()
    12. {
    13.    if ( Application.isEditor ) {
    14.      ScrollCamera();
    15.    }
    16.    
    17.    CheckForMouseClick();
    18.  
    19.    UpdateIndicator();
    20. }
     
  11. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    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.
     
  12. GetUpKidAK

    GetUpKidAK

    Joined:
    Apr 9, 2013
    Posts:
    84
    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:

    Code (csharp):
    1. hexVertices[0] = Vector3.zero;
    2. hexVertices[1] = newVector3( halfHexRadius * -cos30, 0, halfHexRadius * sin30 );
    3. hexVertices[2] = newVector3( halfHexRadius * 0, 0, halfHexRadius * 1f );
    4. hexVertices[3] = newVector3( halfHexRadius * cos30, 0, halfHexRadius * sin30 );
    5. hexVertices[4] = newVector3( halfHexRadius * cos30, 0, halfHexRadius * -sin30 );
    6. hexVertices[5] = newVector3( halfHexRadius * 0, 0, halfHexRadius * -1f );
    7. hexVertices[6] = newVector3( halfHexRadius * -cos30, 0, halfHexRadius * -sin30 );
    and then when positioning the HexChunk prefabs why the hexRadius is used in full:

    Code (csharp):
    1.  
    2. chunkPos.x = (float)x * (float)chunkSize * Mathf.Cos( 30f * Mathf.Deg2Rad ) * tileRadius;
    3. chunkPos.y = 0;
    4. chunkPos.z = (float)z * (float)chunkSize * 1.5f * Mathf.Sin( 30f * Mathf.Deg2Rad ) * tileRadius;
    5.  
    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!
     
  13. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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.

    Code (csharp):
    1.  
    2. +-+-+
    3. |X|X|
    4. +-+-+
    5. |X|X|
    6. +-+-+
    7.  
    without adding half the hex size, they would have aligned like so :

    Code (csharp):
    1.  
    2. X-X-X
    3. | | |
    4. X-X-X
    5. | | |
    6. X-X-X
    7.  
    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 =]
     
    Last edited: Dec 30, 2014
  14. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    Ok cool, let me know if there's anything I can do to help with your project.
     
  15. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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 :)

    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 : http://forum.unity3d.com/threads/casting-hex-grid-over-terrain.265026/#post-1761876
    --------------------------------------------------------------------------------------------------------------------------
     
    Last edited: Jul 6, 2015
  16. Dan_lala

    Dan_lala

    Joined:
    May 14, 2015
    Posts:
    42
    Hi, the script seems not to be available anymore?
     
  17. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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....
     
    Last edited: May 16, 2015
  18. samAsQ

    samAsQ

    Joined:
    Jun 16, 2015
    Posts:
    7
    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.
     
  19. AlucardJay

    AlucardJay

    Joined:
    May 28, 2012
    Posts:
    328
    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.
     
    Last edited: Jul 6, 2015
  20. samAsQ

    samAsQ

    Joined:
    Jun 16, 2015
    Posts:
    7
    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.
     
    Last edited: Oct 26, 2015
  21. lancer

    lancer

    Joined:
    Aug 1, 2013
    Posts:
    231
    If anyone is looking for code for making a hexagon mesh from code (like I was when I found this), here's some working code (tested in Unity 5.3f) :)

    Code (CSharp):
    1. this.GetComponent<MeshFilter>().mesh = new Mesh();
    2.         Mesh mesh = this.GetComponent<MeshFilter>().mesh;
    3.    
    4.         Vector3[] hexVertices = new Vector3[8];
    5.  
    6.         float size = 1.0f;
    7.         float x0 = 0;
    8.         float y0 = 0;
    9.  
    10.         var SQRT32 = Mathf.Sqrt(3) / 2;
    11.         //-- from above with front faceing up
    12.         //center
    13.         hexVertices[0] = new Vector3(x0, 0, y0);
    14.         //Back Left
    15.         hexVertices[1] = new Vector3(x0 - size / 2, 0, y0 - size * SQRT32);
    16.         //Back Right
    17.         hexVertices[2] = new Vector3(x0 + size / 2, 0, y0 - size * SQRT32);
    18.         //Right
    19.         hexVertices[3] = new Vector3(x0 + size, 0, y0);
    20.         //Front right
    21.         hexVertices[4] = new Vector3(x0 + size / 2, 0, y0 + size * SQRT32);
    22.         //Front left
    23.         hexVertices[5] = new Vector3(x0 - size / 2, 0, y0 + size * SQRT32);
    24.         //Left
    25.         hexVertices[6] = new Vector3(x0 - size, 0, y0);
    26.  
    27.         mesh.vertices = hexVertices;
    28.  
    29.         int[] hexTriangles = new int[] {1, 6, 0, 6, 5, 0, 5, 4, 0, 4, 3, 0, 3, 2, 0, 2, 1, 0 };
    30.  
    31.         //Haven't done the UVing yet..
    32.         Vector2[] hexUV = new Vector2[] { new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0) };
    33.  
    34.         mesh.SetVertices(hexVertices.ToList());
    35.         mesh.SetUVs(0, hexUV.ToList());
    36.         mesh.SetTriangles(hexTriangles, 0);
    37.  
    38.         mesh.RecalculateBounds();
    EDIT:

    Even better, simpler, and I added the UV maps :) Only different is that one is rotated one way, and this one is rotated the "usual" way:

    Code (CSharp):
    1. ameObject o = new GameObject();
    2.         o.transform.position = new Vector3(x, 0, y);
    3.      
    4.  
    5.         o.AddComponent<MeshRenderer>();
    6.         MeshFilter filter = o.AddComponent<MeshFilter>();
    7.         filter.mesh = new Mesh();
    8.         Mesh mesh = filter.mesh;
    9.  
    10.         Vector3[] hexVertices = new Vector3[8];
    11.         Vector2[] hexUV = new Vector2[8];
    12.  
    13.         float size = 1.0f;//148.367f; //1.0f;
    14.  
    15.         //OK
    16.         //center
    17.         hexVertices[0] = new Vector3(0, 0, 0);
    18.         hexUV[0] = new Vector2(0.5f, 0.5f);
    19.  
    20.         //OK
    21.         //left back
    22.         hexVertices[1] = new Vector3(-size, 0, -(size/2));
    23.         hexUV[1] = new Vector2(0, 0.25f);
    24.  
    25.         //OK
    26.         //left front
    27.         hexVertices[2] = new Vector3(-size, 0, size/2);
    28.         hexUV[2] = new Vector2(0, 0.75f);
    29.  
    30.         //OK
    31.         //front
    32.         hexVertices[3] = new Vector3(0, 0, size);
    33.         hexUV[3] = new Vector2(0.5f, 1);
    34.  
    35.         //OK
    36.         //right front
    37.         hexVertices[4] = new Vector3(size, 0, size/2);
    38.         hexUV[4] = new Vector2(1, 0.75f);
    39.  
    40.         //OK
    41.         //right back
    42.         hexVertices[5] = new Vector3(size, 0, -(size/2));
    43.         hexUV[5] = new Vector2(1, 0.25f);
    44.  
    45.         //OK
    46.         //back
    47.         hexVertices[6] = new Vector3(0, 0, -size);
    48.         hexUV[6] = new Vector2(0.5f, 0);
    49.  
    50.         int[] hexTriangles = new int[] { 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0, 6, 1, 0 };
    51.  
    52.         mesh.SetVertices(hexVertices.ToList());
    53.         mesh.SetUVs(0, hexUV.ToList());
    54.         mesh.SetTriangles(hexTriangles, 0);
    55.  
    56.         mesh.RecalculateBounds();
     
    Last edited: May 6, 2016
  22. kenaochreous

    kenaochreous

    Joined:
    Sep 7, 2012
    Posts:
    395
    Lancer could you post the whole script and how to implement it? I'm confused by your script's syntax and pasting it into a blank C# script gives me a bunch of errors.