- Introduction
- Setup
- Map-Generator-Summary
- Map-Generator-Basic-Settings
- Map-Generator-Advanced-Settings
- Map-Generator-Debug-Settings
- FAQ
- Links
- Help
This system will create an updatable mesh from a map. It uses the marching squares algorithm and supports random meshes, manual meshes, saving/loading meshes, automatic chunking and more!
- Download the Asset from the Unity asset store.
- Import the package.
- For an example of some of the map generation in action, head over to the Showcase scene. For an example of how to implement a miner, go to the Miner scene
- To add a map to a scene, just drag the mapGen prefab into any scene and click play. It should generate a little map for you. If you want to add mining for the player, either enable the debug draw mode or add a miner prefab to the scene. If you want to add your own mining system, take a look at the 2 demo mining/building systems built in to guide you how it works. More on that in the FAQ section.
- 4 of the 6 scripts in this package are completely optional (DemoMovment, DemoUI, DemoMiner, and ShowcaseCamera) and are just here to make the showcase look a little better and show off the system's abilities. So you can feel free to delete those if you don't want them.
Since the map generator script and marching square algorithm within it is a little complex, I'm including a short step by step run down on how the script works:
- First step is to generate a 2d integer array called map. Each element of the map array (map[x,y]) is either equal to 0 or 1. 0 is air, 1 is solid (wall). We Generate the map array like so:
- Fill the map according to the map generator's map method, width, height. It is either filled randomly, based on a mesh, manually, or from a file.
- Add the borders to all sides based on the map generator's border size property.
- Smooth the map. this makes it look less random. I suggest not using this if you want to use manual or file based generation.
- Setup auto chunk if set to do so. More on that in the settings page.
- Next step is to generate the mesh based on the map we just found. If you have no idea how marching squares work I suggest googling a video if you want to fully understand how it works. A quick rundown is as follows:
- Using this line:
squareGrid = new SquareGrid(map, squareSize);
- Using this line:
- Finally, the mesh is generated and can now be updated. When UpdateMesh is called with a world point, it checks if that point is within the bounds of the map, translates the position to local, and translates that local point to a grid position from (-width/2, -height/2) to (width/2, height/2).
- We then set the map spot and other map spots around it in a given area to either 1 or 0 depending on if the updatemesh was called to remove or add walls. You can add more brush shapes if you'd like.
- Now that we've updated the map, we save and regenerate it.
-
Map Method - enum:
This decides how the map (2D integer array) will be generated. random: uses the width, height, and fill percent to randomly fill the area with walls. mesh: makes an area based on a mesh. manual: use a pre existing map. file: load from a file which has a saved map.
-
Width - int:
The width (x) of the map in squares.
-
Height - int:
The height (y) of the map in squares.
-
Square size - float:
The size of each square in meters (unity units). So you can find how big your mesh is going to be by doing (width * square size, height * square size).
-
Border Size - int:
The size of the border around the mesh in squares. If you are using manual or file, you should probably set this to 0 as it will save the border as well so no need to add more border every time.
-
Auto Chunk - bool:
Auto chunking is a powerful tool which can take a huge map and break it down into chunks (think terraria or minecraft). I suggest having less than 10k squares in each chunk/map to keep the verticies and triangles low so generating is easier. You can find about how many squares you will have by doing width * height. If there are a lot of squares (and you haven't disabled warnings/errors), there will be warning or error based on the number of squares. Auto chunking itself works by going through the map and creating more map generator prefabs as children of this generator which each have a chunk of the parent map. They then all call their own generate mesh function and the original parent map does not call a generate mesh function. This massively improves performance.
-
Auto Chunk Width - int:
The width of each chunk created through auto chunk. Make sure auto chunk width * auto chunk height isn't too high or there will be the same issue as not auto chunking a large map. Default is 64.
-
Auto Chunk height - int:
The height of each chunk created through auto chunk. Make sure auto chunk width * auto chunk height isn't too high or there will be the same issue as not auto chunking a large map. Default is 64.
-
SmoothLevel - int:
The higher the smooth level, the more smooth the map will be. Play with it and find what is good for your system. A random map with no smoothing will be complete chaos but if you smooth it say 3-5 times, it will start to look like a cave system
-
Fill Percent - int:
When using random fill method, fill percent decides how much is filled on the the initial map generation (before borders or smoothing).
-
Use Random Seed - bool:
If true, random map generation will choose a random seed based on time. If false, we use a seed.
-
Seed - string:
The seed used if Use Random seed is false
-
Manual Map - List:
If using manual map generation, this determines the map. This should be basically exclusively used to copy and use a mesh generation or just a random generation you liked. Simply right click "Manual Map" in the inspector for the map generator you want to save and click copy, stop the play mode, and then right click the "Manual Map" property on any map generator and paste the map you copied. Run it and it will be the same map.
-
Save Map To Computer - bool:
If true, the map will be saved to your computer and any map that is set to file map generation method will load it from the save file. With this extremely simple system, you can only save one map and chunked maps wont save but you can change this if you want. This is just here to show off how it is possible.
-
Use 3D Collision - bool:
If true, a mesh collider will be used for the map's collision. This works with 3d colliders for players and objects like mesh colliders. Otherwise a polygon collider will be used which works for players and objects with 2D colliders. The map generator will remove and create the correct components if you do not based on this bool.
-
Poly Collider 2D - PolygonCollider2D:
The 2D collider. don't worry if this is not set (although you can set it if you are using 2D collision and would like to), it will set it itself if use 3D collision is false.
-
Mesh Collider 3D - MeshCollider:
The 3D mesh collider. don't worry if this is not set (although you can set it if you are using 3D collision and would like to), it will set it itself if use 3D collision is true.
-
Reference Holder - PrefabReference:
Because the autochunk method creates instances of the map gen prefab and the map gen prefab is the thing holding the map generator script, unity gets confused if you just store the prefab as a game object like you normally would so I did this to get around that. It just holds the map gen prefab reference.
-
Use Demo Controls - bool:
if true, free brush is enabled and you can regenerate the map with the R key.
-
Debug Brush Size - int:
if use demo controls is true, this controls the starting size of the free brush.
-
Debug Mode - enum:
Sets the level of debug mode. mesh mode shows debug cubes for the mesh nodes (gray being active, white being inactive) and map mode shows the map building step.
-
Draw Raycasts - bool:
If true, draw raycasts when we are generating a mesh fill map.
-
Disable Errors And Warnings - bool:
I highly suggest keeping this false but if you want to you can disable the warnings and errors I've made.
-
Debug Brush Type - enum:
If you make more brush types, you can change the demo brush type to the ones you've made if you want.
-
How to make a map?
Simple drag a map gen prefab object into your scene. The prefab is found at Simple2DMining/Prefabs/mapGen
-
I used the manual or file map generation methods but it looks different than it did when I copied/saved it?
You probably just need to set the smooth level and border size to 0. Remember, the border and smoothness are saved in both the save file and the manual map variable that you copied so if you generate a random map for example with a smoothness of 3 and a border size of 5, if you copy or save this, when you generate a new file or manual map, you should set the smoothness and bordersize to 0. If it is still wrong, there might be some weird width/height or square size difference that could be messing with it to look different but I haven't ran into that issue and it shouldn't since the map is created in those cases completely based on the size of the saved/manual map and changing squaresize should just scale it up. Let me know if you are still running into that problem.
-
How can I change the save method to save more maps?
The method I put in this package is extremely simple and can only save one map. To save more you can do a lot of different things including saving each map with an ID number or it's position so you can differentiate which one you're saving to and load many at once. I would suggest you do this logic on a new script instead of trying to edit the MapGenerator script. Then you can just change the save/load logic and it will all work.
-
How do I add a brush shape?
- Head over to line 13 of MapGenerator.cs and add your new shape name to the MineShape enum like so:
public enum MineShape { square yourshape yourshape2 }
- Head down to the switch statement on line 222 and add a new case like so:
switch (shape) { case MineShape.square: // My square logic between line 227 and 241 break; case MineShape.yourshape: // do all your logic... break; case MineShape.yourshape2: // do all your logic... break; }
- Whenever you want to use your new shape, call the UpdateMesh function like so
YourMapGenerator.UpdateMesh(worldPositionOfMouse, whateverSize, remove, MineShape.yourshape);
-
How can I use my own mining/placing system instead of the demo miner?
You'll have to do a little scripting for this. To update a mesh you simply call this function with the world point you wish to update (not the grid), the size of the area to change, and if you are removing wall or adding it. If you only have one map generator, it is extremely simple. You just call something like this:
// removing walls YourMapGenerator.UpdateMesh(worldPositionOfMouse, whateverSize, true); // adding walls YourMapGenerator.UpdateMesh(worldPositionOfMouse, whateverSize, false);
More complex examples of how to go about this that work with multiple map generators are found in the Update function of MapGenerator.cs starting at line 182:
if (!Input.GetMouseButton(0) && !Input.GetMouseButton(1)) { lastWorldPos = null; return; } Vector3 mousePos = Input.mousePosition; mousePos.z = -Camera.main.transform.position.z; Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos); if (!lastWorldPos.HasValue || Vector3.Distance(worldPos, lastWorldPos.Value) >= squareSize) { UpdateMesh(worldPos, debugBrushSize, Input.GetMouseButton(0)); if (lastWorldPos.HasValue && Vector3.Distance(worldPos, lastWorldPos.Value) > squareSize * debugBrushSize) { UpdateMesh((lastWorldPos.Value + worldPos) / 2, debugBrushSize, Input.GetMouseButton(0)); } lastWorldPos = worldPos; }
as well as starting at line 16 in demoMiner.cs:
void Update() { if (!Input.GetMouseButton(0) && !Input.GetMouseButton(1)) { lastWorldPos = null; return; } Vector3 mousePos = Input.mousePosition; Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos); worldPos.z = transform.position.z; // Do nothing if it is out of the miner's range if (Vector3.Distance(worldPos, transform.position) > range) return; // Don't constantly call to update the same square (this is what can cause the auto chunk issue but it heavily saves on performance). if (!lastWorldPos.HasValue || Vector3.Distance(worldPos, lastWorldPos.Value) >= currentMap.squareSize) { // Since this is demo controls, it just goes through all the maps/chunks in the world. I would not do this at scale. // You could instead store which map the player is standing on and its close neighbors and then only check those for example. // If you need help doing this, reach out to me. foreach (var map in FindObjectsByType<MapGenerator>(FindObjectsSortMode.None)) { // We check it like this instead of using bounds to work with edge cases. if (!map.autoChunk && worldPos.x + mineSize / 2 * map.squareSize >= map.transform.position.x - map.width / 2 * map.squareSize && worldPos.x - mineSize / 2 * map.squareSize <= map.transform.position.x + map.width / 2 * map.squareSize && worldPos.y + mineSize / 2 * map.squareSize >= map.transform.position.y - map.height / 2 * map.squareSize && worldPos.y - mineSize / 2 * map.squareSize <= map.transform.position.y + map.height / 2 * map.squareSize) { if (map.UpdateMesh(worldPos, mineSize, Input.GetMouseButton(0), shape)) currentMap = map; } } lastWorldPos = worldPos; } }
-
I'm laggginnggg??
There are 3 things that can sometimes cause lag with this system.
- You have a map with too many squares. Do Width x Height to find about how many squares you will have. I suggest limiting each map to 10k squares if you are updating the mesh often and limit it to 20k-30k squares if it is a static. Do this by making multiple map generator prefabs and putting them next to each other or you can use the auto chunk function (experiemental but looking good so far). At above around 75k squares you get such bad lag generating that you might get corruption and it will fail to generate the mesh correctly.
- You are constantly updating unneccessarily. Updating a mesh means regenerating the whole map and map with all the verticies and triangles. You should try to limit how many times you are doing that. The last World Pos logic you can see on the demo controls and miner controls shows how you can make sure it isn't calling update mesh every frame.
- You have many many maps and are looping through all of them to figure out which map is being updated by your miner. This is how the demo miner does it so I don't blame you but if you have a lot of maps you should change your mining system to only check maps close enough for the miner to update.
Unity Asset Store Package GitHub Online Readme
If there is a suggestion or bug you'd like to report or you need help with something related to this package, you can reach out to me either through email: marasciagames@gmail.com or using the unity asset store page.