Building an Hexagonal Grid Map Editor

Hello everyone,

I’m currently trying to build a Map Editor for a Hexagonal Grid based game. I would like some guidance on the method I should use. I’ve already tried some things but I’m not experienced enough to tell if it’s feasible.

I would like to make a “realistic” terrain, like in civilization 5 or 6 for exemple. I already made a hexagonal mesh where i can define a resolution for each hex. I want to be able to click on hexs to generate mountains (or cliffs, etc). With maybe a procedural generation in the futur. (Maybe a hexagonal mesh is not the good approach?)

My first approach was to use Perlin noise and a mask to tell which hexs has mountains on it. But I’m not sure what type of mask I should use (for now I’ve made a non-optimised one with a gaussian function in the center of the selected hex but the result is not very good with the noise (maybe a mask in x^3?)). I’m also using chunks for performance and the transistion seems complicated.

The second approach was to use Height Maps of mountains and “paint” them on a blank texture which I apply on the chunks (with something like setPixels/GetPixels). With maybe the RGB for the texture and alpha for the height. But for performance issues at run time meh (the texture has to be quite large for the whole chunk).

I’ve done the catlikecoding tutorial on hexmaps (which I recommend). For those who have followed it I would like something alike but with more realistic graphics.

Thank you for those who read through !

Anyone working with hexagons should know about Amit Patel’s Web page on them. It’s pretty amazing.

3 Likes

Yes I’ve read it too, it’s amazing !

There is numerous ways you can approach the problem.

Myself doing this l, but on sphere

I think Unity has now built in hex tile map generator. Don’t know however, what it allows to do.
I use indeed perlin noise, to generate deprcios and mountains. In fact, I use multiple perlin noises, with different settings and combining them.

You can also use hight map, but instead of having high resolution map, you create map, where each pixel represents corresponding hex tile.

Regarding selecting tiles, you can either generate mesh collier for each tile. This method is not most optimal, but possibly the easiest one. Other option is to have only few coliders of just few hex tiles. Then offset their position, as you mouse pointer moves in the map.

Or having an equation, which calculate hex boundaries, to detect which tile is selected.

Any further information about the terrain, you can store in array of tile class.

1 Like

Thank you for the answer but I’m looking for a realistic terrain, something like that:

My biggest problem is the mask while using perlin noise, I don’t know how do make it better.

Btw nice video, I like the graphics of flat hexes like that too!

Thx:)

Can you show screenshot what you got so far?
Could show also, what you define as chunk?
It will be easier to understand your problem.

I assume, you want low level ground around the edges of hex tile and terrain deformation toward middle? For simplification reasons, I would use inner circle of hex, with gradient black to white toward middle. Then apply noise as multiplication of the circle mask and noise mask. This should give more desired results, close as on the link provided above. Btw. gradient don’t have to be linear.

Not sure if this is what you looking for?

The screenshot i sent was what I have right now.
A chunk is usually 4 * 6 hexes, i use chunks for the limitation of vertices per mesh.

I think low level around the edges is the more simple approach but for more realism blending bettwin hexes would seems more realistic. For now 0 at the edges is a start.
How do you apply the gradient to the noise ? For now I calculate the distance from the center of the hex.

What type of gradient whould you use ? Something close to 1 in the center and fades away at the edges.

Ah so the screenshot was yours :slight_smile:
But regarding smoothing between tiles, that was my second thought, what maybe your goal.

In my view, you will need to have shared common noise, across multiple tiles. Hence you may need to increase resolution of the noise. Instead of generating noise per tile, you generate noise per chunk, or per map. You will need have an absolute position of veritices for tiles, rather than local.

So for example, you take your tiles in chunk, calculate world position of each tile, and grab world position of each corresponding vertices as a groups. Now you can apply noise to a chunk of tiles, by applying it to vertices. Knowing the group of vertices per tile and its offset, you should be able to generate nice unified terrain per chunk.

Regarding gradient, for example in case of tile, you grab desired local vertex postion, (as you said) calculate distance from center of tile and knowing the radius of inner circle, you should be knowing value of gradient noise. Then just multiply. You can use not sqr distance, to gain on performance.

Alternatively, regarding gradient, have a texture and grab a pixel color, corresponding to tile, or terrain vertex position.

Yes

1 Like

I used latitude/longitude system to define chunks. Basically adding a tile to an abstract geometry collection would find a nearest chunk based on local lat/long angles, which were then rounded to some resolution. It was really pretty, and I got equal area chunks that fully map a sphere, poles included.

@Antypodish is it a subdivided icosahedron, or do you warp the hexes to fit them nicely?
I had a perfect sphere, so subdivided icosahedron with unavoidable 12 pentagons. I also made a pretty decent dynamic tessellation of tiles so I could probably make a full size planet if I wanted to. Tessellation turned out immensely fast (for a CPU solution), and I would run it recursively so that the area around the player would be the densest and would then radially loosen up toward horizon, so no T junctions or weird geometry. Yours is nice looking too, though a bit slow for my taste.

Ah yes I did the sqrt when i was calculating the distance but you’re right! Do you think something like a polynomial gradient whould do? Hard edges are not good so linear is not an option.

Sure, you can use any curve you want to.

Sorry for a bit offtopic, but is interesting subject and can not resist. :slight_smile:

Yeah my base is also icoshedron with 12 pentagons.

I suppose our goals are different, which also result with different approach of generating hex planets.

My aim for example, is to keep planet of size 2k to no more than 40k tiles. I use 3D pent / hex tiles, to define height. This adds verts to calculation. Including recalculating UV. But I know if I want to really hard, I could optimise this a bit further (not my goal / need atm), by collapsing some vertices. Additionally I use vertex color for shader, when changing noise, as result height of tiles. Then on top of that, I calculate and assign neighbors tiles, for later path finding. All unfortunately ads up :slight_smile: But then, once is planet generated, it will not change during gameplay.

Do you use smooth height transition between tiles? Would I be correct assuming so?
What is you approach to apply texture / colors to tiles?
I for example resigned from using planet texture. Instead I use per tile shader / optional texture.

Tessellation is interesting too. I was thinking about it at some point.
But I don’t want / need to use of it in my case. I keep planet of “small” size. I rather save CPU cycles for other game mechanics :slight_smile:

This is solid way for chunkify map / sphere.
I used mesh division, per 65k vertices limit, rather than chunks. Hence for 163k tiles there is over 70 mseshes.
My “chunks” are per tile based. Each tile is to store information, what instances are “living” on it (using DOTS).

Did you have a look at Honey Hex framework? It’s even free. It’s a bit older but maybe it’s easier to make it work in Unity than rolling your own solution? Theres also an editor for it but it costs a few bucks. I have not worked with Honey but it may be what you are looking for.

1 Like

@exiguous that is interesting and nice one.

@exiguous Yes i’ve seen the Honey Hex frameWork, it’s amayzing but I could not understand it. That’s too complicated for me right now! :x I want to have something I made myself and I have to at least understand it if I follow someone else work.

But effectivly it’s what i’m looking for.

1 Like

And it laid in plain sight ;).

The topic of procedural mesh/terrain generation is inherently complicated. But I understand thats its better to build your own stuff for learning.
I think it also had a WIP thread. Maybe you can post some questions there to understand it better?

I’ve read that they don’t respond anymore but yes I can try! :slight_smile:

OP sry for the off-topic

Yes, my approach was to hide the tiles from being apparent. My tiles were more like sophisticated geometrical seeds for things to be procedurally added in local space. And for this I needed tessellation to blend between two spaces (or scales) seamlessly.

So I had these two spaces, practically, one that was global, where every tile is known and populated with rough macro data, and the local space, which generated on the fly according to camera quat (technically a position on this sphere). One of the most important things was auto-culling, able to cull a sphere against camera frustum, so the camera was free to observe the sphere from any angle, while the tessellation would kick in based on distance. So the closer you get, the less of the global world you see, but the triangles get more dense, refining the topology, hence it would reach a frame polycount equilibrium, which was really nice to see in action. I also had two different renderers, rendering at different scales and married through overlaid renders, so that you could really fly through space and reach to the surface. It wasn’t a true planetary scale, though, it was around 6 times less than the Earth in equator, but still huge and very impressive.

Also, unlike the global one, the local camera was fixed, meaning the world would turn beneath you, so having the two views combined to form a seamless image was a mini-feat in itself. I made this so that I wouldn’t worry about the floating point imprecisions because of the scales involved. The global world was really small, I think 6000 times less, if I remember correctly, so that it wouldn’t become a problem if I wanted to implement an entire solar system at some later point.

The macro world had a dedicated map generator, that would consider tectonic forces and create tectonic plates with varying levels of composition density, orogenic and volcanic processes – of course, simplified to an extent – but all of this creates incredibly believable and consistent Earth-like geography. I wanted to implement mid-ocean ridges but never got to do it, but I got lost in this for weeks. Such a nice job, minus the fact that I had to make an inspector to experiment with it more. Perlin would kick in subsequent refining passes, and tessellation was based on blitz-fast face subdivision where you end up having only two possible geometrical patterns in order to maintain splitting without T junctions, and then recursively refining down to a certain limit. The end face would be around a meter or so compared to the player.

All of this was a massive learning experience for me, and I was astonished how fast it turned up in the end. I also had some really interesting solutions; for example, one of which I’ve named a toponym, practically a way to bundle and unique identify any point on the surface, not in terms of standard coordinates, spherical (2x 32 bits) or Cartesian (3x 32 bits) or god forbid quats (4x 32 bits), but in terms of tile triangular subdivision, which ended up being perfectly unambiguous, coherent with my setup, and only 24 bits in size. Which is pretty interesting when you think about it, to keep precision coordinates of a huge sphere in just one uint. Obviously the concept has a limited resolution and works in tiny triangular steps, but I haven’t even used the full 32 bits, so I could’ve refined it to sub-face barycentric space if I wanted, just by adding few more steps. It’s incredible how compressed and fractal-like this thing is at describing the spherical plane. This alone opened up new mathematical venues for me.

Anyway, the whole thing turned out glorious for a proof of concept, but I have shelved the project in pursuit of some other ideas. I had to do it because it started to resemble something that only a decent-sized studio would be able to pull off satisfactorily as a full game – and I have only scratched the surface of some tech, no game in sight – and so I’ve found myself in a situation where I had to come up with stepping stones in between that place and where I was initially. I made this on a potato and this made me realize I could implement and get away with much smaller and simpler technologies, if only I’d put more emphasize on key takeaways from this. As it turned out, I’ve figured out ways to stay noticeable without having to commit to a spherical geometry (let’s be honest here, it’s hard as hell, but at least I learned quats inside and out), but that let me focus more on the gameplay side of things.

Oh I’ve designed a dedicated mesh container with topological relationships, so that I could immediately tell the tile neighbors, tile triangles, that kind of thing, and I would optimally export Unity meshes from that. Had two of these, one global and one local, and the local would import ad hoc geometry from the other one, and split this geometry as needed. Regarding textures, the idea was to create a custom splat shader for the local terrain (never got to do it, but I had everything in place), and to dynamically unwrap the macro world for the stratospheric view, with the intent to make the local splat shader fade in based on macro render’s gbuffer depth. When I made the unwrapper (it’s on the forum) I’ve also discovered a bias in FromToRotation which I had to fix, there is a thread somewhere around here. And so on, pretty massive undertaking, I’m a bit crazy, and still learning to discipline myself when it comes to feature megalomania. Contrary to popular belief, being able to make almost anything on your own, also comes with its share of problems.

And maybe I’m an okay programmer, but I still need to think like an indie if I’m to achieve anything of commercial value. So it’s a learning process on so many levels. Anyways, I’m glad you’re pursuing this path as well, there is a lot to discover in this space, and it has a lot of potential. Obviously you’ve tuned your tech constraints and sanity checks in such a way to meet more realistic requirements.

Catlike coding has an excellent hex grid tutorial as well. You could check it out.
https://catlikecoding.com/unity/tutorials/hex-map/

2 Likes

Srr OP, for keeping offtopic :slight_smile:
Hope don’t mind?

Yeah, this is a big challenge when comes to indie. I was fighting with this as well, when I was in prime learning stage of Unity, before get on to recent projects. I mean I still like challenge myself and carry on with prototypes. But indeed, I constrained myself, to be more realistic. Also focusing on specific features at the time. Otherwise is easy to go astray :slight_smile:

The geographical tectonics effect sounds cool.
But unless I would be doing geo simulation, I wouldn’t go that far.
I mean, if you went so far, maybe is worth to think, how you can utilize it in small arcade like scale.
You see, today’s trending is, to make all procedural and realistic scales. Ending up with No Mans Sky empty deserted planets. Somehow beautiful for the first few times you visit them. Until you learn theirs pasterns. And you know all.
So my approach was actually opposite. Stay casual in aracade scale. Is easier to handle empty space and make things interesting, rather than walking simulator :stuck_out_tongue:

If definitely sounds that you undertook massive learning journey, in procedural generation. Use it, specially that well made planet generation is no that simple. Narrow your goal and will be more realistic. So you wont get scared by “only large studio” perspective.

Btw. you got some link to your thread? Or screenshots?