Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Better way to do tentacles?

Discussion in 'Scripting' started by Camronas, Dec 4, 2014.

  1. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    This is a script a friend of mine made for me to create tentacles. It looks and works great. It creates its own mesh and manipulates it to make the wavy effect.

    (It says Images are attached so hopefully you can see them)

    The issue is that the draw calls seem to be quite high. Just wondering if that is normal for a tentacle in a game or if there if a better way to be doing this.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class tailScript : MonoBehaviour
    5. {
    6.     public int tailType = 0;
    7.  
    8.     public bool alive = true;
    9.     private Mesh mesh;
    10.     public Material material;
    11.     private float angle;
    12.     public float length;
    13.     private int state;
    14.     public const int segmentCount = 60;
    15.     private float[] segments = new float[segmentCount];
    16.     private float[] targets = new float[segmentCount];
    17.     private float[] frequency = new float[segmentCount];
    18.     private float[] amplitude = new float[segmentCount];
    19.     private float offset=0;
    20.     public int mode=1;
    21.     private int count=0;
    22.  
    23.     // Use this for initialization
    24.     void Start ()
    25.     {
    26.         mesh = new Mesh();
    27.         for(int i=0;i<segmentCount;++i)
    28.         {
    29.             segments[i]=0.0f;
    30.             targets[i]=0.14f;
    31.             frequency[i] = Random.Range(0.1f,1.0f*2);
    32.             amplitude[i] = Random.Range(0.2f,0.4f*2);
    33.         }
    34.         //material = new Material(shader);
    35.     }
    36.     void SetTargetAngle(float angle)
    37.     {
    38.         for(int i=0;i<segmentCount;++i)
    39.         {
    40.             targets[i]=angle;
    41.         }
    42.     }
    43.  
    44.     // Update is called once per frame
    45.     void Update ()
    46.     {  
    47.         if(mode==0)
    48.         {
    49.             for(int i=0;i<segmentCount;++i)
    50.             {
    51.                 float delta = (targets[i] - segments[i]);
    52.                 float turnSpeed = 0.14f * Time.deltaTime;
    53.                 if(delta>0 && delta > turnSpeed)
    54.                 {
    55.                     delta = turnSpeed;
    56.                 }
    57.                 else if(delta<0 && delta < turnSpeed)
    58.                 {
    59.                     delta = turnSpeed;
    60.                 }
    61.                 segments[i]+=delta;
    62.             }
    63.         }
    64.  
    65.         if(mode==1)
    66.         {
    67.             for(int i=0;i<segmentCount;++i)
    68.             {
    69.                 float delta = (targets[i] - segments[i]);
    70.                 float turnSpeed = -0.14f * Time.deltaTime;
    71.                 if(delta>0 && delta > turnSpeed)
    72.                 {
    73.                     delta = turnSpeed;
    74.                 }
    75.                 else if(delta<0 && delta < turnSpeed)
    76.                 {
    77.                     delta = turnSpeed;
    78.                 }
    79.                 segments[i]+=delta;
    80.             }
    81.         }
    82.  
    83.         else if(mode==2)
    84.         {
    85.             if (!alive)
    86.             {
    87.                 Die();
    88.             }
    89.  
    90.             for(int i=0;i<segmentCount;++i)
    91.             {
    92.                 segments[i] = Mathf.Sin(Time.time * frequency[i])*amplitude[i]/2.5f;
    93.             }          
    94.         }
    95.         Vector3[] vertices = new Vector3[segmentCount*2+2];
    96.         Vector3[] normals = new Vector3[segmentCount*2+2];
    97.         Vector2[] uvs = new Vector2[segmentCount*2+2];
    98.         Color[] colours = new Color[segmentCount*2+2];
    99.         int[] indices = new int[segmentCount*6];
    100.      
    101.         Vector3 currentPos = new Vector3(0,0,0);
    102.         float currentAngle = 0;
    103.      
    104.         Vector3 dir=new Vector3(0,0,0);
    105.         Vector3 perp=new Vector3(0,0,0);
    106.         //Debug.Log("Start");
    107.      
    108.      
    109.         Vector3 lastPos = new Vector3(0,0,0);
    110.         for(int i=0;i<segmentCount;++i)
    111.         {
    112.             dir = new Vector3(Mathf.Cos(currentAngle),Mathf.Sin (currentAngle),0);
    113.             perp = new Vector3(dir.y /(i+1.5f), -dir.x /(i+1.5f), 0);
    114.          
    115.          
    116.             vertices[i*2] = currentPos - perp;
    117.             vertices[i*2+1] = currentPos + perp;
    118.             normals[i*2] = new Vector3(0,0,1);
    119.             normals[i*2+1] = new Vector3(0,0,1);
    120.             uvs[i*2] = new Vector2(((float)i)/segmentCount,0);
    121.             uvs[i*2+1] = new Vector2(((float)i)/segmentCount,1);
    122.             lastPos = currentPos;
    123.             currentPos = currentPos + dir * length;
    124.             currentAngle = currentAngle + segments[i] + offset;
    125.          
    126.             Collider[] things = Physics.OverlapSphere(transform.localToWorldMatrix.MultiplyPoint(lastPos), perp.magnitude*2);
    127.             foreach(Collider thing in things)
    128.             {
    129.                 /*
    130.                 if(thing.gameObject.tag == "Player")
    131.                 {
    132.                     PlayerScript player = (PlayerScript) thing.gameObject.GetComponent(typeof(PlayerScript));
    133.                     if(player!=null)
    134.                     {
    135.                         player.PlayerDamage(1.0f);
    136.                         player.playerIsHitTimer = 0;
    137.                     }
    138.                     //Debug.Log("Player was hit! "+count);
    139.                     count++;
    140.                  
    141.                     thing.gameObject.BroadcastMessage("changeGuiOffRegen"); //turn off gui white when hit, regen
    142.                 }
    143.                 */
    144.             }
    145.          
    146.         }
    147.  
    148.         mesh.vertices = vertices;
    149.      
    150.         for(int i=0;i<segmentCount-1;++i)
    151.         {
    152.             if(tailType == 0)
    153.             {
    154.                 indices[i*6] = i*2+0;
    155.                 indices[i*6+1] = i*2+3;
    156.                 indices[i*6+2] = i*2+1;
    157.                 indices[i*6+4] = i*2+2;
    158.                 indices[i*6+3] = i*2+0;
    159.                 indices[i*6+5] = i*2+3;
    160.             }
    161.             else if(tailType == 1)
    162.             {
    163.                 indices[i*6] = i*1;
    164.                 indices[i*6+1] = i*2+3;
    165.                 indices[i*6+2] = i*2+1;
    166.                 indices[i*6+4] = i*2+2;
    167.                 indices[i*6+3] = i*2+0;
    168.                 indices[i*6+5] = i*1;
    169.             }
    170.             else if(tailType == 2)
    171.             {
    172.                 indices[i*6] = i*2+0;
    173.                 indices[i*6+1] = i*1;
    174.                 indices[i*6+2] = i*2+1;
    175.                 indices[i*6+4] = i*2+2;
    176.                 indices[i*6+3] = i*1;
    177.                 indices[i*6+5] = i*2+3;
    178.             }
    179.         }
    180.      
    181.         mesh.uv = uvs;
    182.         mesh.triangles = indices;
    183.         mesh.colors = colours;
    184.         mesh.normals = normals;
    185.         mesh.RecalculateBounds();
    186.      
    187.      
    188.         Graphics.DrawMesh(mesh, transform.localToWorldMatrix, material, 0);
    189.  
    190.     }
    191.  
    192.     void Die()
    193.     {
    194.         mode = Random.Range (0, 2);
    195.     }
    196. }
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,498
    Hey, that's pretty spiffy!

    I suggest perhaps instead of doing the imperative Graphics.DrawMesh() call, perhaps just make (and of course modify every frame) a MeshFilter/MeshRenderer gameobject to do what you want.

    Also be sure to share the single instance of a material among all the different tentacles you use, so that Unity can batch drawcalls.

    I think if you do it that way, Unity may have better luck batching drawcalls, as the tentacle(s) will be just part of the main scene.

    Kurt
     
  3. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Unity won't batch anything unless it's static (with Pro), or uses <300 total vertex attributes for dynamic objects, so sharing a material won't help here.

    Nothing to do with drawcalls, but one issue with that script is that it's allocating new arrays every frame, which slows things down and causes GC. Ideally it would create the arrays only once; as far as I can see only the vertex array actually needs to be updated every frame.

    --Eric
     
    Stoven likes this.
  4. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Thanks a lot. My FPS now floats around 70 rather than 55-60.

    Feel really dumb now not seeing it was declared inside the update. Was a real face palm moment for me. Thanks again!
     
  5. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    it seems to me this would be better done with a rigged mesh, script controlled animations, and IK.
     
    BrandyStarbrite likes this.
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Seems like that would be slower; you'd need a lot of bones, judging from the screenshots.

    --Eric
     
  7. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Yeah each tentacle has 60 segments so it would be a lot of bones. I will be using less in game but I am just stress testing it at the moment and testing for the best effect to lowest segments. But any and all help to make it better is what I am aiming for :)
    Thanks again to everyone, really appreciate it.
     
  8. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I'd love an explanation for that please. This massive monstrosity of managed code is faster than the native systems designed to handle motion and animation? how?
     
  9. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Mesh skinning with lots of bones is not free, and doing basic calculations with a Vector3 array is very fast. As I mentioned earlier, most of the mesh array code should be moved out of Update—I think only the vertices actually need to be changed every frame—but if that's done I can't imagine what you'd gain by using a skinned mesh. Moving a bunch of transforms around + skinned mesh overhead is going to lose compared to simply iterating over a vertex array.

    --Eric
     
    Tomnnn likes this.
  10. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    I did move the declarations of those variables to out side the update and it did help considerably.
     
  11. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Not just the declaration, but as far as I can tell, the triangles, UVs, and normals don't actually change, so they can be computed once only. Only the vertices change every frame, yes? Also you could probably just do RecalculateBounds once. (Unrelated to performance, the tailType and mode variables should really be enums rather than ints, in order to get rid of magic numbers and make the code more understandable.)

    --Eric
     
  12. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,623
    It would be nice to see the final code on this, to avoid confusion.
     
  13. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Thanks a lot Eric5h5, after neatening it up and adding some comments I now have it running at 60 FPS or higher even with 100 Tentacles on screen! I now see what you meant by not just the declaration; I was setting the connections every frame too when I should have been doing it once. If there are any more ways I can improve it please let me know!

    Other than that here is the current script, I hope it is easy enough for people to read and understand. If you like the script you are more than welcome to use it in your games and edit it as you please :)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class tailScript : MonoBehaviour
    5. {
    6.     public TailType tailtype;                                //How will the tail look?
    7.     public TailMode tailMode;                                //How will the tail behave?
    8.  
    9.     public bool alive = true;                                //Is the tail alive?
    10.     private Mesh mesh;                                        //Store a mesh
    11.     public Material material;                                //Stores a material
    12.     private float angle;                                    //At what angle is this segment?
    13.     public float length;                                    //How long is each Segment?
    14.     public const int segmentCount = 30;                        //How many parts does the tentacle have
    15.     private float[] segments = new float[segmentCount];    
    16.     private float[] targets = new float[segmentCount];    
    17.     private float[] frequency = new float[segmentCount];
    18.     private float[] amplitude = new float[segmentCount];
    19.     private float offset=0;
    20.     private int count=0;
    21.  
    22.     private Vector3[] vertices = new Vector3[segmentCount*2+2];
    23.     private Vector3[] normals = new Vector3[segmentCount*2+2];
    24.     private Vector2[] uvs = new Vector2[segmentCount*2+2];
    25.     private Color[] colours = new Color[segmentCount*2+2];
    26.     private int[] indices = new int[segmentCount*6];
    27.  
    28.     // Use this for initialization
    29.     void Start ()
    30.     {
    31.         mesh = new Mesh();
    32.         for(int i=0;i<segmentCount;++i)
    33.         {
    34.             segments[i]=0.0f;
    35.             targets[i]=0.14f;
    36.             frequency[i] = Random.Range(0.1f,1.0f*2);
    37.             amplitude[i] = Random.Range(0.2f,0.4f*2);
    38.         }
    39.  
    40.         for(int i=0;i<segmentCount-1;++i)
    41.         {
    42.             if(tailtype == TailType.thin)    //Make the tail a fin line
    43.             {
    44.                 indices[i*6] = i*2+0;
    45.                 indices[i*6+1] = i*2+3;
    46.                 indices[i*6+2] = i*2+1;
    47.                 indices[i*6+4] = i*2+2;
    48.                 indices[i*6+3] = i*2+0;
    49.                 indices[i*6+5] = i*2+3;
    50.             }
    51.             else if(tailtype == TailType.fish)    //Make the tail a fanning shape
    52.             {
    53.                 indices[i*6] = i*1;
    54.                 indices[i*6+1] = i*2+3;
    55.                 indices[i*6+2] = i*2+1;
    56.                 indices[i*6+4] = i*2+2;
    57.                 indices[i*6+3] = i*2+0;
    58.                 indices[i*6+5] = i*1;
    59.             }
    60.         }
    61.     }
    62.     void SetTargetAngle(float angle)
    63.     {
    64.         for(int i=0;i<segmentCount;++i)
    65.         {
    66.             targets[i]=angle;
    67.         }
    68.     }
    69.  
    70.     void Update ()
    71.     {
    72.         if(tailMode==TailMode.CurlUpRight)    //Curls the tail to the Right
    73.         {
    74.             for(int i=0;i<segmentCount;++i)
    75.             {
    76.                 float delta = (targets[i] - segments[i]);
    77.                 float turnSpeed = 0.14f * Time.deltaTime;
    78.                 if(delta>0 && delta > turnSpeed)
    79.                 {
    80.                     delta = turnSpeed;
    81.                 }
    82.                 else if(delta<0 && delta < turnSpeed)
    83.                 {
    84.                     delta = turnSpeed;
    85.                 }
    86.                 segments[i]+=delta;
    87.             }
    88.         }
    89.  
    90.         if(tailMode==TailMode.CurlUpLeft)    //Curls the tail to the Left
    91.         {
    92.             for(int i=0;i<segmentCount;++i)
    93.             {
    94.                 float delta = (targets[i] - segments[i]);
    95.                 float turnSpeed = -0.07f * Time.deltaTime;
    96.                 if(delta>0 && delta > turnSpeed)
    97.                 {
    98.                     delta = turnSpeed;
    99.                 }
    100.                 else if(delta<0 && delta < turnSpeed)
    101.                 {
    102.                     delta = turnSpeed;
    103.                 }
    104.                 segments[i]+=delta;
    105.             }
    106.         }
    107.  
    108.         else if(tailMode==TailMode.Wiggle)    //Makes the tail wiggle
    109.         {
    110.             if (!alive)
    111.             {
    112.                 Die();
    113.             }
    114.  
    115.             for(int i=0;i<segmentCount;++i)
    116.             {
    117.                 segments[i] = Mathf.Sin(Time.time * frequency[i])*amplitude[i]/2.5f;
    118.             }        
    119.         }
    120.  
    121.         //Based on  new information make, set the tails values for drawing
    122.         Vector3 currentPos = new Vector3(0,0,0);
    123.         float currentAngle = 0;
    124.      
    125.         Vector3 dir=new Vector3(0,0,0);
    126.         Vector3 perp=new Vector3(0,0,0);
    127.      
    128.         Vector3 lastPos = new Vector3(0,0,0);
    129.  
    130.         for(int i=0;i<segmentCount;++i)
    131.         {
    132.             dir = new Vector3(Mathf.Cos(currentAngle),Mathf.Sin (currentAngle),0);
    133.             perp = new Vector3(dir.y /(i+1.5f), -dir.x /(i+1.5f), 0);
    134.  
    135.             vertices[i*2] = currentPos - perp;
    136.             vertices[i*2+1] = currentPos + perp;
    137.             normals[i*2] = new Vector3(0,0,1);
    138.             normals[i*2+1] = new Vector3(0,0,1);
    139.             uvs[i*2] = new Vector2(((float)i)/segmentCount,0);
    140.             uvs[i*2+1] = new Vector2(((float)i)/segmentCount,1);
    141.             lastPos = currentPos;
    142.             currentPos = currentPos + dir * length;
    143.             currentAngle = currentAngle + segments[i] + offset;
    144.         }
    145.  
    146.         mesh.vertices = vertices;
    147.  
    148.         mesh.uv = uvs;
    149.         mesh.triangles = indices;
    150.         mesh.colors = colours;
    151.         mesh.normals = normals;
    152.         mesh.RecalculateBounds();
    153.  
    154.         Graphics.DrawMesh(mesh, transform.localToWorldMatrix, material, 0);    //Draw the tails new look
    155.  
    156.     }
    157.  
    158.     void Die()    //When the tail dies it will curl in a random direction (Left or Right)
    159.     {
    160.         int curl = Random.Range (0, 2);
    161.         if (curl == 0)
    162.         {
    163.             tailMode = TailMode.CurlUpRight;
    164.         }
    165.         else
    166.         {
    167.             tailMode = TailMode.CurlUpLeft;
    168.         }
    169.     }
    170.  
    171.     public enum TailType
    172.     {
    173.         thin,
    174.         fish
    175.     }
    176.  
    177.     public enum TailMode
    178.     {
    179.         CurlUpLeft,
    180.         CurlUpRight,
    181.         Wiggle
    182.     }
    183. }
    184.  
     
    Last edited: Dec 5, 2014
    sengels666 and hopeful like this.
  14. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    I did some stress testing, I was able to have 800 on screen before I dipped below 60 FPS and even then it only went to 55 at the lowest.

    So I am quite pleased with this over all now, thanks again to everyone!
     
  15. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    You still have the uvs and normals being calculated in Update, instead of Start. Also you should set mesh.triangles in Start rather than Update. Setting mesh arrays copies them to the graphics card (it's not just a reference), so it should be avoided if nothing in the array has changed. So Update should only calculate the vertices array, and only set mesh.vertices. (Also mesh.colors doesn't seem to be used, so you can remove any references to that.)

    --Eric
     
  16. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Whenever I move mesh commands to start I get this error

    Just to clarify I am moving this section of code to start
    Code (CSharp):
    1.  
    2. mesh.vertices = vertices;
    3. mesh.triangles = indices;
    4. mesh.colors = colours;
    5. mesh.normals = normals;
    6. mesh.RecalculateBounds();
    I might be trying to move the wrong bit of code. Sorry I am not that good at coding yet, but that is what practice is for :p
     
  17. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Are you moving that code to the end of the Start function?

    --Eric
     
  18. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Yes
    Code (CSharp):
    1.     void Start ()
    2.     {
    3.         mesh = new Mesh();
    4.         for(int i=0;i<segmentCount;++i)
    5.         {
    6.             segments[i]=0.0f;
    7.             targets[i]=0.14f;
    8.             frequency[i] = Random.Range(0.1f,1.0f*2);
    9.             amplitude[i] = Random.Range(0.2f,0.4f*2);
    10.         }
    11.        
    12.         for(int i=0;i<segmentCount-1;++i)
    13.         {
    14.             if(tailtype == TailType.thin)    //Make the tail a fin line
    15.             {
    16.                 indices[i*6] = i*2+0;
    17.                 indices[i*6+1] = i*2+3;
    18.                 indices[i*6+2] = i*2+1;
    19.                 indices[i*6+4] = i*2+2;
    20.                 indices[i*6+3] = i*2+0;
    21.                 indices[i*6+5] = i*2+3;
    22.             }
    23.             else if(tailtype == TailType.fish)    //Make the tail a fanning shape
    24.             {
    25.                 indices[i*6] = i*1;
    26.                 indices[i*6+1] = i*2+3;
    27.                 indices[i*6+2] = i*2+1;
    28.                 indices[i*6+4] = i*2+2;
    29.                 indices[i*6+3] = i*2+0;
    30.                 indices[i*6+5] = i*1;
    31.             }
    32.         }
    33.  
    34.         mesh.uv = uvs;
    35.         mesh.vertices = vertices;
    36.         mesh.triangles = indices;
    37.         mesh.colors = colours;
    38.         mesh.normals = normals;
    39.         mesh.RecalculateBounds();
    40.     }
     
  19. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    You have mesh.uv and mesh.vertices swapped.

    --Eric
     
  20. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    I swapped them around, no longer get the error but the tentacles do not show up anymore :\
     
  21. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Did you leave "mesh.vertices = vertices" in Update? Did you move all the UV and normals calculations out of Update, and into Start? Remember that the point of this is to get the UV and normals calculated once in Start, and completely out of Update. Only the vertices stuff should be in Update.

    --Eric
     
  22. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    If I have it just in start nothing is visible.
    If I have it in both it all turns black.
    If I have it just in update I get an error.

     
  23. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Very cool.... Script with all the changes works great on my system, mesh does disappear when I move the base of the tentacles out of view of the camera...
     
  24. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    That's motivating to hear. I definitely want to get into procedural meshes more. Can you comment on this being the case at a large scale also?
     
  25. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    You need to copy the loop, and put it in Start. Remove the vertices stuff from the loop in Start. Keep mesh.vertices = vertices in Start (you need to set the vertices array before the other stuff, even if vertices aren't computed yet). Remove the UV + normals stuff from the loop in Update.

    It really depends on what you're doing.

    --Eric
     
  26. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    When I added them in a more complete project ( on a test player character with movement ) I noticed they can only be seen from certain camera angles.... seems they are one pixel thick..... Later on going to try add a thickness value....
     
  27. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    They are zero pixels thick actually. You could have the quads be oriented toward the camera, which may be easier than adding thickness.

    --Eric
     
    image28 likes this.
  28. Camronas

    Camronas

    Joined:
    Apr 20, 2012
    Posts:
    154
    Yeah they are 0 thick. These were designed and made for games like platformers where you will only ever see them from one camera angle. Sorry, should have mentioned that earlier.