I have a “grass” png file and 2 “dirt” png files: “straight path” and “corner”.
I want to create a flat 100x100 tiles ground that’s all grass, except a dirt path that’s randomly generated each time the game is ran.
The (x,y) pathing wasn’t an issue, what I’m struggling with it’s with it’s with the best approach to generate the meshes:
A double 0-to-99 for loop that creates 10,000 static meshes, assigns them the “box” mesh shape and assigns them the grass material if it isn’t a path tile, or the corresponding dirt material if it’s path. I’ve done this, and have read that’s inefficient (and my computer is suffering).
A single huge mesh object with a size of 100x100 tiles for grass, and the path overlapping it, like 1 pixel higher in the Z index, so it’s not noticeable for the eye. Given that the path tiles should be like ~120 on average, I’m saying like 9,900 meshes.
Any other option that looks more professional? I’ve seen that there’re things like procedural meshes and instanced static meshes, and many more things, but no idea if the can be useful for my purpose of telling them “make the element (13,79) have the dirt material instead of grass”.
Unlikely to ever hit 10.000 at once on a 2D game unless the tiles are really tiny as in Terraria tiny. Can still chunk load it both visually as for gameplay purposes.
Instanced static meshes make your pc suffer less. Better approach though is to store corner points of your grass plane, points of the corners on the roads, then triangulate using Delaunay. Gives you a single mesh. See my full delaunay implementation:
Smart, but why then tile it in the first place and not have a 4 vertice plane with an X vertice road mesh on top?
You can assign multiple materials to a single mesh, if you build it in Blender for example. You can also procedurally generate meshes in UE and assign materials to that. You can even instruct a single material to change appearance with an internal switch or by altering a material parameter externally.
This could also be a case for a procedurally built mask in the form of a material parameter. For example you could track where the player walks through snow and write those positions to a grid on a mask as white on black, then read that mask in the snow material. Any spot on the mask marked white is lowered to generate footsteps or flattened snow. Same type of mask can be used to blend between multiple materials, you would just write a value on the mask representing the material you want. A material parameter collection is useful here for global access.
This is easy enough. Since the path is procedurally generated you can walk from start to end and use the dot product on the current direction vs the next direction. The dot product returns 1 if the direction remains the same, 0 for a corner.
Also, if you define the path up front as connected vectors you can easily place the corner points as verts afterwards. Don’t start optimizing before you got it working though.
First of all, thank you very much for your detailed response and the effort you seem to have put on it. Honestly, thanks.
I’m sorry for the late answer, but I’m just a newbie, so I’ve panicked when reading “Delaunay” along with so many concepts I’m not able to understand, so have been these 2 days trying to understand your message, as it exceeds my (lack of) expertise .
I have a dictionary that takes (x,y) into boolean. True if that (x,y) tile is path, false if it’s not. I’ve created a function that randomly chooses a (x,y) from the map edge as path_start, another (x,y) as path_end, and randomly generates an accurately connected path setting the correct boolean values. That’s not an issue. The issue is how to translate that into meshes.
Prior to posting this thread, I’ve been playing with instanced static meshes. I’ve successfully created a 100x100 instanced static mesh, so I got a 100x100 uniform grass surface with no dirt path as the instanced static mesh only allows me to assign 1 single material for the whole thing, and not a specific material per triangle… Or at least I haven’t found the way to do it. That means, a complex process with 20K triangles that look exactly the same as putting a single box and applying the grass material to it. In other words: I still don’t understand what are instanced static meshes and what they are for.
So my intention when posting here was to get tips about which would be the simplest and less resource-draining way to make a rectangular terrain composed by square tiles with a randomly generated path crossing it. A random path which underlying logic isn’t an issue, as it’s developed already.
So the answer I’m looking for is which of the options is: 1 mesh/tile? Instanced static mesh? Procedural static mesh? Another I don’t know about? Which of these options is the one I should choose?
And something like “you can set the material for a specific instanced static mesh tile doing X” (incase that’s possible), or whatever that applies to the chosen option.
Thank you very much for your patience with this newbie
The problem with instanced static meshes is performance. I’ve yet to find a use for them myself other than just to visualize 3D grids quickly for debugging purposes of algorithms.
Need to get one thing out of the way, which I should have asked first. Is there a gameplay reason for the existence of tiles? Such as being able to replace a grass tile with a dirt tile? Otherwise I would forget about making all tiles a separate mesh entirely.
This is great, the grid is all we need.
This seems to be true for both the instanced and procedural mesh, but with the procedural mesh you would end up with only 2 meshes, one a grass plane, other a dirt road.
These don’t go well together. For a big terrain which makes your PC suffer, you would probably rather go with a more complex initial setup / calculations of which after completion of creating the landscape the PC will not suffer.
You can’t set a material per instance as far as I am aware, unless you use one instanced mesh component for grass and one for road.
If you wish to go the more initially complex route and really build a mesh from scratch, you most likely end up with a higher final performance but you have some learning to do.
Below is the first thing I come up with, first pancake is never the best but I have made similar systems before.
When you generate your 2D grid, say white == roads, then the center of every white tile represents a point on the grid. the size of the amount of units you want a tile to be, which could be 100cm a tile. On the image I have pulled them apart a bit so we can clearly see the corners of the squares we end up with.
The square corner points can be stored, then used as vertices to create a mesh with.
Optimally, the vertices you store would be shown in red:
A smart approach is possible to create only the square corners relevant for the mesh but that has to be thought through well to not run into exceptions. For now just store 4 corners per tile on the grid. Forgive me for my rough sketch:
A corner point can be stored on a map of key FIntPoint (XY Coordinate) to value FVector4 (4 corners of float values). A corner point can be retrieved by multiplying the grid position by the tile size (say X3X5 * 100 becomes X300Y500) and adding half of the tile size towards the corner points. When you collected all those red points it is done and ready for Delaunay. You can copypaste my delaunay implementation into your project and pass in the points, it will give you the triangles that optimally connect neighboring points over the entire path. That triangle data can be fed into the ProceduralMeshComponent as is.