Search Unity

help with dynamically creating a wall

Discussion in 'Scripting' started by hellaeon, Sep 5, 2011.

  1. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Hi all,

    Just wondering on the maths or programming approach to creating a dynamic 'wall' (lets worry about x,y only to simplify)

    The steps to create our dimensions for the wall would be that the user can click on the screen - drag the mouse to a new position - and a 'wall' is created. This can be done with the line renderer but ultimately I would prefer to use a wall of sorts, so I can use a collider (extra problem perhaps?) to then utilize all the collision functions for later purposes.

    I have tried a few experiments - scaling an object, creating objects at the mouse position etc, even mesh creation, but cant seem to imagine where to go next.

    For a mesh - My stumbling block appears to be in calculating the coordinates
    For a prefab - I dont know how I can click down on mouse to create a new wall 'hold' onto the end of a small wall and 'drag' it to another position and let mouse go to almost 'draw' the wall.

    I like the mesh idea because in small steps I could
    1) create the mesh
    2) create the mesh at the mouse position
    3) create the mesh at the mouse position and control certain coordinates while mouse down, to then 'drag and drop' the wall end point.

    In fact, just typing that out gives me an idea on how I can achieve it in a way with step 3 haha....

    Thoughts?

    Cheers
    Matt
     
  2. Afisicos

    Afisicos

    Joined:
    Nov 13, 2010
    Posts:
    326
    I think this method:
    You can Instantiate prefab walls with a fix size.
    First, check the initial position of the click and then check the final position when you release the click.
    Substract the first position to the final position to create the director vector.
    Then check the vector's magnitude and divide it by wall's x size to get the number of walls needed
    Create a new coordinated reference system from the initial position whit its x or z axis in the director vector direction.
    Last, instantiate a wall using the number of walls obatained and the wall's size.
     
  3. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    I don't know if you will get an answer worthy of action without explaining a lot more about your game. For example...
    • Will this be in 3D?
    • Is there a brick-like texture which can't stretch or a procedural texture that doesn't rely on UVs?
    • Do you want to click an open end on this wall then drag out the new wall with a visual update of some kind? Does it have to be the wall or will a solid colored icon work? Is it possible to drag it out and fail a drop-point check, meaning are there plac
    es where it can't be droped? Should it snap in to place? etc..
     
  4. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Thanks Rafes, fair questions.

    * The game is orthographic, top down - staring into Z.
    * Essentially a swipe on the screen will draw the wall at a later stage, each swipe removing the previous wall and creating a new one from swipe start to swipe end.
    * I dont have any critical textures for it. I can apply that later but I would probably just start with a straight material colour as you are viewing it top down.
    * The wall is thin, with depth to cater for the Z axis and colliders which will come into play to block objects.
    * The viewing area is to be fairly small, no scrolling, just the same view. So on screen is the entire playing area.
    * Im not worried about snapping at this stage, and likely dont want it.

    During some more testing, I initially thought I could 'attach' one side of the wall (is like a peg initially size:1,1,10), the attached side rotating and dragging the vertex points as I moved the mouse around till I released the mouse. I am drawing the 'peg' by creating an empty game object and adding the mesh. This way I can control the required vertices - once I work it out :)

    Cheers

    Cheers
     
  5. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    Another possibility would be to skin a wall in a 3D package, like Maya (skin binding). Just use a flat heirarchy of two joints placed exactly at the edge of the mesh and skin the wall to them. Then in Unity you can put down one joint and drag the other.

    Probably overkill, but just through I would share. I'd say a line renderer is a great choice though. Very light and easy to texture.
     
  6. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    im probably thinking way too hard about this.

    Does the line renderer render 'depth' ?
    I might have a quick look at that object.

    This is what I have so far - creating a box at the mouse position that rotates, but I cant figure out how to 'drag' the right edge to wherever the mouse goes, this is where my maths is failing me. Its the code if the 'else' block. I think initially I should rotate the 'wall' game object to be facing down the Z as well, its probably making my calculations goes extra screwed.

    Thoughts?

    I will investigate the line renderer.

    Cheers
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Player : MonoBehaviour {
    6.    
    7.     // wall material
    8.     public  Material    wall_material;
    9.     public  GameObject  camera;         // main camera
    10.     private GameObject  wall;           // the player wall, drawn by the player on screen (turn into array to support more then 1 line)
    11.     private Mesh        mesh;           // the mesh we create and attach
    12.    
    13.     #region wall vertex stuff
    14.    
    15.     // builds our 'wall'
    16.     private Vector3[] vertices = new Vector3[]
    17.     {// Front face  
    18.         new Vector3(-1.0f, -1.0f,  1.0f),  
    19.         new Vector3( 1.0f, -1.0f,  1.0f),  
    20.         new Vector3( 1.0f,  1.0f,  1.0f),  
    21.         new Vector3(-1.0f,  1.0f,  1.0f),  
    22.          
    23.         // Back face  
    24.         new Vector3(-1.0f, -1.0f, -1.0f),  
    25.         new Vector3(-1.0f,  1.0f, -1.0f),  
    26.         new Vector3( 1.0f,  1.0f, -1.0f),  
    27.         new Vector3( 1.0f, -1.0f, -1.0f),  
    28.          
    29.         // Top face  
    30.         new Vector3(-1.0f,  1.0f, -1.0f),  
    31.         new Vector3(-1.0f,  1.0f,  1.0f),  
    32.         new Vector3( 1.0f,  1.0f,  1.0f),  
    33.         new Vector3( 1.0f,  1.0f, -1.0f),  
    34.          
    35.         // Bottom face  
    36.         new Vector3(-1.0f, -1.0f, -1.0f),  
    37.         new Vector3( 1.0f, -1.0f, -1.0f),  
    38.         new Vector3( 1.0f, -1.0f,  1.0f),  
    39.         new Vector3(-1.0f, -1.0f,  1.0f),  
    40.          
    41.         // Right face  
    42.         new Vector3( 1.0f, -1.0f, -1.0f),  // 16
    43.         new Vector3( 1.0f,  1.0f, -1.0f),  // 17
    44.         new Vector3( 1.0f,  1.0f,  1.0f),  // 18
    45.         new Vector3( 1.0f, -1.0f,  1.0f),  // 19
    46.          
    47.         // Left face  
    48.         new Vector3(-1.0f, -1.0f, -1.0f),  
    49.         new Vector3(-1.0f, -1.0f,  1.0f),  
    50.         new Vector3(-1.0f,  1.0f,  1.0f),  
    51.         new Vector3(-1.0f,  1.0f, -1.0f)  
    52.     };  
    53.    
    54.     // these are the original vertices for the right side
    55.     private Vector3[] tempVertices = new Vector3[]
    56.     {
    57.         new Vector3( 1.0f, -1.0f, -1.0f),  
    58.         new Vector3( 1.0f,  1.0f, -1.0f),  
    59.         new Vector3( 1.0f,  1.0f,  1.0f),  
    60.         new Vector3( 1.0f, -1.0f,  1.0f),  
    61.     };
    62.    
    63.     private int[] triangles =
    64.     {
    65.         0,  1,  2,      0,  2,  3,    // front  
    66.         4,  5,  6,      4,  6,  7,    // back  
    67.         8,  9,  10,     8,  10, 11,   // top  
    68.         12, 13, 14,     12, 14, 15,   // bottom  
    69.         16, 17, 18,     16, 18, 19,   // right  
    70.         20, 21, 22,     20, 22, 23    // left
    71.     };
    72.  
    73.     private bool mesh_created = false;
    74.    
    75. #endregion
    76.    
    77.     // Use this for initialization
    78.     void Start ()
    79.     {
    80.    
    81.     }
    82.    
    83.     // Update is called once per frame
    84.     void Update ()
    85.     {
    86.         // create a new object, first get mouse position:
    87.         Vector3 mpos = Input.mousePosition;
    88.         mpos.z = Mathf.Abs(camera.transform.position.z);
    89.         Vector3 spos = camera.GetComponent<Camera>().ScreenToWorldPoint(mpos);
    90.    
    91.         if (Input.GetMouseButton(0))
    92.         {
    93.             if (!mesh_created)
    94.             {  
    95.                 // instantiate wall with required mesh stuff
    96.                 wall =  new GameObject();
    97.                 wall.transform.position = spos;
    98.                 wall.transform.rotation = Quaternion.identity;
    99.                 //wall.transform.localScale = new Vector3(1.0f,0.1f,0.1f);
    100.                 wall.AddComponent<MeshFilter>();
    101.                 wall.AddComponent<MeshRenderer>();
    102.                
    103.                 // lets create a new mesh for the game object
    104.                 mesh = new Mesh();
    105.                 wall.GetComponent<MeshFilter>().mesh = mesh;
    106.                 mesh.vertices = vertices;
    107.                 Vector2[] uvs = new Vector2[vertices.Length];
    108.                
    109.                 int i = 0;
    110.                 while (i < uvs.Length)
    111.                 {
    112.                     uvs[i] = new Vector2(vertices[i].x, vertices[i].z);
    113.                     i++;
    114.                 }
    115.                
    116.                 // create uvs, triangles used and the normals!
    117.                 mesh.uv = uvs;
    118.                 mesh.triangles = triangles;
    119.                 mesh.RecalculateNormals();
    120.                
    121.                 // set material
    122.                 wall.transform.renderer.material = wall_material;
    123.                 mesh_created = true;
    124.             }
    125.             else
    126.             {
    127.                 // rotate with mouse
    128.                 wall.transform.LookAt(spos);
    129.                
    130.                 // lets manipulate the vertices...
    131.                 spos.z = 0;
    132.                 vertices[12] = spos - tempVertices[0];
    133.                 vertices[13] = spos - tempVertices[1];
    134.                 vertices[14] = spos - tempVertices[2];
    135.                 vertices[15] = spos - tempVertices[3];
    136.                
    137.                 mesh.vertices = vertices;
    138.             }
    139.         }
    140.        
    141.         if (Input.GetKeyUp(KeyCode.Space))
    142.         {
    143.             Destroy(wall);
    144.         }
    145.        
    146.     }
    147. }
    148.  
     
  7. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Looks like the line renderer will not do what I was hoping, I really need it to be a 'thick' object, how thick can vary, but ideally I can finish off the script in the else block....

    thoughts anyone?
     
  8. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    Then the game isn't orthographic and top-down. It is top-down but "perspective". An orthographic camera would only see the top of the wall if looking straight down. I just wanted to point out this vocab for communications-sake. Sorry i don't have time to dig in to code at the moment. I'm trying to get something released and I got distracted ;)

    On the plus side, the thing I'm trying to release is a constraint package for Unity and it will be free. If you can get your mesh doing what you want, you can use our look-at constraint to make the center look at the mouse position. It isn't complex code but it is very convenient.
     
  9. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Thanks for the help Rafes,

    The Camera is set to orthographic, starting down the Z, so haha not technically 'top down', but this is probably where I am making things awkward.

    in a nutshell, that 'wall' you can draw should be able to be collided with, hence the idea that I need some kind of depth to it to account for a collider and all the goodness that comes with one.

    2 ways to approach:
    1) the current way, I am drawing the wall 'live' so to speak
    2) plot the line initially, (mouse down, drag, mouse up) then draw in the box.

    My head keeps telling me 2 would be easier, but 1 is much nicer, though 2 might help me understand where I am going wrong.

    Thoughts?

    Cheers
     
  10. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    A box collider does not need a mesh to work, you can have a line with no depth and still have a box collider on the wall. You just have to set it's size, etc, once you have a set wall, unless you need it to collide while drawing of course. You only need the mesh to render stuff you can actually see.
     
  11. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Rafes: Of course! I cant believe I forgot about that, I have done it previously when mucking about.

    so :
    1) Create GameObject,
    2) Attach Line Renderer, try for 'live' draw option
    3) Attach collider to Line Renderer
    4) Increase Depth of collider as per requirements.

    Mate, will give it a try later and let you know how it goes, thank you so much for the help so far!
     
  12. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    One gotcha in this idea is that a box collider is not on the line renderer, it is on the GameObject with the line renderer component. You will need to orient the GameObject to match the line renderer to get the box to align before you can begin to set the size to match. I'm not sure if you can do this post "drop" or if you need to get the game object to point at the pointer in Y (or whatever) only.
     
  13. Schnelle

    Schnelle

    Joined:
    Jan 17, 2011
    Posts:
    89
    You could try to generate the mesh yourself and attach colliders, but if I were in your position I'd look at UniTile in the asset store. It's a tile editor that generates meshes for use in a top down or side scrolling game and automatically attaches colliders.

    Sometimes having the source code that someone else wrote will help: http://www.mudloop.com/unitile

    I'm not affiliated with UniTile, but I bought it.
     
  14. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Hi all see attached:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Player : MonoBehaviour {
    6.    
    7.     // wall material
    8.     public  Material    wall_material;
    9.     public  GameObject  camera;         // main camera
    10.     private GameObject  wall;           // the player wall, drawn by the player on screen (turn into array to support more then 1 line)
    11.     private LineRenderer lineRenderer;
    12.     private bool        wall_created = true;
    13.        
    14.     // Use this for initialization
    15.     void Start ()
    16.     {
    17.    
    18.     }
    19.    
    20.     // Update is called once per frame
    21.     void Update ()
    22.     {
    23.         // create a new object, first get mouse position:
    24.         Vector3 mpos = Input.mousePosition;
    25.         mpos.z = Mathf.Abs(camera.transform.position.z);
    26.         Vector3 spos = camera.GetComponent<Camera>().ScreenToWorldPoint(mpos);
    27.         spos.y = 0; // reset our Y
    28.        
    29.         if (Input.GetMouseButtonDown(0))
    30.         {
    31.             if (wall_created)
    32.             {  
    33.                 // kill it first
    34.                 Destroy(wall);
    35.                
    36.                 // instantiate wall
    37.                 wall =  new GameObject();
    38.                 wall.transform.position = spos;
    39.                 wall.transform.rotation = Quaternion.identity;
    40.                
    41.                 // the line renderer!
    42.                 lineRenderer = wall.AddComponent<LineRenderer>();
    43.                 lineRenderer.SetPosition(0,spos);
    44.                 lineRenderer.SetWidth(0.1f,0.1f);
    45.                
    46.                 // add a material to the game object
    47.                 wall.renderer.material = wall_material;
    48.                
    49.                 // we have not finished creating it until the mouse button is up....
    50.                 wall_created = false;
    51.             }
    52.         }
    53.        
    54.         if (Input.GetMouseButton(0))
    55.             if (wall) lineRenderer.SetPosition(1,spos);
    56.            
    57.         if (Input.GetMouseButtonUp(0))
    58.             wall_created = true;
    59.     }
    60. }
    61.  
    62.  
    Have ended up using the line renderer, but I also moved the camera to correctly stare down the Y for proper top down :)

    In saying that I have been briefly playing with the colliders to find my best option, which appears to be a mesh collider.
    Ideally I want to do from now is attach the collider with some depth in the Y.

    In my perfect 'it just worked this way' world I would want to:

    * Get the line renderer mesh coordinates in world space (or perhaps the actual mesh itself?)
    * Create a mesh (or use the line renderer mesh)
    * manipulate the mesh to give it the slight depth and extra width
    * attach the mesh collider to the line renderer game object
    * attach the new padded mesh I created to the mesh collider

    If anyone can point me in a less painful direction that would be great, otherwise I will investigate a few options including the above.

    Cheers
     
  15. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    Mesh colliders are the slowest and your only mesh is the line renderer, which is all you need to render your game (it sounds like), so making more mesh just to use a slow collider only makes sense if you need a really complex shape. I would think a wall is square though, which means a cube collider (the second-fastest) would be perfect...?
     
  16. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Hey Mate,

    I agree with you for sure except the box collider does not align itself around the plane nicely, it adds a heap of extra space along z and x when the line is drawn on an angle - and no y. Give it a try and attach a box collider, you'll see my problem.

    In saying that, the idea of getting the coordinates and making my own mesh is indeed to create my own box, but by supplying the actual coordinates of a simple box mesh, which then acts as the collider is so I dont have to try and manipulate any other collider to fit (change angles, use a heap of maths to turn it around and move it in to place)

    I dont think its going to be a massive issue as the collider mesh I make is pretty much a box collider.

    Actually - haha just talking about this gives me an idea - given I have the start and end coordinates of the line:

    1) get new points using cross product of start and end coordinates to create points x distance away from ends (at a right angle) I would need to create 6 extra points (3 on each 'end'), which I think I can do using the cross product as it always gets the point at the right angle...This creates the 'edge' quads.

    2) then create the 6 quads needed for the box collider
    3) attach as the mesh
    4) hopefully drink a beer with a smile.

    Thoughts?
     
    Last edited: Sep 9, 2011
  17. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    Any mesh collider will be slow. It shouldn't matter if you make a snake or a box since it will have to process the faces (I'm assuming). I'm not sure I understand why you can't use a box. A box can be sized and oriented anyway you want.
    - Find the center-point between the two end-points for the position of the game object.
    - Then rotate the game object to point at one of the end points.
    - The size of the box collider should be the distance between the two points (maybe divided by 2). The width would be based on the line width and the height is irrelevant except for working with the physics stuff.

    I think this should work?
     
  18. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Thanks Rafes,

    I will give this a try tonight, I think thats a sound idea. Much easier then my method!
    I might have scared myself away from the box collider idea though due to orientation issues.

    Im not able to check right now, but I believe the collider orientation is dependant on the game object orientation.

    With this in mind, I will need to
    * create the game object,
    * get the angle as you mention (angle between two vectors)
    * rotate
    * attach the line.

    Im my mind it presents itself nicely and the box should be immediately around the line 'mesh'
    I can then just increase the collider dimensions - but without testing in front of me haha, I am blowing hot air.

    Thoughts?
     
  19. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    That is how my mind makes it work too. I just hope Unity agrees =D
     
  20. hellaeon

    hellaeon

    Joined:
    Jul 20, 2010
    Posts:
    90
    Damn!

    Unfortunately this result ended up the same as before. Rotating the game object did nothing to change how the box collider creates itself by default.

    However I came to realise I may not need depth on the wall anyway for the moment or indeed if at all.

    As I will be testing it as a blocker for a ball or similar object, that object itself can have the collider depth/breadth/whatever and as long as it collides, even with a flat collider on the wall, thats fine.

    Will do some more tests to check the collision function are entered.

    Cheers
     
  21. Rafes

    Rafes

    Joined:
    Jun 2, 2011
    Posts:
    764
    I have a cube collider on an object which is rotating and it works fine. Weird. You can do it in the Editor manually and it works too.
     
  22. markpollard1

    markpollard1

    Joined:
    Apr 1, 2010
    Posts:
    70
    Did you get this working ? As i am having the same issues creating walls on mouse dragging.