- Home /
Generating and altering a mesh at runtime as a grid based terrain
Hey guys, I've just gotten stuck into my first big project with Unity (loving it BTW) and I've hit a wall. The game is going to be using a tile grid style map that is procedurally generated and will be able to have buildings placed upon it by the player. I have been experimenting with using individual prefabs for each tile shape (flat, sloped)and then replacing the material to indicate the tile's type (grass, sand, etc.), but it takes a long time to set up at the start (when instantiating all the tiles) and it seems a little wasteful to have thousands of cube floating about. I did a little research into generating a custom mesh at run time and then messing with the individual vertices to change the land's shape, but I'm really having trouble wrapping my head around it. Ideally, I'd like to end up with a landscape that works similarly to that in 'Populous' (google it for screen shots if you don't know it) where I can just create a 2d array of tiles and use that data to set the vertices and materials of the "tiles" on the giant landscape mesh. Can anybody give me some pointers as to how I'd go about achieving this? Should I just go back to the thousands of prefabs method? Is there a better way to go about achieving this kind of interactive landscape???
Answer by Owen-Reynolds · Jun 05, 2011 at 04:42 PM
All the ground is, is a boring old 2D array of floats: float[,] HT;
The detail texture strengths are stored in a regular 3D array (2D for the ground, the 3rd dimension is the strength of each texture. D[6,3,1]=0.25
sets the second texture at ground grid (6,3) to be quarter strength. Make sure D[6,3,*] adds to 1.)
The trickiest part is remembering you are setting corners of squares, and not squares. And keeping straight that if your ground squares are 5x5 meters, than world 38,12 is just past terrain corner 7,2.
Look at TERRAINDATA for all the names. You can't change the size of the ground array (Width/Height) that I know of, so make a Terrain of the correct size first. May as well manually load all the detail textures you want. SetHeights copies your array to the ground. SetAlphaMaps copies your detail strengths.
Not sure how to look up the "World width" of each terrain square (think it might be in AlphaMapWidth,) but you can compute it with size/heightMapWidth
. I'd make/change some terrains and just print all the vars, just to be sure.
Lastly, to make changes "stick," you have to use GetComponent<Terrain>().Flush();
(listed in Terrain, and not TerrainData.)
lol wow- I think this is pretty much exactly the answer I was looking for. :) A lot of it sailed clear over my head on the first pass, so I'm going to have to re-read slowly it a few times and do some experiments, but I think this could give me a direction to work towards. Just for clarification, say I have 3 textures on the landscape [dirt,grass,sand], would I use the texture strength settings to change the type of say tile [2,5]to sand with something like 'D[4,10,0]=0f; D[4,10,1]=0f; D[4,10,2]=1F;'? Thanks heaps for taking the time to help me out; It's really nice to know that there's people out there ready and willing to give you a hand when you get stuck :)
That's what worked for me (but only backwards, reading textures.) Of course, D is just any array and needs to be installed with SetAlpha$$anonymous$$aps then Flush.
I finally got in and did some experiments and I think this is going to be a perfect solution for what I want. Actually translating my tile's data into alterations to the terrain is going to be a brain twister, but simply toying about with the terrain's vertices and the alpha map is surprisingly very easy (after hours of staring hopelessly at the script reference, I just jumped straight in and got an easy little test working in $$anonymous$$utes) Just wanted to thanks again for steering me in the right direction as this was one of those things that was always going to keep me awake at night if I didn't find an amicable solution to it.
Let us know how this works for you. (An example posted in the Showcase forum would be great)
Answer by Eric5h5 · Jun 05, 2011 at 04:32 PM
No, don't use zillions of separate gameObjects. See the procedural examples for examples of using the Mesh class.
Don't know how this helps if you want to address each of the tiles as individual objects, i.e. allow them to have scripts attached, colliders, etc. Perhaps this isn't a requirement.
@ikeo: A landscape that works similarly to that in Populous has no use for individual scripts, colliders, etc. Offhand I can't think of any terrain system that would; it would be very wasteful without any corresponding benefits.
That's right, and that's why I wasn't comfortable using tons of GameObjects as it just felt really wasteful. The landscape I want should treat all of its tiles the same as they only really serve to present the player with an obstacle (where you can and can't build, what should go where etc.) and the real magic should happen in the game pieces themselves. I did read through the procedural examples whilst I was researching possible alternatives, but it hurt my brain and I wanted to make sure that that approach wasn't a dead end before devoting the next few hours to staring at brain hurty math. :D Thanks a lot for your answers, I really appreciate the help.
@Quimby_RBG: once you've done mesh construction a few times, it becomes less brain hurty.
lol good to know; It's been a pretty steep learning curve co$$anonymous$$g from making pure 2d games in C# to learning to work in 3d through an engine, but man it's nice to magically have a 3D game :D I'm still just treading water with this editable mesh thing, but I'm glad I posted this question as I'm starting to get a little traction with it now.
Answer by ikeo · Jun 05, 2011 at 04:49 PM
I've solved a similar problem, albeit not for terrain. I have thousands of meshes that need to be created/released based on player position. I use a combination of the object recycler pattern - i'll call it a pattern because it's basically become the accepted way to do reuse of large numbers of instantiated objects. In concert with the object recycler, I have a mesh loader which changes out the verts/tris/norms of the object's mesh on the next available free object from the recycler . Coroutines keep this working without locking the interface, although you can expect stuttering based on the size and complexity of the meshes that you instantitate. To your point about wastefulness, yes it is wasteful in one sense to have all those cubes instantiated at startup, but it is much slower to instantiate them in coroutines at run time. I can't vouch for any of the precooked tile solutions, as my solution needed to be a bit custom,, but there might be something that fits your requirements.
Yeah, I experimented with something similar (though a lot more basic) when I was first having some performance issues with my million GameObjects approach. I was basically using materials to demonstrate the different tiles and tracking their position relative to a stationary object and when they scrolled too far to the side and were definitely offscreen I'd change their position to the opposite side of the screen and change the material. (Sort of how you'd use parallax scrolling backgrounds, but with the tiles themselves looping around) I decided pretty quickly that this was a pretty crap idea as it was too hard to track the important persistent stuff (like the buildings and their respective data) and still had some frame rate issues. I was probably doing something wrong as I've seen full on $$anonymous$$ineCraft style voxel worlds run without a hitch in Unity, but from what I can tell the editable mesh approach to my landscape will be the best suited to what I actually want it for. Thanks very much for the suggestion though; That would definitely be the way I'd go for a more intricate procedural level generating solution, say for something like a exploratory platformer or something that requires more interesting set pieces. :)