Hello Unreal community,
I am working on a personal project where I am trying to generate in a procedural fashion a hex tile map. When I first started this project it was just for some c++ practice but I’ve been getting really involved in it recently. I work on this on my spare time, I am programmer for an indie dev company and I’m working on another unreal project at the same time.
Ok, moving on, I will give some context on the encountered problems and the current situation.
First encountered problem and how I solved it:
When I started the project I aimed at creating the hex tile grid generation algorithm first and I did it using Actor hex tiles. I am using axial coordinates in my grid. I managed to implement neighbor functionality to apply a cellular automata algorithm afterwards. Until this point everything worked flawlessly and I had the desired effect on the generation of the map. Nevertheless, I quickly learnt that having big maps using actor tiles would decrease performance very heavily considering that each actor would make a draw call on the engine, which wasn’t acceptable to me. And yes, as you would expect, I read Zeustiaks hex world generation threads. I found out that using one Instanced Static Mesh component per tile would improve the performance by a ton by having each one make only one draw call per tile type. This led me to change my hex tile grid generation algorithm to support the ISM’s. This involved generating random seed tiles from an array of ISM’s for every position of the hex grid.
Second encountered problem (replaced structs with custom class):
Since I had to remake the map generation to support the instanced meshes I lost a lot of functionality regarding the data that I had been storing in every actor tile when I first started the project. So, again (thank you Zeustiak), I looked on the forums for any information on how to proceed from this point onward and I found out that the best way was to index every tile to their relevant information. I have my ISM’s in an ISM component array. So, basically the information I use is: ISM component index on the array, the Instanced Mesh index that was created by this ISM, and the related X,Y position to the hex grid that this instanced tile is using. Until here we are fine. To manage the indexed information I am using a USTRUCT array. The problem starts when I try to use the information I am saving.
For some reason the neighbors are not being saved in the array inside the Ftile_indexer struct. I am accesing the neighbours member directly, I tried to do it though a function and it is still not working. I even tried to initialize the array and it didnt work either. Anyone has any idea on how to solve this, or tell me what is exactly what I am doing wrong? I read around the answers hub that there were some problems with the UStructs and 2D arrays. But Ive seen other people pull stuff like this off without problems so I am thinking that there is a mistake I am making somewhere.
Thanks very much to anyone that can give this a look and help me out a little, even if it is with just an idea to replace the system or make it work.
Cheers!
UPDATE1: I removed the USTRUCTS and replaced them with a custom c++ class that I made. The only thing holding me back a little is that I am using the operator new every time I want to add an object of this class to the TArray. I havent done c++ programming like this in a while so I am a little scared of dangling pointers.
New question: I am only using the new operator when I am adding a tile_index object to a TArray in the manner of: tile_indexer.Add(new tile_index(…)); Does the TArray deallocates the pointers on its own or do I have to do it myself at some point. If I had to do it myself, where exactly should I be deleting them in the Unreal classes. I would have to delete a TArray to my class in the map_controller which is an actor, and I would also have to delete the neighbor array contained my tile_index class. Anyone know this?
Thanks again!
UPDATE2: I finally got my indexing system working. I ended up sticking to the pointers. Cellular automata works great and afterwards I apply a cleanup on the end. I’m generating about 2000 tiles and performance stays at about 80 fps compared to the 20 fps I was getting using actors. Here is a screenshot of the results:
The only perceivable problem I can find right now is that when i spawn the initial hex tile grid it draws it at 120 fps. After I apply the cellular automata algorithm and I replace existing tiles it lowers the fps to 80 on the final grid. I am not sure why this is happening I am gonna have to do some debugging. I’ve already checked my indexer and it has exactly the same amount of entries on both the initial and final hex grid. What I’m thinking is that maybe it is not deleting correctly some of the initial instanced meshes when I am replacing them, but that doesn’t make any sense in my head. I’ll update again if i figure it out. In the mean time, I’m gonna leave for anyone that is interested in this topic the code I have up to this moment. EDIT: I took it out, the past version was way less inefficient than the current one, maybe i’ll post it again in the future.
Map generates 2000 tiles in about 30 seconds or so in my PC, next up I’m gonna try to optimize the code for faster generation. I will also try to make it look nicer
UPDATE3: I took my baby to the beauty salon! Plants and trees are generated procedurally based on certain conditions. These are the results and running at 100 fps!
UPDATE4: Ok, so everything is working good up to now. I tweaked my code so that instead of adding and removing instanced tiles during the cellular automata( which apparently are really costly operations for the engine) I just instance the final resulting grid. This has increased performance up to the point I can generate 30000 tiles in like 30 seconds and get a whooping 200 fps when I play the game on standalone. I am still thinking I might have a memory leak because the engine crashes when I try to erase my indexing pointers on EndPlay(), so up to now I’m not really sure where I should be deleting them, hope to solve this soon enough.
New problem has arisen: The first time I play the project in a new editor window it runs perfectly smooth at about 120 fps and takes 8.35ms to render the frame in average. You can see this in the GPU profiler i did for this instance:
LogRHI:Warning: 100.0% 8.35ms FRAME 233 draws 5503800 prims 9053677 verts
LogRHI:Warning: 94.3% 7.88ms Scene 152 draws 5503556 prims 9053189 verts
From here onwards every time I play the game in the editor the fps decreases by about 10 and the draw time increases exponentially too. By the 10th time I click on play in new editor window I get the following results:
LogRHI:Warning: 100.0% 33.26ms FRAME 234 draws 4718972 prims 7717523 verts
LogRHI:Warning: 96.0% 31.94ms Scene 152 draws 4718792 prims 7717163 verts
It takes 25 ms more to render the frame and my fps has decreased to around 20 fps. I have no idea where this is coming from. Is this a known bug with the ISM components? Are they still in the draw list every time i regenerate the map? This does not happen when i run the game as standalone though. Must be some editor bug. If I restart the editor, it goes back to 120 fps on the first run. Pretty weird.
Well, anyway, if someone know why this is happening and if there is a solution I would highly appreciate it. In the meantime, here are some pictures of the procedural map looking more beautiful than ever:
This one took a while to generate, its around 100000 tiles running at 100 fps.
I’ll update when I get some results!!
UPDATE5: I have implemented height maps in the generation. The algorithm I am using is Simplex Noise to generate values between -1 and 1 and multiplying by an offset. I am still not sure how to randomize it more, still figuring the algorithm out. Anyway, here is a screenshot:
I think im gonna have to change my water material, is there a specific way to work with tiled water and heights? Or should I just stick to spawning water at a certain height and get on with it?