This was such an incredibly busy two weeks for me especially the last week. There have been a ton of changes to the game and I’ll try to capture the majority of it here.
Very small side note: the in game models for the power and hub networks are very much “work in progress” and are going to be replaced by the next blog post, hardly worth mentioning.
Water will be an important element in the game because it will be a critical element in generating power. Originally I wanted to restrict the player so that they couldn’t walk into the water but I thought it would be easier from a development perspective to let the player swim in water. I thought about the possibility of adding in procedurally generated colliders that would restrict the player from going in water but I want the player to have the freedom to explore the map however they want so I made it possible for players to swim in water instead. It was also something super small to add to the game to make it a lot more fun in my opinion.
Adding water was a huge change to the way that terrain is generated, first of all I’m changing the height of the terrain dynamically and I don’t want the water to feel weird or out of place so the texture generator needs to be able to take water into account when generating the terrain texture. In the most recent build, water is always surrounded by sand and there should only be sand beneath the water. Here is a top down view:
This effect is really subtle right now and I want to make it more prevalent but I have to make some upgrades to the texture generator before I do that. If I increase the intensity of the sand right now it will start creating large patches of sand where there is no water which isn’t what I intended.
One thing that I considered before adding water and dynamic height into the terrain is that it makes placing buildings extremely awkward. What if I want to place a miner on terrain that isn’t on the same elevation? Well, for now you can’t do that, and the indicator will update when you encounter this situation:
There will be buildings that you can place in the water that will have the opposite behavior: they can only be placed on water, not on land. This will come in a later update however and is not ready to be shown off quite yet.
Power + Hub changes
Over the last 2 weeks I spent quite a bit of time working on some abstractions for connecting buildings together. The main two systems that are going to exploit this abstraction are power and the “hub” network. The hub network is going to be used for transporting items quickly between buildings automatically.
Certain buildings in the game will require power and in order to provide buildings with power you will need a power source and power poles. When poles are placed within the same area of effect (AOE) they become part of the same network. You can see the new AOE indicator here:
The AOE indicator is actually its own procedurally generated mesh. This means it will conform to the terrain or water that it is on top of. Here is a more extreme example:
This isn’t just a nice visual feature, it will actually be an important aspect of the game to be able to see the AOE of buildings that are either elevated or in the water. You will be able to place certain buildings in the water and it will be important for you to be able to see where the AOE starts and ends for power poles.
Hubs are similar to power poles except instead of providing power to buildings they move items. Specifically, hubs will try to move items from output slots of buildings (like miners) and move them to input slots (like furnaces). Here is a super simple example of a hub network:
On the left you have miners which are extracting copper ore from the terrain and on the right you have a single furnace that is smelting the copper ore into ingots. The building in the middle is moving items from the miners to the furnace. To be fair, it doesn’t look like a whole hell of a lot is going on here, but don’t worry this will be improved in the near future (you’ll have an indication that items are moving).
Now what if you want to move items a much further distance? Well you can chain hubs together to create a network just like power poles:
This is a more complicated example where the miner and the furnace are not able to communicate until the hub in the middle is placed. This is because the 2 hubs that exist on the map are not close enough to each other to share items. The addition of the third hub allows the 2 existing hubs to communicate which in turn allows the miner to communicate with the furnace.
That’s it for this week, I’ve done an insane amount of work this week and I’m really excited to show off some of the stuff that didn’t make it into this post. For the people who want to know more about the development challenges I faced this week I wrote quite a bit about it below. Otherwise thanks for reading, and consider joining the discord!
Preface: This is going to be a long and complicated description and requires some basic knowledge of programming/algorithms to understand.
Placing power poles and hubs makes a lot of sense from a player’s perspective, but from a developer’s perspective how do you know that buildings are able to communicate with each other?
Lets first tackle some ideas that everyone probably has on their mind:
- Buildings should keep a list of other buildings that are within their AOE. This represents the “connections” between buildings.
- When a new building is placed, that building should look at all buildings within its AOE and add those buildings to its list. This new building should also communicate to the other buildings that it now exists (this is basically a building – building handshake).
- When a building is destroyed, it should tell all buildings within its AOE that it is being destroyed and they should drop connections to that building.
Okay, now just with these 3 things implemented you can do everything that I did in this post: buildings can share items, share power, and basically do everything that you need just by querying buildings. Buildings know which buildings they are connected to and those buildings know which buildings they are connected to, therefore asking any building “do you have x item” just becomes a depth first search problem. However, doing this every frame for every building is extremely cumbersome and it does not scale well at all. So now the obvious next step is move to an observer pattern:
- All buildings should register themselves with a “NetworkManager” and that determines which network the building is in.
- The NetworkManager should query the building and determine what inputs and outputs the building has, these are added to two different lists: allInputs and allOutputs.
- Every frame you can now iterate through the list of inputs and see if there is an output that has the item you are looking for.
This implementation scales much better than just having a bunch of buildings communicating with each other and is where most people would start. However we’ve now introduced a couple of major issues:
- What happens if you add a building and its AOE affects 2 or more buildings that are in different networks?
- What happens if you remove a critical building that was required for a network to operate and you’ve now split it into 2 or 3 or more networks?
These two issues have simple solutions but the solutions themselves require some explaination.
Adding “bridge” Buildings
There are obviously many ways to address this issue, but the approach I took is I decided that the building being added needs to “kick off” the process so to speak. When a building is added, that building first determines if it can find at least one building in its AOE that already belongs to a network. If it can, then great we will assign the new building to that network. Then any other building that is within our AOE will also be assigned to that network — and this is a lot simpler than it sounds:
I’m using some programming jargon like graph and node but you can simplify it this way: graphs are just networks of nodes where nodes are connected to each other and nodes in this context are buildings. And yes I comment like a madman, every function in my codebase is commented like this.
You can see that adding a new building to a network is extremely simple, it basically boils down to: do I have an existing network? Okay, notify that network that I’m leaving and then add myself to the new network and if I have any children I’m going to add them too. The
false parameter in the
Remove(...) call just signifies that the graph shouldn’t try to rebuild itself right now because we’re in the middle of a complex operation, a rebuild will happen later (rebuilds bake the input/output mappings for buildings).
At the end of this operation, we tell all graphs to check to see if they don’t have any nodes remaining. If they have no nodes remaining, they are destroyed.
Removing “bridge” Buildings
Removing bridge buildings is a bit more complicated because when you look at all of the buildings in your AOE they are in the same network, so there isn’t an easy way to distinguish who should be in their own network now. Take this example:
When the green building in the middle exists, both the blue and the red group of buildings are in the same network. When this green building is removed, there is nothing connecting those two networks and they should now be two separate networks, each containing 3 buildings.
Here is a brief description of the algorithm I came up with, which probably has an actual name but I am not aware of it:
- Duplicate the list of buildings that are in the entire network
- Remove all buildings from the network
- Add the first building to the network, this should recursively add all children to the network.
- Iterate through all of the rest of the buildings, if you find a building that has no network, create a new network for that building and add that building and all of its children to that network
- Repeat step for 4 until there are no buildings that remain that have no network.
This solution actually works really nicely because when you remove a building that wasn’t a bridge, you have no change, the buildings are just removed and re-added to the same network. If you have a very complicated situation where many networks need to be created, this algorithm will produce all of the networks required. Since buildings are not removed very often this solution has been working nicely for now.
Design Challenges Conclusion
There were a lot of fun an interesting challenges this week and I’m sure you guys are wondering what logic I used to do certain things and I can definitely go into more detail in the next blog post if there are specific things you guys are interested in. If you have something specific you want to ask me about, hit me up on Discord.