Hi, I am working on a game and part of the game requires random 2D shape generation. The game is completely 2D. I would like to be able to change the border and fill of the shape, but that should be quite simple to work out. Also, is it possible to add a specific script to each random 2D shape? Thank you. -8Development
Hey there, what kind of shapes would you like to generate? You may want to check out unity procedural mesh generation. Thing is you can't really go for "drawing" shapes, pixel by pixel. Depends on what you need, but that may be quite expensive performance-wise. So you'll need to generate a mesh and use a Sprite shader on it (this will control your fill color). Then, as you say your game is 2D and the mesh is technically a 3D object, you're gonna need to be able to change its layer/sorting order. Here's a script for that : (The Editor Script. Place it in a folder named "Editor") Code (CSharp): using UnityEngine; using UnityEditor; using UnityEditorInternal; using System; using System.Reflection; using System.Collections; [CustomEditor(typeof(SortingLayers3D))] public class SortingLayers3DEditor : Editor { string[] options = new string[]{}; void OnEnable(){ options = GetSortingLayerNames(); } public override void OnInspectorGUI() { base.OnInspectorGUI(); SortingLayers3D S = (SortingLayers3D)target; Transform T = (Transform)S.transform; EditorGUILayout.BeginHorizontal(); S.index = EditorGUILayout.Popup("Sorting Layer", S.index, options, EditorStyles.popup); EditorGUILayout.EndHorizontal(); S.order = EditorGUILayout.IntField("Order in Layer", S.order); //Debug.Log(T.renderer.sortingLayerName); T.renderer.sortingLayerName = options[S.index]; T.renderer.sortingOrder = S.order; } // Get the sorting layer names public string[] GetSortingLayerNames() { Type internalEditorUtilityType = typeof(InternalEditorUtility); PropertyInfo sortingLayersProperty = internalEditorUtilityType.GetProperty("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic); return (string[])sortingLayersProperty.GetValue(null, new object[0]); } // Get the unique sorting layer IDs public int[] GetSortingLayerUniqueIDs() { Type internalEditorUtilityType = typeof(InternalEditorUtility); PropertyInfo sortingLayerUniqueIDsProperty = internalEditorUtilityType.GetProperty("sortingLayerUniqueIDs", BindingFlags.Static | BindingFlags.NonPublic); return (int[])sortingLayerUniqueIDsProperty.GetValue(null, new object[0]); } } The actual script (Place it in a normal folder. You'll attach it to the 3D object you want to treat as a sprite) Code (CSharp): using UnityEngine; using System; using System.Reflection; using System.Collections; public class SortingLayers3D : MonoBehaviour{ [HideInInspector] public int order = 0; [HideInInspector] public int index = 0; } As for the border, you'll either need a shader or if you shape is convex you can duplicate it, place it one layer behind and assign it a different color (black for instance). And sure, it is possible to assign it a script at runtime. Once you have your GameObject created, use yourGameObject.AddComponent<YourScriptNameHere>(). Code (CSharp): void Start(){ //This will create an empty GameObject with the name "GO_Generated_In_Code" GameObject newGameObject = new GameObject("GO_Generated_In_Code"); //This will attach the script named "YourScriptName" to your new GameObject YourScriptName script = newGameObject.AddComponent<YourScriptName>(); }
I'm looking to actually create random polygons. Oh, and thank you for your detailed response I really appreciate it!
http://wiki.unity3d.com/index.php/ProceduralPrimitives Here i wrote a quick script which would create a new GameObject, generate a 2D plane procedurally and attach a script to it : Code (CSharp): using UnityEngine; using System.Collections; public class PlaneGen : MonoBehaviour { public Material spriteMaterial; void Start(){ //Create a new GameObject named "Spawned GameObject (Plane)" GameObject spawnedGameObject = new GameObject("Spawned GameObject (Plane)"); //Create a 2D plane procedurally on our new Object GeneratePlane(spawnedGameObject); } void GeneratePlane (GameObject spawnedGameObject) { // You can change that line to provide another MeshFilter MeshFilter filter = spawnedGameObject.AddComponent< MeshFilter >(); Mesh mesh = filter.mesh; mesh.Clear(); float length = 1f; float width = 1f; int resX = 2; // 2 minimum int resY = 2; #region Vertices Vector3[] vertices = new Vector3[ resX * resY ]; for(int y = 0; y < resY; y++) { // [ -length / 2, length / 2 ] float yPos = ((float)y / (resY - 1) - .5f) * length; for(int x = 0; x < resX; x++) { // [ -width / 2, width / 2 ] float xPos = ((float)x / (resX - 1) - .5f) * width; vertices[ x + y * resX ] = new Vector3( xPos, yPos, 0.0f ); } } #endregion #region Normales Vector3[] normales = new Vector3[ vertices.Length ]; for( int n = 0; n < normales.Length; n++ ) normales[n] = -Vector3.forward; #endregion #region UVs Vector2[] uvs = new Vector2[ vertices.Length ]; for(int v = 0; v < resY; v++) { for(int u = 0; u < resX; u++) { uvs[ u + v * resX ] = new Vector2( (float)u / (resX - 1), (float)v / (resY - 1) ); } } #endregion #region Triangles int nbFaces = (resX - 1) * (resY - 1); int[] triangles = new int[ nbFaces * 6 ]; int t = 0; for(int face = 0; face < nbFaces; face++ ) { // Retrieve lower left corner from face ind int i = face % (resX - 1) + (face / (resY - 1) * resX); triangles[t++] = i + resX; triangles[t++] = i + 1; triangles[t++] = i; triangles[t++] = i + resX; triangles[t++] = i + resX + 1; triangles[t++] = i + 1; } #endregion mesh.vertices = vertices; mesh.normals = normales; mesh.uv = uvs; mesh.triangles = triangles; mesh.RecalculateBounds(); mesh.Optimize(); //Attach material MeshRenderer rend = spawnedGameObject.AddComponent<MeshRenderer>(); if(spriteMaterial){ rend.material = spriteMaterial; } } } Create a new C# script named PlaneGen, copy paste this in, drag this script to a GameObject (it could be anything, let's say the Main Camera), if you want add a material to the spriteMaterial variable and hit Play. You should see a small plane generated.
Ah, great then! It means this is what you're after... This is the core principle, however depending on how random your polygons will be, things may get trickier for triangulation. If you have a weird concave blob generated randomly, you'll also need to figure out a way to make the triangles properly...
Thank you. This is interesting. So, does that mean that I could just create a random amount of triangles using Random.Range, and set that? That would create a random shape, right?
I tired that, and it doesn't seem to work. Now, how do I make it a randomly generated shape? I tried to create a Random.Range(3, 36). How would you do this? Thanks for your help.
Unfortunatelly, not exactly. You can tweak these variables Code (CSharp): int resX = 2; // 2 minimum int resY = 2; to add more divisions to the plane. However, if you start to change the positions of the vertices around, the shape may not look as you want. Say you have these random dots (these would be the vertices of your polygon) : There are mutliple ways to connect them... The way you arrange the connections between dots is determined by the triangles array. It is basically an array of indexes for each triangle. It is saying which vertex in the vertices array should be assigned in that triangle. Again, unfortunately it is a more complex subject and it depends exactly on how random your shapes will be. The worst will be triangulation. You can't just define the points on the border like when you draw a path in Photoshop for instance
at line 40 you can change this line vertices[ x + y * resX ]=new Vector3( xPos, yPos, 0.0f ); to this : vertices[ x + y * resX ]=new Vector3( xPos + Random.Range(-0.5f, 0.5f), yPos + Random.Range(-0.5f, 0.5f), 0.0f ); if you want to slightly deform the plane and get a feel for it. But it is probably not what you need. Again, it depends very much on what you need. If the shape is truly random, then it is not easy at all...
If your shape is always convex* then it's easy as you don't have to deal with triangulation. http://www.rustycode.com/tutorials/convex/concavevsconvex2d.jpg Depends on what you actually need... if they really have to be 100% random Edit: I meant convex, not concave
There are essentially an infinitely variable number of ways to procedurally generate mesh geometry. I suggest you try to understand the essential components in the script that @Ted Chirvasiu posted above, which is a fine example of simple-to-understand code. When you understand what it does, you can begin to modify it in order to experiment and learn how procedural geometry creation works, what the gotchas are, what the easy problems are, what the hard problems are, etc.
Okay. I think I know what to do. Due to this being only a prototype, I won't bother with this. I have an old Java program which creates shapes as PNGs. I can just run that and get some prototype sprites. Thanks for the help guys. In the real game, I will try this.