I want to have an expanding/collapsing grid in my game like the one drawn in the editor window. As you zoom in, it proliferates. Problem is I can't think of a way to do it efficiently. I suppose I could create a bunch of line renderers to do it (one for each line of the grid, with a monobehaviour to keep track and control their visibility). But that seems excessive. Do you think a scaling grid like this is within the realm of possibility?
Well, if you have the Pro version, you could do something like this (this draws one big grid): Code (csharp): void DrawGrid() { GL.Color(Color(0, 255, 0)); // Draw a 1x1 grid along the X and Z axis' for(float i = -50; i <= 50; i += 1) { // Start drawing some lines GL.Begin(GL.LINES); // Do the horizontal lines (along the X) GL.Vertex3(-50, 0, i); GL.Vertex3(50, 0, i); // Do the vertical lines (along the Z) GL.Vertex3(i, 0, -50); GL.Vertex3(i, 0, 50); // Stop drawing lines GL.End(); } } Note, this is untested, as I don't have Pro. But the regular C++/OpenGL equivalent does work.
Good to know! Unfortunately, I don't have Pro. Maybe I'll look into SetPixel? Seems like it could be expensive to do every frame.
You wouldn't draw every pixel in the texture every frame, just the lines, which should be very fast. Although the entire texture would have to be uploaded. Another possibility would be using the mesh scripting interface to make a grid out of thin polygons, although you'd have to make sure they still showed up at low resolutions without AA. But it would definitely be fast and it wouldn't need a large uncompressed texture. --Eric
I'll see what I can do with a mesh. I've had problems in the past with thin lines disappearing and flickering (as you say). Hopefully I have the skill. Merci.
Out of curiosity, I checked out SetPixels, and in fact it does get a little slow for uploading a decent-sized texture every frame (SetPixels itself isn't a problem for drawing some lines). For smaller textures it's not a big deal, but then you'd end up with thick lines. Too bad Debug.DrawLine doesn't work outside the editor, eh? --Eric
You can actually get pretty tricky stuff done with the mesh. I have a mesh that uses tiling so that you can have all sorts of nice smooth edged connections and such. It requires a set of 4 vertices for each "square" so there's lots of overlapping vertices, but it's necessary because, afaik, there's no way to set UVs per triangle corner, only per vertex. Anyway, you could make your grid up of square polygons that have either a straight line texture or a cross where they come together (by changing the uvs.) If you really wanted to be sure you could see the lines... dynamic sizing of the mesh would do it for you. You could even make the size of the polygons proportional to the display. Plus, you'll get anti-aliased lines that way. Even with all that calculating the mesh is darn fast. I do a bunch of perlin noise on mine and it works fine on 1ghz g4s.
Yeah, I was going to suggest using a line texture. Although I don't think I would bother with a cross for intersecting lines; I'd just use an alpha texture. It would be a lot simpler just to have 1 quad (2 triangles) for each line. With an alpha texture you'd get a darker spot where the lines intersect (if you're not using pure black), but it looks like that's what happens with the grid in the Unity editor too. --Eric
Good point. It would be far, far, FAR, less geometry that way too. It also opens up the way for shnazzy looking lines, whatever that may mean to you. The big drawback to this method is the difficulty in keeping the size of the lines consistent. I was sort of thinking a 2D grid because that's what my brain is on, but if you want it like the Unity editor, it will be tricky-to-impossible to keep a consistent look with the line thickness. You'd probably have to settle for lines getting thicker as they get closer. Probably not what you want.
I thought the point of this was to not zoom the lines, but create them with the mesh interface? That's what I'd do anyway...they'd stay the same thickness and you'd just move the vertices around. (And "shnazzy looking lines" are a good thing, right? ) --Eric
Yes, the lines would be the same thickness, BUT as you get closer to them with perspective (as when you change the rotation for instance), they would look wider. This could be a good effect, but it's not what happens in 3D editors and their grids, they look about 1 pixel at any distance from the camera, and I thought that's the look that Marble was going for. If, on the other hand, we are talking about a 2D grid that we are always looking at "straight on", this can be handled pretty easily. Shnazzy is good. I'm thinking glowing lines, wavy lines, dotted lines... all sorts of fun stuff can be accomplished easily once you start using custom made meshes. [EDIT: Lots on my mind, not sure I'm being very coherent here today... sort of spilling my brain out onto the pages. Sorry if anything is unclear or for any brain matter stains.]
Just to make things simpler, the grid is 2D, viewed by an orthogonal camera. A more finely tessellated grid needs to appear as one zooms in (and the opposite for out). Assuming I read you guys right, using the mesh interface to draw "lines" or quads for my grid seems to be the best way to get this done. So, I simply (ha) keep the width of my grid lines proportional to the growth in my camera's orthographic size so that it never seems to change. Am I on the right track here?
Yes. That should work great... also if you are using an ortho camera you won't have to worry about resizing the lines because there will be no perspective and they won't look bigger as they get closer or anything. That makes it particularly easy... once you get used to the mesh interface.
Yep! But don't listen to Aaron , if you're changing the camera's orthogonal size, you're right that you'll need to change the size of the lines. Alternately, you could use two cameras and keep one of them stationary for the the grid in the background, in which case you'd move the lines around instead of changing the width. I'm not sure in which case the math would be easier, but either way it shouldn't be too hard. (Famous last words....) --Eric
Thanks to Eric, I was able to create a couple of scripts that do what I needed. It creates one grid that will fade into a larger grid when the orthographic camera is "zoomed out." The system only works with two grids right now; been thinking about letting it support an arbitrary amount but that's for another day. On a couple empty Game Objects- GridManager.js Code (csharp): var gridExtents = 1000.0; var gridSpacing = 2.0; var gridZ = 100.0; var lineWidth = 0.5; var gridMaterial : Material; private var cameraManager : CameraControl; private var XGrid : Vector3[]; private var YGrid : Vector3[]; private var grid : MeshFilter; function Start() { cameraManager = Camera.main.GetComponent(CameraControl); XGrid = new Vector3[(gridExtents / gridSpacing) * 2]; YGrid = new Vector3[(gridExtents / gridSpacing) * 2]; var i = gridExtents; for( var gridEnd : Vector3 in XGrid ) { gridEnd = new Vector3( i * -1.0, gridExtents, gridZ ); i -= gridSpacing; } i = gridExtents; for( var gridEnd : Vector3 in YGrid ) { gridEnd = new Vector3( gridExtents * -1.0, i * -1.0, gridZ ); i -= gridSpacing; } grid = gameObject.AddComponent(MeshFilter); grid.mesh = new Mesh(); Redraw(); gameObject.AddComponent(MeshRenderer); renderer.material = gridMaterial; cameraManager.SetGridColor( renderer.material.color ); } function Redraw() { grid.mesh.Clear(); var modLineWidth = lineWidth * cameraManager.constanceFactor; var vertices = new Vector3[ XGrid.length * 4.0 + YGrid.length * 4.0 ]; var triangles = new int[ vertices.length * 1.5 ]; var v = 0; var t = 0; for( var gridEnd : Vector3 in XGrid ) { vertices[v] = gridEnd - Vector3(modLineWidth / 2.0, 0.0, 0.0); v++; vertices[v] = gridEnd + Vector3(modLineWidth / 2.0, 0.0, 0.0); v++; vertices[v] = gridEnd - Vector3(modLineWidth / 2.0, gridExtents * 2.0, 0.0); v++; vertices[v] = gridEnd + Vector3(modLineWidth / 2.0, gridExtents * -2.0, 0.0); v++; triangles[t] = v - 4; t++; // 1 triangles[t] = v - 3; t++; // 2 triangles[t] = v - 2; t++; // 3 triangles[t] = v - 1; t++; // 4 triangles[t] = v - 2; t++; // 3 triangles[t] = v - 3; t++; // 2 } for( var gridEnd : Vector3 in YGrid ) { vertices[v] = gridEnd + Vector3(0.0, modLineWidth / 2.0, 0.0); v++; vertices[v] = gridEnd - Vector3(0.0, modLineWidth / 2.0, 0.0); v++; vertices[v] = gridEnd + Vector3(gridExtents * 2.0, modLineWidth / 2.0, 0.0); v++; vertices[v] = gridEnd - Vector3(gridExtents * -2.0, modLineWidth / 2.0, 0.0); v++; triangles[t] = v - 2; t++; triangles[t] = v - 3; t++; triangles[t] = v - 4; t++; triangles[t] = v - 3; t++; triangles[t] = v - 2; t++; triangles[t] = v - 1; t++; } var uvs = new Vector2[vertices.length]; for (var i=0;i<uvs.Length;i++) { uvs[i] = Vector2 (vertices[i].x, vertices[i].z); } grid.mesh.vertices = vertices; grid.mesh.triangles = triangles; grid.mesh.uv = uvs; grid.mesh.RecalculateNormals(); } function SetSpacing( spacing : float ) { gridSpacing = spacing; } On the main camera- CameraControl.js Code (csharp): var zoomSpeed = 10; var minZoomDistance = -2.0; var constanceFactor : float; var zoomChange = false; var grid1 : GridManager; private var gridColor : Color; var grid2 : GridManager; var switchToGridTwoAt = 1000.0; private var originalSize : float; function Awake() { originalSize = camera.orthographicSize; } function Update () { if( zoomChange ) { if( camera.orthographicSize < switchToGridTwoAt ) { if( !grid1.gameObject.active ) grid1.gameObject.active = true; grid1.renderer.material.color = gridColor - (gridColor * ( camera.orthographicSize / (switchToGridTwoAt - originalSize) ) ); grid2.renderer.material.color = gridColor - grid1.renderer.material.color; grid1.Redraw(); grid2.Redraw(); } else { if( grid1.gameObject.active ) grid1.gameObject.active = false; grid2.Redraw(); } zoomChange = false; constanceFactor = 1.0; } if( Input.GetAxis("Zoom") ) { camera.orthographicSize -= Input.GetAxis("Zoom") * Time.deltaTime * zoomSpeed; constanceFactor = camera.orthographicSize / originalSize; transform.position.z += Input.GetAxis("Zoom") * Time.deltaTime * zoomSpeed; zoomChange = true; } if( Input.GetAxis("Mouse ScrollWheel") ) { camera.orthographicSize -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomSpeed * 100; constanceFactor = camera.orthographicSize / originalSize; transform.position.z += Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomSpeed; zoomChange = true; } if( transform.position.z > minZoomDistance ) transform.position.z = minZoomDistance; if( camera.orthographicSize < 15.0 ) camera.orthographicSize = 15.0; } function SetGridColor( color : Color ) { gridColor = color; } Thanks to the Unity community! EDIT: Added UVs and normals so shaders won't complain. Thanks Eric for pointing that out (below).
Cool; that works well. I always get messages about the shader wanting texture coordinates (because of no UVs), but I don't know what you're using for a shader. --Eric