- Home /
2D Procedural Terrain with SpriteManager
Hello and thanks for reading this thread. This is my second question here, I hope that I'll find the answer!
Recently I have been working on a 2D game using sprite manager and a combination of other tunes in order to disable all the effects related to the Z axis or rotation around the Y/X axis.
Well the tricky thing about it is the following: I want to have procedural terrain generated based on a string variable (a seed) and generate the whole world out of 2D blocks (something like the game Terraria, just I wont ever use that big worlds since my game isn't based on digging and editing the terrain mainly (So far I checked, SpriteManager can handle about 8000 sprites before it freezes the game for unknown reasons).
Anyway here is what I planned to do:
I started at a point in space (0, 0, 0) lets say, then I generate a random Y amount and accordingly to that the X value (there is a few other calculations but its not important at the time). Once I have a (start) and (end) X,Y position I add it to a jagged array something like this:
float[][] paths = new float[1][];
void SaveCoords(float fromX, float fromY, float toX, float toY)
{
int LastElementID = paths.Length - 1;
paths[LastElementID] = new float[4];
paths[LastElementID][0] = fromX;
paths[LastElementID][1] = fromY;
paths[LastElementID][2] = toX;
paths[LastElementID][3] = toY;
Array.Resize(ref paths, paths.Length + 1);
}
Then I just call the method , pass the old X,Y coords, the new X,Y coords in and it stores them down. Later I have shot a DrawLine() for each of the elements inside the jagged array something like this:
for (int i = 0; i <= paths.Length - 2; i++)
{
Debug.DrawLine(new Vector3(paths[i][0], paths[i][1], 0), new Vector3(paths[i][2], paths[i][3]), Color.white);
}
The result ended up as expected:
The height and width of each line has a random factor but it is calculated so I have left a few parameters to tweak up the height and the width (flatness and sharpness) of the hill amplitudes. Basically I could generate the exact map as below just 10 times larger (this is important for what I'll ask later on)
The next idea was to create a 2D grid of sprites , lets say 200x40 with blocks (well squares) that are of dimensions: 16x16.
Next thing I did was thinking about the way how could I edit my flat 200x400 terrain of blocks based on the line generated above. The answer I came up on the end was shooting a SphereCast instead of drawing the lines, each of my squares created with the SpriteManager has its own colider (it just cant rotate, bump off or anything like that) simply for collision detection.
I ran unity, it rendered everything in about 5-10 seconds ( the time includes adding all the squares to the hierarchy. On a build run it takes about 3 seconds ), and because of SpriteManagers awsome property to create a mesh of sprites, the whole thing is rendered with one DrawCall resulting in no lag. To help you visualize the whole thing I took a screen shot:
Next on is to shoot the SphereCast based on the line above, and for each collision, write the blocks ID into an array. Once it finished the run it passes the array to another function which uses a list of all block IDs, then it loops through all of them and compares IDs. If the blockID == ID of the colided block, delete that block. (Basically it should delete all the blocks that collided with the sphere on its way & make the same "line" as on the picture 1 just made of holes (I know it won't be exactly the same due that a line made of squares isn't super flexible, but that would be fine for me).
At this point I remember you of what I said above: I can generate a map line with smoother/sharper edges for my squares to collide with and create a copy as detailed as possible. Later on I won't leave this 'line of holes' inside a grid of squares, but invert it. I will only leave the squares that the SphereCast collided with, and remove the rest. So I get a nice little surface. Also I won't render all 8000 of them.. I'll just place their colliders , do the calculations, run the SphereCast, and render only those that the sphere colided with, then remove all the other colliders.
But I ran into a problem which I don't seem to understand here is a picture of both:
[1]: http://img818.imageshack.us/img818/8240/seed2.png
[2]: http://img818.imageshack.us/img818/8240/seed2.png
[3]: http://img818.imageshack.us/img818/8240/seed2.png
[4]: http://img818.imageshack.us/img818/8240/seed2.png
[5]: http://img818.imageshack.us/img818/8240/seed2.png
[6]: http://img818.imageshack.us/img818/8240/seed2.png
As you clearly can see block 547s collider was directly within the path of the SphereCast (I have rendered a line to indicate the path), but it was not noticed at all where few others were processed correctly. I thought it might be the spheres thickness so I tried a range of 0.5 to 10 thickness, resulting in less and less squares being cut out.
I have no idea what is going on beside , maybe SphereCast has problems when it hits multiple objects at once and cant .add() it to the arraylist? This is what I wanted to ask here. Sorry on the huge post I only wanted to explain every detail.
And for the last here is the code part of how I use the sphere:
RaycastHit hit;
for (int i = 0; i <= paths.Length - 2; i++)
{
Ray direction = new Ray(new Vector3(paths[i][0], paths[i][1], 0), new Vector3(paths[i][2], paths[i][3], 0));
Physics.SphereCast(direction, 1f, out hit);
if (!hitBlocks.Contains(hit.collider.gameObject.name))
hitBlocks.Add(hit.collider.gameObject.name);
}
SendMessage("KillBlocks", hitBlocks);
I know this might be super inefficient method.. but it was the simplest and only one I could think of at the time. If some one has another idea tho, feel free to post!
Thanks in Regards ~Ilhan
Your answer
Follow this Question
Related Questions
2D Procedural Terrain Generation + Pooling 2 Answers
2D Tilemap room prefabs for dungeon generation. 0 Answers
Procedural terrain- of the 2D circular variety. 0 Answers
How would I procedarally generate a terrain? 2 Answers
Creating 'worms'-like terrain 1 Answer