No this actually looks quite simple. WFC would fill in areas with tiles that are allowed in combination and most likely to occur. WFC works better for small areas than entire landscape and would be pretty overkill here. Besides the difficulty of initial setup it is also not a perfect algorithm since it creates unsolved areas.
The editor you show is all manual work.
We can make Point a struct:
* float ZHeight
* LandscapeType
We can make Grid a class:
* Map<FIntPoint, Point> PointsOnGrid
* float PointSize (say X100cm,Y100cm,Z100cm).
Here’s where the fun starts.
What we see in the editor is that every point on the grid is somehow connected to another. At this moment we should have collected “Point” structs on all XY positions of the Grid class, where we need one (air etc has none, Z height is stored on a point).
Every point represents a tile connected to another one. First we need to triangulate the points, Grab my Delaunay implementation from here:
Math in Delaunay triangulation algorithm, too many triangles appearing - #4 by Roy_Wierer.Seda145
Now you have the entire surface plane triangulated, ready to be passed on to Unreal’s procedural mesh system.
Next up, you only have the center points of your tiles, which doesn’t look as interesting , they are just points and don’t contain the nice randomized edges we see on the land to mountain / water tiles. We can calculate those either before or after triangulation.
Next up we see how materials are blended from one tile to another. You could say “Z level 0 is water”, or “a slope of 50 degrees up from water is land, from land to mountain is stone, from mountain to mountain is stone”, since there are not that many possibilities here. Anything you paint over it manually would be stored as a “post” effect to apply on that base data. This allows to create that neat grass but also custom details like fallen leafs or bones or scorched earth.
For pathing you could think of AStar or vector fields. Z pathing is irrelevant. You could say “If a point is missing, or if Z difference to a neighbor tile > 2 then obstacle” or “if Z 0 == water then obstacle”. Of course you could store this data in advance since it is a constant on the map during gameplay. Having an editor for this could give some unique benefits as you could manually say “you can walk through this / up this” without depending on such hardcoding. This is common when you decide to path ladders for AI or magic bridges etc.
Shader implemented is a start, if you just want to darken space or hide units but it also means they are actually still there, just rendered differently. This leads to silly issues even seen in games as AOE4 today. Being able to click on mines you can’t see, know when trees are gone when the renderer doesn’t yet show it, or be a smartass and read the data from memory so you can hack the fog of war.
Put the grid on a GameInstanceSubsystem or other place you can globally reach it, GameMode is a good place for a server, then convert your unit location from FVector to FIntPoint (divide it by the point dimensions, then round it to int), then read what XY position on the grid your unit is on. Retrieve the Z of the point. Do the same for the enemy unit. check the difference between your unit and the target (TargetZ - YourZ), if it’s up then disadvantage otherwise advantage. How you implement this will probably affect a lot of things from AI to fog to squad to per unit logic, so it is interesting enough to collect in a subsystem of some sort.
Unless you need the 1001 features which I doubt for an RTS, make your own. It’s as simple as described above. Unreal comes with a 1001 things getting in the way when you want 20. It does come with some useful stuff like the ProceduralMesh which you can feed from the delaunay, no need for the UE landscape itself.
I would not even use the movement components, perhaps not even the pawn class for units but that’s another story. You don’t need the default pathing implementation or movement if you are simply going to move things on XY snapped to a Z at a constant speed like warcraft does. Saves the headache.
FDelaunay Delaunay = FDelaunay();
const TArray<FIntVector> Triangles = Delaunay.Triangulate2D(Points);
// Do whatever you want with the triangles if need be. can be used for pathing on its own, can be modified here, could also just go with another algorithm to generate a fancy mesh.
// TArray<FVector> Verts;
// TArray<int32> Indices;
// Spawn the mesh
ProceduralMeshComponent->CreateMeshSection_LinearColor(0, Verts, Indices, TArray<FVector>(), TArray<FVector2D>(), TArray<FLinearColor>(), TArray<FProcMeshTangent>(), true);
// Enable collision data
ProceduralMeshComponent->ContainsPhysicsTriMeshData(true);
“I don’t know how I would go about starting this.”:
Make a plugin as an editor module, write the editor there. Export created data in any way you like and try to avoid UAssets and Blueprints like the plague.