Monday, April 5, 2021

Creating a (mini)map

How can one play a game with such a big map without the help of a minimap? - time to start working on that.

Today I will be creating the base for the minimap, I'll first be mapping out the terrain of the world - buildings and all that will come a little later on.



To color the terrain I just need to sample the game world by the relative coord on the minimap(I made the minimap resolution work per chunk so you'll get the same "pixel per hex" on different map sizes), sounds pretty simple in theory - and luckily this time it is.

I can easily convert the texture [x,y] pixel coord to hex grid (same math used for the mouse position) -> get a hex -> get the texture index for the terrain -> get the terrain texture from the texture array I use in the terrain shader -> average that and stick it in the minimap [x,y].

Viola - minimap!


Oh right... the water... doh!

Ok, so let's say that if a hex is underwater we'll lerp the color to blue-ish by how deep the water is from 1-16 elevation steps


well.. had a feeling the low end won't be enough, maybe we'll force it to be atleast half blue?


Ah - that's better!
Still could use some tweaking though, and I don't think a simple square/inverse square will do.
Time to whip up my favorite solution - AnimationCurve!


It's a subtle change, but I think it's much smoother.

I forgot to mention, those minimaps are on a 35x35 map with a resolution of 8x8 per chunk, here is the same map with different resolutions going from top left to bottom right (1x1, 2x2, 4x4, 8x8, 16x16, 32x32)


I know you can't really see a difference between 8x8 and higher, but I'll be adding a zoom function so that'll be useful then.

You know what? why wait, let's do that now, I think all I need is a ScrollView and a slider.

Here's the difference between 8x8, 16x16, 32x32 and 64x64 - left to right, bottom zoomed out, top zoomed in. (I moved them aside a bit so you can see the area they're zoomed onto)


One thing I think we can add is the clouds, should be simple enough, just like the water - only this time lerping to white instead of blue.


It's a start, I think we can call our good friend the AnimationCurve once again.



Maybe I'll play with the curve a little more, but it's much less aggressive.
 
I think that's good enough for now, when the rest of the game comes a bit further along I'll add in faction-colored building,
I'm thinking about how to display terrain height on the minimap,
Also wondering if and how to add the plants too.

I still need to save a minimap for each map along with the map data to display on the loading menu fast, but I think I'll do that tomorrow - I've spent most of the day working on the UI code and creating a main menu (nothing interesting to hear about) and it's getting late!

And a camera indicator with a click-to-move functionality should also be good, can't forget about that one.



Thanks for reading, and check back soon for more, the next video log should be ready soon along with the next test build!

Thursday, April 1, 2021

Finishing the wall & building systems

Last post was mainly about putting variable height on the walls and updating the wall UI a bit, now it's time to finish the rest of the building system and the wall-building connections.

Right now the building system only care about terrain elevation and only allows for building on flat ground
I need to make it care about waterbodies, rivers and up/down hill separately
I also need to tell the game the height of each building/wall and make the wind interact with it
The wall-building connections also need a touch up, the building-side of thing expects a wall of a certain height and the wall are trying to find a height on the buildings (the same height that is used for the wind)



First to give the buildings height, I simply added a byte to each one of the buildings segments/"BuildCoord" (buildings can take multiple tiles and each tile has its own coord and variables) just like I added to the walls, each step is half of the terrain elevation step.

Now the walls can read some height off the building, but having them connect to the very top of the building will hardly do.
What I wanted to do is give the walls a range of heights they can connect to the buildings with, but that proved a bit more tricky(both in code and later on textures on the buildings not lining up) than I initially thought, so I decided to have a fixed height walls can connect to per building segment.

So add another byte, make sure it's less than the building height and let the walls read that for the connection height - done!
I still kinda want to be able to connect at different levels, I might be coming back to this later on.

Integrating this into the wind simulation will be very easy, I already made the grounds for this when created the wind system, just need to add the height from the wall/building (if there is one on the hex) to the variable the wind reads from.
The vision range calculation also uses this variable so buildings will now also block sight. (fog of war)



Now let's start with the terrain elevation, what I'm doing is calculating a height offset for each segment from the buildings pivot (the segment you're "holding" and rotate around while placing the building) and checking if it's in the height range for the segments.

It works pretty well as longs as the terrain isn't too crazy, I think I might need to add a limit for max offset from each of the neighbors instead of the pivot.



To add water into the mix I added two variables (again, per building segments), "minWaterDepth" and "maxWaterDepth", when trying to build it simply checks if the tile's water depth is within this range.



Rivers are a little problematic, they're directional, and taking that into account in the buildCoords is a real hassle (not impossible, just a real headache) I think I'm just going to check if a river exists in a tile, I can always update this part later on if it becomes a real issue.

What this means though is that placing building like the waterwheel can be a bit awkward, take the waterwheel picture for example(pictures below) - what I did here is require 3 tile of river in a row and blocked them on either side, forcing the river to be strait where the wheel is, if the side building was not there the wheel could be place on a zig-zag river and the wheel placement won't make any sense.




All in all most of these changes were pretty simple (as long as you don't overwork yourself and start creating problems instead of solutions - spread your work out people!), the main headache I had was with the wall-building triangulation code - I'll explain more on that when I do the video log and have some visuals so you understand what I'm talking about.




The next step I plan on making is working a bit more on the models (the current ones are mainly for figuring out the building layouts and help me build the code) and finally get to doing the "first"(the "zero-th" entry was mostly for a couple of my testers) entry into the video log and show you what it's all about!


But just so I don't leave you high and dry until then, here are some pictures from a quick little test city I created:



small housing in a row



small housing in alternating rows



medium housing in a whatchamacallit.. cul de sac



medium housing in a row



waterwheel





3 single wall segment, variable height and battlements (middle one selected)



double and triple connections



bottom segment has stairs, rising to a more elevated platform (notice the battlements only protecting who ever is higher up)



small tower



medium tower (this is a single external model, the 3 sides are not from the wall system)



small tower with 3 walls attached to it



small tower surrounded by walls



medium tower with walls attached



city walls, double gatehouse with murderholes and a tower on both sides



other side of city walls, medium tower and walls



middle of city walls, corner with small tower (left is the medium tower, right is the gatehouse)



birds eye view of everything



and also a fishing dock





All the models need a lot more work obviously, but it's starting to take shape! 😁
I also need to make the highlights a little transparent

But that's it for now - thanks for reading and check back soon for more!

Monday, March 22, 2021

Working on the wall system

The bulk of this system is already done, I just need to add variable wall height and optimize the UI interface.
There are a couple more stuff I want to add, like connecting the wall to nearby terrain (like it does other walls and some buildings) to act as a bracing wall and building a roof to provide extra protection (Abwurfach??? imma just gonna go with "roof" on this one) but I don't know if I'll get to it now.

Right now walls are just a fixed height above the terrain they're on, first thing to do is add a variable to the wall class for the actual height, I'll be using a byte for this.
Change the constant wall height to a minimum height (the height walls start from at 0 height), add a constant for how much each physical height each level adds(I'm thinking half the terrain step elevation) and a constant for the maximum height steps a wall can have (I don't think I'll be using the full 128 values a byte has, a couple dozen top)



Hmmm.. on second thought, maybe I should start on the UI, it'll make testing everything much easier and I always end up getting to it last and cutting corners because I don't like doing UI.

Currently the interface is very wonky, it takes up too much space and requires more clicking than actually needed.
you can see the edge selector on the right, the group of buttons on the bottom are the controls for the entire wall segments and the group on top are for the selected edge.




To remedy all of these problems I'm going to compact everything (and the new height control) into the area the edge selector currently takes and make everything a one-click operation.
What I did is place a small hex in a big hex and connect the corners - creating a central hex for the global controls with six trapezoids extensions, one for each edge.
Now I can place all the buttons on the appropriate areas, all accessible all the time.

Here's what it looked like during the design, you can see the seven segments (center + six edges), the letters are where all the buttons are gonna go and the red circle mark the safe area for placing the center buttons (the graphic is spinning with the camera to align the edges to the view and the center buttons don't rotate)




This is the button legend:
  • CR - crenellations (either edge or all)
  • MC - machicolations (edge or all)
  • G - gate
  • L - lock gate (edge or all)
  • ST - stairs
  • MH - murderhole
  • (-) - decrease height
  • 99 - current height
  • (+) - increase height
  • R - roof

After settling on the layout I made it a bit prettier (but it still needs another pass! 😅), got it into Unity and added all the other elements to it(they don't have fancy graphics yet), this is how it looks now:




I like it, still needs more polish and the buttons are still jarringly ugly - but that'll come in due time.
I want to make the buttons flip when upside down, but I think i'm gonna hold off on that, see if I can make some icons that are easily legible from all angles first.



Now back to my safe space the code, first to make a few adjustments to accommodate how the new UI works, this is also a great opportunity to refactor all this code over to the InputMaster class. (it didn't exists when the old UI code was created)
Redoing all the UI code and wiring it up was pretty repetitive work, it took an hour or two, to finish,
Next step is to make the walls actually have variable height, this turned out to be way simpler than I thought - lucky I over engineered it when I first created it!
I changed around some of the constants to work with the new per-wall math, instead of using a constant "wallHeight" for everything I used the new height(that is on the wall) multiplied a constant "wallStepHeight".
Then it was just a simple matter of copy-pasting(only made four errors!) the new math where the old constant value was in the triangulation code, and it actually worked!
I made some other changes like disallowing gateways on wall lower then a height of six units, the gates are still a constant height, I'm thinking of maybe changing it a bit later on - maybe big entities can't pass though the small gate? we'll see.

Oh, and texturing them might be a good idea also - totally planned on it and forgot.
We can just pretend it's a very good whitewash for now. 😳

Anyway, here's a screenshot of the finished product, you can see an arrow pointing to the selected wall segment, the outer part of the inspector is rotating to match the actual orientation but the center doesn't spin.




I'll be working on a showcase video soon, so come back for more!
Thanks for reading.

Friday, March 19, 2021

Creating the climate simulation

Developing this system was quite the journey, I've been working on it for a few months now and I'm still ironing it out, but it's finished for the most part.

Right now I'm simulation surface water, ground water, clouds, salt/mineral, wind, air temperature and ground temperature.
But I didn't start with all that, at first I was afraid I couldn't make the climate simulation fast enough to be simulated during the game - so I designed it so the climate will be simulated in the map editor and remain static during the actual gameplay.

I started with generating a humidity map and a salinity map for the map, I based it on procedural noise and started adding stuff to it, the main thing I did was go over each hex and if it's part of a waterbody or a river I made it increase the humidity value of the nearby area, to get some intelligent results I ran a D* algorithm on the map (using the A* grid I use for navigation) from each hex to get the hexes it effects and the travel distance to each hex, I made going up in elevation very expensive and going down to be very cheap - that gave me quite the decent flood plane.

Building on that I did the same for the salt map, only using different values for how far it reaches and how strong it is.
I gave the waterbodies a bool for fresh or salty and if the hex is part of a waterbody marked as salty or part of a river coming out of a salty waterbody it's gonna run the algorithm and add salt to whatever it reaches.



That was the basis for the climate, I knew it wasn't good yet, but couldn't think of any way to improve it.. one thing that got my attention is Catlike again, in his tutorial he's simulating a water cycle to paint the terrain pretty much like I wanted to do (and other stuff not relevant to me, but go check him out if your interested in that), I started doing the same but made some changes to the water movement logic.

Right now I had humidity and salinity, I added clouds too to bring it into line with Catlike.
I started playing around with it and it became clearer and clearer that one major thing missing in my sim is wind, so I started looking into ways to do that.

In Catlike's tutorial what he's doing is deciding on a global wind angle that is perfectly aligned to one of the six hexagonal directions (0, 60, 120, 180, 240, 300) and have a constant global strength, I did not like that part at all. (not to trash Catlike or anything, the tutorial is excellent - just not what I need here)

I wanted to make the wind more organic, simulate that too so it'll appear to have some validity to the terrain.
I looked for tutorials, libraries, or anything to help me out with this - nothing fruitful came up on my radar, so I started creating math for this.. (oh geez!)

I need ways to represent both angle and strength separately, and I wanted both of them to be normalized to a 0-1 range so I can later use them however I want.
I need to be able to know what hex neighbors a given direction is pointing towards and the ratios between them (a direction can point at only 2 hex edges at a time)
I need to know how the terrain effects the wind.
And I need a way to add/move the wind from one hex to another - how the hell do you that that with straight up angles? let's just create a function that converts an angle to a vector and do some simple and familiar vector math.

This surprisingly took a shorter time than I thought, I got it down in 3 days, but my brain was done for, math is not my strongest side, had to take some time off to recover mentally.

Coming back to it I started creating the wind simulation, what I did is generate the wind before the water cycle starts, and it was then used as conveyor belt for the clouds.
Finally the clouds aren't just hanging around water, they started to be effected by the wind - major improvement.



One other thing that was annoying me is the water flow, at this point humidity was pretty close to what's on Catlike's tutorial, it evaporates into clouds (and precipitate back), flows to neighbors (faster if they are lower elevation, slower if they are higher elevation) and carries salt when it flows (leaving it behind when evaporating).

That made all of the hexes that have a neighbor lower than them significantly drier than the surrounding environment.
I tried playing around with the values for way too long and no combination gave me the results I wanted.
I tried blurring the final map, created an algorithm that does gaussian-style blur on a hex texture, but that only took care of around 30% of the problem... oh well, at least I got that blur thingy, that'll be useful sometime.

The solution I came up with is to separate humidity into two "layers", surface water and ground water.
Surface water takes the role humidity currently plays, moves exactly the same way, evaporates (and receive precipitation) and carries salt.
Ground water acts as a capacitor of sorts, they flows to rules very similar to that of the surface water but much slower and they do not evaporate or carry salt.
The way they interact with each other is by osmosis, they try and reach a "biased equilibrium" ( 20% surface and 60% ground is "equal", for example - ratio is editable)

That worked great, made the water cycle much more stable.
At this point I started working on other stuff, like the building system and plant system, but they'll get their own development post, this one is about the climate.

After playing around with the climate system for a while I saw that it wasn't good enough..
The way you create terrain, generate wind and then water+salt works in a very unfriendly way to the user, and while that is part of the map editor - I still want as much people as possible to use it.
But the biggest issue I have with this whole system is that it's static, nothing you do in game has any effect of the climate, and that was part of the point to the game - sure, the plant and animal system are much more prevailing here gameplay-wise, but the climate itself is an integral part of the whole environment.
If you terraform field into a mound and build a castle wall over it, it should have implications to the climate in the long run, and it's just not feasible to run the climate baking algorithm every time there is a change to the map, it also means that every changes takes effect instantly - that's not gonna do at all!

After doing some thinking, I decided to make the climate dynamic in game.
This was a risky step to take, I wasn't sure at all I could pull it off in it's current state and I still had stuff I wanted to add to this system - but without this I don't think the game can be what I want it to be, so time to be a little crazy with my decision making!
I made some big changes to how the algorithm works, I tore the wind logic and the water/salt cycle logic out of their separate functions and created a new system that alternates between iterating both of them.
I don't mean to make it sound easy, it took a couple days to figure out how to structure it and then debug all the new errors.

The magic number I came up with for one whole iteration time is 37.5 seconds(on X1 game speed), a game day is 900 seconds long, and the climate ticks one time each game hour (900/24=37.5).
Now that's all fine and lovely - but how much does the climate simulation actually take? well, it's not that simple.. that depends both on the hardware it's running on and the map size, and we're still not done with adding stuff so I'll get into some actual numbers later on this post. (spoiler alert - we're good to go!)



Okay, so by this point I've got a climate system I'm happy with, it solved all the previously mentioned problems.
Players no longer need to fiddle around with 2 messy "baking" systems in the map editor - they just unpause the game time(or hit a button that simulates as fast as possible to get a new map to simulate quickly), and now if you do any sort of terraforming, build big buildings, consume or dump significant amounts of water/salt into the simulation you're gonna effect the whole climate, and it's gonna slowly work its way to accommodate changes, one iteration after another.

And to top it off it made the visuals much nicer - on the previous version both the wind and the clouds were static during gameplay, now both of them move and change over time, creating some great organic visual effects, I just need to find a better way to draw them, right now I'm just drawing a white hexagon with the GL library above each hex with alpha(transparency) by the cloud value, and that's fine, but it could be much nicer.
One thing I'm considering is sticking to my current style but give the clouds some serious geometry.
Another thing I want to experiment with is creating volumetric fog to visualize the clouds, but doing that kind of stuff is not my strongest side... best to hold off on that for now.



Now, the only thing that is missing is temperature, temperature plays a quite the role in the simulation and it's split to 2 layers in a similar way to water, air temperature and ground temperature.
High temperature increase evaporation rate, low temperature decreases evaporation rate.
Evaporation causes cooling, precipitation causes heating.
Freezing ground temperatures stops water from moving, trapping water on cold mountain tops.
Air temperature difference creates wind, wind carries air temperature just like clouds.
Ground temperature moves with ground water movement.
Osmosis happens between air and ground, like surface and ground water but this time it's a true equilibrium regarding the levels but the ground has much more thermal capacity - one hex with 100% ground temperature has the same "kinetic energy" as 42(by default, editable) hexes with 100% air temperature.
So if a hex is subject only to osmosis, ground temperature is at 100% and air temperature is at 0%, they won't meet at 50%, they'll meet at around 98%, if ground is at 0 and air is at 100 they'll meet at around 2%.

This addition was the final piece of the climate puzzle, now all that's left is to fine-tune and optimize it.

This is around when I started recording my development, I made a little video showing how the air temperature and wind interact.
It's a hydrothermal vent in an empty world, so you see a central point that is being heated.
You can see it on youtube



The major problem I encountered is salt taking over everything in certain maps..
Let me explain more clearly, the way water and salt enters the system is though the waterbodies, they add surface water and salt directly to the simulation, if a waterbody is fresh and not salty it removes from the simulation.
If we're trying to simulate a map with a large shoreline what ends up happening is the shore just keeps getting bigger and bigger, eventually taking over the whole map, I had to find someway to control it.
The first thing I tried was making the salt decay over time, that did the job, but created another problem - salt planes started disappearing (how did I not see this coming?).
After a little think what I came up with is to destroy a little salt when clouds precipitate(rain) - shores are always gonna have some amount of precipitation, even if the wind is blowing directly from inland out to sea 99% of the time, and a dry salt plane doesn't get rain so it won't get bothered.

I've made a few timelapses of a the simulation at this point, and you can see the difference visually.
Version 1 - when salt was just destroyed over time
Version 2 - removed salt destruction over time & changed how clouds are rendered
Version 3 - continuing the map from version 2, rain is now destroying salt.



About the terrain textures, they indicate the climate conditions.
it goes from sand to earth to grass based on water, high salt levels move it back to sand.
it's combined with a rock texture based on the terrain hardness, soft (no rock), medium ( 1/3 rock) and hard ( 2/3 rock)
if temperature is low enough and there is enough ground water you get snow on top of the texture.

You can see a video showcase of all different texture on youtube



About the iteration times, like I've said it depends both on the hardware and on the map.
This currently runs on the CPU, but I'm planning on doing all this work on the GPU once I figure out compute shaders, that should improve time significantly.
On the map side it depends mainly on the map size but the terrain complexity also plays a role here.
The numbers listed here are on my machine (intel i-7 7700K) and are for "worst case" map (highest complexity), and I'll also take this opportunity to list the map sizes I have planned.

Mini (35x35) - 700ms
Small (50x50) - 1800ms
Medium (70x70) - 3600ms
Large (100x100) - 7300ms
XLarge (135x135) - 13000ms

Those would be the common map sizes, but you can do any size you want and you can use different sizes for each axis (75x110, for example)

Creating a smaller map (10x10-20x20) can be useful to play with the climate values and see it iterate fast before starting it up on a big map.

Creating bigger maps is possible but requires a lot of RAM, the limit for 4GB is around 50x50, for 8GB is around 80x80 and for 16GB is around 140x140
I can't say for more RAM seeing as I only have 16GB.
Those aren't exact numbers either, seeing as not all the system ram is available to me and I need to also do other stuff with it, I was able to create a 200x200 map on my 16GB machine, but I had to drop the draw distance all the way down and it crashed after trying to generate the other stuff on the world (plants, animals, buildings)

And like I've said before - my target iteration time is 37.5 seconds (37500ms) on 1X game speed, judging from these numbers the available game speed in the world can be up to X10 on stronger machines and smaller maps, and at least X2/3 on weaker machines and bigger maps.
I think that's perfectly acceptable.



And that's about it for now..
Wow, this post is way longer than I thought it would be, thank you for reading!

Thursday, March 18, 2021

First Steps: Creating the terrain and wall systems

The first step in creating the game was the terrain, I went through a fair amount of trying stuff out myself, I good some decent results but they didn't work the way I needed them to work for the game.

One thing I experimented with is shallow water equations (that's how they implemented the water system is Cities: Skyline) with all kinds of erosions and tectonics.
rocky terrain rises from the fractures and erodes into sand the water can carry, it was very cool and I planned soil to be generated from organic decomposition - but it didn't go with the well with the rest of the design and didn't scale to the map sizes I need... just too much for me, let's save that one for Hex Hold 2 ;)

Finally I decided on using Catlike Coding's Hex Map tutorial.
The terrain, water and river mesh triangulation code is pretty much what is featured in Catlike's tutorial, changes I made to them are very minor.
I created my own chunk/batching system, and used Amit Patel's hex guide for all the hex related math, coordinate system and etc.

What I changed on the visual side is the textures and the terrain shader - I added specular, smoothness and AO textures.
Also tried adding normal maps, but it didn't go well with the aesthetics.

The terrain textures reflect the state of the climate, you can think of them having 3 layers.
the bottom is the rock layer, it shows the terrain hardness, terrain can be either soft (showing no rock), medium ( 1/3 of the texture is rock) or hard ( 2/3 of the texture is rock)
the middle layer is the main layer, it goes from sand-earth-grass depending on the total humidity (ground and surface water) and salt levels (I need to add temperature here too)
the top layer is snow, it comes in 4 levels (none, light, medium, heavy) and it shows when it's cold and wet enough (ground temperature and ground water)


You can see a video of all different terrain textures on youtube



After the terrain system was completed I began work on the castle walls, I wanted something similar to the walls in stronghold, the requirements I came up with are:

  • each wall segment takes a full cell

  • can connect to neighboring segments and to special buildings

  • can be made into a gatehouse and have stairs 

  • can have battlements: crenellations, machicolations and murderholes

Creating a system that does all of this wasn't trivial(at least for me), I spend about 2 weeks figuring out how to triangulate and implement it, here are some of the sketches I made during that time:




Pen & paper? where'd I get such ancient technology? anyway, you can see a video of the wall system in game on youtube


And that's how the game started to materialize..
Thanks for reading, if you have any questions feel free to ask.

Sunday, March 14, 2021

What (TF) is... - Hex Hold?

Hex Hold is a medieval themed city builder-RTS hybrid set in a dynamic environment, in this introductory post I'll briefly go over all of the major elements in the game, so without wasting any more time..


  • The main focus of the game is the city builder aspect, you play as the ruler of a faction from a "bird-eye" view and you need to make sure you and your kingdom survive though out the ages.

  • The RTS comes into play in that you can meet other (AI) factions in the game, faction to faction interaction include (but are not limited to) resource trading, citizen immigration/emigration, and of course.. war. and alliances, probably.

  • The "environment" (climate, plants and wildlife) is completely dynamic and your actions can effect all it's aspects and come back to bite you in the butt, when expanding you will have to consider more then just strategic location but this also creates the option to utilize the climate in war, kill all the prey near your enemy to cut down his food supplies and send nearby predators to find the only prey is his citizens, plant some useless and aggressive plants around him to overgrow the local plants and wreak havoc on the ecosystem, geoengineer the map(by building a dam or whatever) to flood your opponent into a swamp or dry him up to a desert.

  • Climate system include surface/ground water, clouds, salt/minerals, ground/air temperature and wind.

  • Plant grow, spread and die, they depend on the climate system to flourish, factions harvest for food and materials, herbivore wildlife eats plants.

  • Animals live in the world, eat plants or hunt other entities(animals or faction units like citizens or soldiers), reproduce and die, factions can hunt animals for food and materials

  • You do not have direct control of your citizens but can influence their actions and behavior in many ways like laws, regulations, healthcare, education, work, housing, entertainment, etc.

  • You have direct control over military units, move here, guard this, attack that - you know, the usual RTS stuff.

  • You build your kingdom by placing build orders, citizens working in construction will proceed to carry out the build itself. (prepare ground, collect resources, build building)

  • Production chains will be quite complex, but will be easy to automate if you're not into micromanagement, you just just request an end product and your citizens will create it (provided you gave them some access to all the requirements)

There is much more to the game and each element, all will have their own dedicated post soon, thanks for reading and check back for more!