Search Unity

Draw circles or primitives on the new UI canvas?

Discussion in 'UGUI & TextMesh Pro' started by esteban16108, Oct 6, 2014.

  1. esteban16108

    esteban16108

    Joined:
    Jan 23, 2014
    Posts:
    158
    Hi, I'm trying to build a Circular Health bar and I was wondering if it's possible to draw Circles/Arcs or primitives like lines into the canvas so I can create my Circular HealthBar ?

    Regards

    E
     
    just4fun13 likes this.
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    There's no support for primitives right now. You can, however, use an image and a Radial Fill to make a circular health bar.

    If you want to get into the nitty gritty of the UI it's also possible to write a component that extends from some UI classes and builds your own mesh. I haven't gotten into that yet though, so I can't really give you advice on getting started.
     
  3. ChimeraSW

    ChimeraSW

    Joined:
    Jul 24, 2013
    Posts:
    28
    Yes; I've done this, its not all that hard. The only gotcha when making circle is that the OnFillVBO function expects you to send quads only (no triangles) so you will be sending extra geometry.

    Here's the docs with a complete example:
    http://docs.unity3d.com/460/Documentation/ScriptReference/UI.Graphic.html
     
  4. FoWare

    FoWare

    Joined:
    May 24, 2013
    Posts:
    13
    Well, not completely unsupported. You can take any object and replace the Transform component with a RectTransform and the anchors seem to be working for the most part. It's not perfect but I can pretty much put anything into my canvas. Keep in mind my canvas is camera based not overlay, and also if you add some 3D objects the z-axis will most likely need to be adjusted.

    That said, a specific fill pattern on an Image object or script drawing with a Graphic subclass is probably the most reliable.
     
  5. madebynoxc

    madebynoxc

    Joined:
    Jan 14, 2014
    Posts:
    10
    Just wrote a class that allows to make a circle in edit mode based on example above. It can be filled or not and you can edit the "fill angle" for it. The code should be optimized

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5.  
    6. [ExecuteInEditMode]
    7. public class UICircle : Graphic
    8. {
    9.     [Range(0,100)]
    10.     public int fillPercent;
    11.     public bool fill = true;
    12.     public int thikness = 5;
    13.  
    14.     void Update(){
    15.         this.thikness = (int)Mathf.Clamp(this.thikness, 0, rectTransform.rect.width/2);
    16.     }
    17.  
    18.     protected override void OnFillVBO (List<UIVertex> vbo){
    19.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    20.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thikness;
    21.  
    22.         vbo.Clear();
    23.  
    24.         UIVertex vert = UIVertex.simpleVert;
    25.         Vector2 prevX = Vector2.zero;
    26.         Vector2 prevY = Vector2.zero;
    27.  
    28.         float f = (float)(this.fillPercent/100f);
    29.         int fa = (int)(361 * f);
    30.  
    31.         for(int i=0; i<fa; i++){
    32.             float rad = Mathf.Deg2Rad * i;
    33.             float c = Mathf.Cos(rad);
    34.             float s = Mathf.Sin(rad);
    35.             float x = outer * c;
    36.             float y = inner * c;
    37.             vert.color = color;
    38.             vert.position = prevX;
    39.             vbo.Add(vert);
    40.             prevX = new Vector2(outer * c, outer * s);
    41.             vert.position = prevX;
    42.             vbo.Add(vert);
    43.             if(this.fill){
    44.                 vert.position = Vector2.zero;
    45.                 vbo.Add(vert);
    46.                 vbo.Add(vert);
    47.             }
    48.             else{
    49.                 vert.position = new Vector2(inner * c, inner * s);;
    50.                 vbo.Add(vert);
    51.                 vert.position = prevY;
    52.                 vbo.Add(vert);
    53.                 prevY = new Vector2(inner * c, inner * s);
    54.             }
    55.         }
    56.     }
    57. }
    Also it can be done using shaders. The main goal of this one is to draw a non-filled circle with constant border thickness. So it won't change while you resize the rect :)
     
    Last edited: Mar 7, 2015
    n1gth likes this.
  6. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Thank you for this NoxCaos.

    I was wondering if anyone could point me in the right direction to add a texture to this? I think I've managed to assign UVs, but not sure how to draw an image to them.
     
  7. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Got it figured out. Just had to add this:

    Code (CSharp):
    1.     [SerializeField] Texture m_Texture;
    2.  
    3.     public override Texture mainTexture
    4.     {
    5.         get
    6.         {
    7.             return m_Texture == null ? s_WhiteTexture : m_Texture;
    8.         }
    9.     }
    10.  
    11.  
    12.     /// <summary>
    13.     /// Texture to be used.
    14.     /// </summary>
    15.     public Texture texture
    16.     {
    17.         get
    18.         {
    19.             return m_Texture;
    20.         }
    21.         set
    22.         {
    23.             if (m_Texture == value)
    24.                 return;
    25.            
    26.             m_Texture = value;
    27.             SetVerticesDirty();
    28.             SetMaterialDirty();
    29.         }
    30.     }
     
  8. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Hmm, must have missed this one.
    @NoxCaos & @zge are you ok with this being added to the UI Extensions repo?
    Granted has to also be updated for the new 5.2 way of doing things but I can sort that.

    Would also be good to add several more primitives as well I think.
     
  9. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    As far as my contribution, it's just copied from Image.cs.

    I used this as a base to create a circular timer with a dynamic number of segments. For some reason the first segment in this code always had a vertex in the first poly at 0,0 even when set to not filled. I couldn't quite figure out why so I've had to do some messy stuff to set the first vertices in the correct position when not filled.

    I'm happy to send you what I've done if it'll help.
     
  10. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Yup, either push it as a PR to the repo, or I'll just grab your code above.
    Might be better as a UI primative library with a few different shapes.

    You can also check out the LineRenderer component already in the UI Extensions repo if you need any tips for rendering raw vertices. (note in 5.2, it all changes!!)
     
  11. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Is it possible to somehow optimize a UI circle? I'm working for mobile and don't want to use 5MB textures to get a sharp circle.
     
  12. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    A primative library would be great, but equilateral shapes can be easily generated using this code, just by changing the number of segments.

    Add this:
    Code (CSharp):
    1.  
    2.   public int segments = 360;
    3.  
    and change to this:
    Code (CSharp):
    1.  
    2.         float f = (float)(this.fillPercent/100f);
    3.         float degrees = (float)360f / segments;
    4.         int fa = (int)( (segments + 1) * f);
    5.  
    6.        
    7.         for(int i=0; i<fa; i++){
    8.             float rad = Mathf.Deg2Rad * (i*degrees);
    9.  

    I did have rendering vertices figured out until 5.2 removed onFillVBO :p I'll be checking out the new Image and lineRenderer to figure out the new methods.

    @Shushustorm NoxCaos code above draws a UI circle without any textures. You can customize it if you need it to be smoother or lower resolution.
     
    Shushustorm likes this.
  13. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    PS I just realized my earlier contribution was stolen from your UILineRenderer, not Image.cs...
     
  14. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Thanks for pointing this out. I would prefer being able to see the UI in the Editor while working on it, though. It's very hard for me to build up UI if I don't see it. So a solution using UI elements would be great.

    Being able to use UV-mapped 2d meshes for UI isn't a planned feature, is it?
     
  15. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
    I'm actually 95% done with mine that I will be submitting to the Asset Store in a day or two if all goes well. =)

    Aaaand that's my final 5%. ;)
     
    Last edited: Sep 12, 2015
    Stephan-B likes this.
  16. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    @Shushustorm This is a UI element and is fully editable and visible in the editor. You can also easily add textures. Text and UI images are just UV mapped 2d meshes like this is. Just add a UI panel and put the script on it, and play with the settings...

    I've also updated it to work with 5.2:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4. [ExecuteInEditMode]
    5. public class UICircle2 : Graphic
    6. {
    7.     [SerializeField]
    8.     Texture m_Texture;
    9.     [Range(0, 100)]
    10.     public int fillPercent;
    11.     public bool fill = true;
    12.     public int thikness = 5;
    13.     [Range(0, 360)]
    14.     public int segments = 360;
    15.     public override Texture mainTexture
    16.     {
    17.         get
    18.         {
    19.             return m_Texture == null ? s_WhiteTexture : m_Texture;
    20.         }
    21.     }
    22.     /// <summary>
    23.     /// Texture to be used.
    24.     /// </summary>
    25.     public Texture texture
    26.     {
    27.         get
    28.         {
    29.             return m_Texture;
    30.         }
    31.         set
    32.         {
    33.             if (m_Texture == value)
    34.                 return;
    35.             m_Texture = value;
    36.             SetVerticesDirty();
    37.             SetMaterialDirty();
    38.         }
    39.     }
    40.     void Update()
    41.     {
    42.         this.thikness = (int)Mathf.Clamp(this.thikness, 0, rectTransform.rect.width / 2);
    43.     }
    44.     protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
    45.     {
    46.         UIVertex[] vbo = new UIVertex[4];
    47.         for (int i = 0; i < vertices.Length; i++)
    48.         {
    49.             var vert = UIVertex.simpleVert;
    50.             vert.color = color;
    51.             vert.position = vertices[i];
    52.             vert.uv0 = uvs[i];
    53.             vbo[i] = vert;
    54.         }
    55.         return vbo;
    56.     }
    57.     //    protected override void OnFillVBO(List<UIVertex> vbo)
    58.     protected override void OnPopulateMesh(Mesh toFill)
    59.     {
    60.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    61.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thikness;
    62.         //        vbo.Clear();
    63.         toFill.Clear();
    64.         var vbo = new VertexHelper(toFill);
    65.         UIVertex vert = UIVertex.simpleVert;
    66.         Vector2 prevX = Vector2.zero;
    67.         Vector2 prevY = Vector2.zero;
    68.         Vector2 uv0 = new Vector2(0, 0);
    69.         Vector2 uv1 = new Vector2(0, 1);
    70.         Vector2 uv2 = new Vector2(1, 1);
    71.         Vector2 uv3 = new Vector2(1, 0);
    72.         Vector2 pos0;
    73.         Vector2 pos1;
    74.         Vector2 pos2;
    75.         Vector2 pos3;
    76.         float f = (this.fillPercent / 100f);
    77.         float degrees = 360f / segments;
    78.         int fa = (int)((segments + 1) * f);
    79.         for (int i = 0; i < fa; i++)
    80.         {
    81.             float rad = Mathf.Deg2Rad * (i * degrees);
    82.             float c = Mathf.Cos(rad);
    83.             float s = Mathf.Sin(rad);
    84.             float x = outer * c;
    85.             float y = inner * c;
    86.             uv0 = new Vector2(0, 1);
    87.             uv1 = new Vector2(1, 1);
    88.             uv2 = new Vector2(1, 0);
    89.             uv3 = new Vector2(0, 0);
    90.             pos0 = prevX;
    91.             pos1 = new Vector2(outer * c, outer * s);
    92.             if (fill)
    93.             {
    94.                 pos2 = Vector2.zero;
    95.                 pos3 = Vector2.zero;
    96.             }
    97.             else
    98.             {
    99.                 pos2 = new Vector2(inner * c, inner * s);
    100.                 pos3 = prevY;
    101.             }
    102.             prevX = pos1;
    103.             prevY = pos2;
    104.             vbo.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
    105.         }
    106.         if (vbo.currentVertCount > 3)
    107.         {
    108.             vbo.FillMesh(toFill);
    109.         }
    110.     }
    111. }
    @Senshi There are a couple of things I'd add to this to make it a more fully functioning shape library. The version I'm using has a few more UV mapping options like slice etc, and discrete segments which was the whole reason for me making it. I needed a radial fill where I could control the edges instead of just an aliased cut-off.

    Good luck with yours :)
     
  17. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    @zge Oh, alright! I thought it would only be drawn when running the game. Thanks for that solution!
     
  18. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Good stuff @Senshi wish you all the best with that asset :D
     
    Senshi likes this.
  19. krstrobe

    krstrobe

    Joined:
    Dec 3, 2015
    Posts:
    5
    @zge this script is awesome! One issue for me is that I'd like to use a material on it and it's not working out... any idea why it disappears when I add a material?
     
  20. Brad-Keys

    Brad-Keys

    Joined:
    May 1, 2006
    Posts:
    161
    @zge Great script! One issue I noticed is that if you attempt to fill the circle completely, the epicentre is not filled in, causing a small dot of transparent background to show through. This was noticeable for me because there was another UI element behind it of a different colour.

    I believe the root of the issue stems from how thickness is being clamped. It's possible that a very small amount is never accounted for. My quick fix was to add +1 after dividing the recttransform in two. This isn't the right solution, but it worked for my case. Attached is a screenshot where you can see the top blue circle is filled in completely with the +1 added to the script while the bottom orange circle has a small green dot in the epicentre using the unmodified script.

    edit: To keep consistency with UIImage I recommend changing fillPercent to fillAmount and make it a float rather than an int.

    edit 2: For my use case I also needed to animate the fill amount, but the graphic wasn't being refreshed if the value changed. Here is my revised script which accounts for all the issues mentioned in my post here.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections.Generic;
    5. [ExecuteInEditMode]
    6. public class UICircle : Graphic
    7. {
    8.     [SerializeField]
    9.     Texture m_Texture;
    10.  
    11.     [Range(0, 1)]
    12.     public float fillAmount;
    13.  
    14.     public bool fill = true;
    15.     public int thickness = 5;
    16.  
    17.     [Range(0, 360)]
    18.     public int segments = 360;
    19.  
    20.     public override Texture mainTexture
    21.     {
    22.         get {
    23.             return m_Texture == null ? s_WhiteTexture : m_Texture;
    24.         }
    25.     }
    26.  
    27.     float oldFillAmount;
    28.  
    29.     /// <summary>
    30.     /// Texture to be used.
    31.     /// </summary>
    32.     public Texture texture
    33.     {
    34.         get {
    35.             return m_Texture;
    36.         }
    37.  
    38.         set {
    39.             if (m_Texture == value)
    40.                 return;
    41.             m_Texture = value;
    42.             SetVerticesDirty();
    43.             SetMaterialDirty();
    44.         }
    45.     }
    46.  
    47.     void Start ()
    48.     {
    49.         oldFillAmount = fillAmount;
    50.     }
    51.  
    52.     void Update ()
    53.     {
    54.         this.thickness = (int)Mathf.Clamp(this.thickness, 0, (rectTransform.rect.width / 2) + 1);
    55.  
    56.         if (!Mathf.Approximately(oldFillAmount, fillAmount)) {
    57.             this.UpdateGeometry();
    58.             oldFillAmount = fillAmount;
    59.         }
    60.     }
    61.  
    62.     protected UIVertex[] SetVbo (Vector2[] vertices, Vector2[] uvs)
    63.     {
    64.         UIVertex[] vbo = new UIVertex[4];
    65.  
    66.         for (int i = 0; i < vertices.Length; i++) {
    67.             var vert = UIVertex.simpleVert;
    68.             vert.color = color;
    69.             vert.position = vertices[i];
    70.             vert.uv0 = uvs[i];
    71.             vbo[i] = vert;
    72.         }
    73.  
    74.         return vbo;
    75.     }
    76.  
    77.     //    protected override void OnFillVBO(List<UIVertex> vbo)
    78.     protected override void OnPopulateMesh (Mesh toFill)
    79.     {
    80.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    81.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thickness;
    82.         //        vbo.Clear();
    83.         toFill.Clear();
    84.         var vbo = new VertexHelper(toFill);
    85.         UIVertex vert = UIVertex.simpleVert;
    86.  
    87.         Vector2 prevX = Vector2.zero;
    88.         Vector2 prevY = Vector2.zero;
    89.  
    90.         Vector2 uv0 = new Vector2(0, 0);
    91.         Vector2 uv1 = new Vector2(0, 1);
    92.         Vector2 uv2 = new Vector2(1, 1);
    93.         Vector2 uv3 = new Vector2(1, 0);
    94.  
    95.         Vector2 pos0;
    96.         Vector2 pos1;
    97.         Vector2 pos2;
    98.         Vector2 pos3;
    99.  
    100.         float degrees = 360f / segments;
    101.         int fa = (int)((segments + 1) * this.fillAmount);
    102.  
    103.         for (int i = 0; i < fa; i++) {
    104.             float rad = Mathf.Deg2Rad * (i * degrees);
    105.             float c = Mathf.Cos(rad);
    106.             float s = Mathf.Sin(rad);
    107.             float x = outer * c;
    108.             float y = inner * c;
    109.  
    110.             uv0 = new Vector2(0, 1);
    111.             uv1 = new Vector2(1, 1);
    112.             uv2 = new Vector2(1, 0);
    113.             uv3 = new Vector2(0, 0);
    114.  
    115.             pos0 = prevX;
    116.             pos1 = new Vector2(outer * c, outer * s);
    117.  
    118.             if (fill) {
    119.                 pos2 = Vector2.zero;
    120.                 pos3 = Vector2.zero;
    121.             } else {
    122.                 pos2 = new Vector2(inner * c, inner * s);
    123.                 pos3 = prevY;
    124.             }
    125.  
    126.             prevX = pos1;
    127.             prevY = pos2;
    128.  
    129.             vbo.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
    130.         }
    131.  
    132.         if (vbo.currentVertCount > 3) {
    133.             vbo.FillMesh(toFill);
    134.         }
    135.     }
    136. }
    137.  
    138.  
     

    Attached Files:

    Last edited: Sep 5, 2016
    stickywes likes this.
  21. Kretin1

    Kretin1

    Joined:
    Sep 29, 2015
    Posts:
    6
    Great stuff making it work for you. Thanks for posting your updates.

    I haven't noticed that problem in the center, but I'm also not sure how the thickness adjustment would have fixed it. There was a big problem with the first polygon which had vertices in the center of the circle even when it wasn't filled. That created some visual artifacts for me.

    One thing you may also notice is that OnPopulateMesh(Mesh m) has been depreciated. Here's a new version using OnPopulateMesh(VertexHelper vh) instead. I've made some other updates which you may be interested in as well. I've commented them in the script:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5. [ExecuteInEditMode]
    6. public class UICircle2 : MaskableGraphic // Changed to maskableGraphic so it can be masked with RectMask2D
    7. {
    8.     [SerializeField]
    9.     Texture m_Texture;
    10.  
    11.     [Range(0, 1)]
    12.     [SerializeField]
    13.     private float fillAmount;
    14.  
    15.     public float FillAmount
    16.     {
    17.         get { return fillAmount; }
    18.         set
    19.         {
    20.             fillAmount = value;
    21.  
    22.             // This detects a change and sets the vertices dirty so it gets updated
    23.             SetVerticesDirty();
    24.         }
    25.     }
    26.  
    27.     public bool fill = true;
    28.     public int thickness = 5;
    29.  
    30.     [Range(0, 360)]
    31.     public int segments = 360;
    32.  
    33.     public override Texture mainTexture
    34.     {
    35.         get
    36.         {
    37.             return m_Texture == null ? s_WhiteTexture : m_Texture;
    38.         }
    39.     }
    40.  
    41.     //float oldFillAmount;
    42.  
    43.     /// <summary>
    44.     /// Texture to be used.
    45.     /// </summary>
    46.     public Texture texture
    47.     {
    48.         get { return m_Texture; }
    49.  
    50.         set
    51.         {
    52.             if (m_Texture == value)
    53.                 return;
    54.             m_Texture = value;
    55.             SetVerticesDirty();
    56.             SetMaterialDirty();
    57.         }
    58.     }
    59.  
    60.     // Using arrays is a bit more efficient
    61.     UIVertex[] uiVertices = new UIVertex[4];
    62.     Vector2[] uvs = new Vector2[4];
    63.     Vector2[] pos = new Vector2[4];
    64.  
    65.     protected override void Start()
    66.     {
    67.         //oldFillAmount = fillAmount;
    68.  
    69.         uvs[0] = new Vector2(0, 1);
    70.         uvs[1] = new Vector2(1, 1);
    71.         uvs[2] = new Vector2(1, 0);
    72.         uvs[3] = new Vector2(0, 0);
    73.     }
    74.  
    75.     // Removed Update because it's more efficient to use a property
    76.     //void Update()
    77.     //{
    78.     //if (!Mathf.Approximately(oldFillAmount, fillAmount))
    79.     //{
    80.     //    SetVerticesDirty();
    81.     //    oldFillAmount = fillAmount;
    82.     //}
    83.     //}
    84.  
    85.  
    86.     // Updated OnPopulateMesh to user VertexHelper instead of mesh
    87.     protected override void OnPopulateMesh(VertexHelper vh)
    88.     {
    89.         // There's really no need to clamp the thickness
    90.         //thickness = (int)Mathf.Clamp(thickness, 0, (rectTransform.rect.width / 2));
    91.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    92.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + thickness;
    93.  
    94.         float degrees = 360f / segments;
    95.         int fa = (int)((segments + 1) * this.fillAmount);
    96.  
    97.         // Updated to new vertexhelper
    98.         vh.Clear();
    99.  
    100.         //toFill.Clear();
    101.         //var vbo = new VertexHelper(toFill);
    102.         //UIVertex vert = UIVertex.simpleVert;
    103.  
    104.         // Changed initial values so the first polygon is correct when circle isn't filled
    105.         float x = outer * Mathf.Cos(0);
    106.         float y = outer * Mathf.Sin(0);
    107.         Vector2 prevX = new Vector2(x, y);
    108.  
    109.         // Changed initial values so the first polygon is correct when circle isn't filled
    110.         x = inner * Mathf.Cos(0);
    111.         y = inner * Mathf.Sin(0);
    112.         Vector2 prevY = new Vector2(x, y);
    113.  
    114.         for (int i = 0; i < fa - 1; i++)
    115.         {
    116.             // Changed so there isn't a stray polygon at the beginning of the arc
    117.             float rad = Mathf.Deg2Rad * ((i + 1) * degrees);
    118.             float c = Mathf.Cos(rad);
    119.             float s = Mathf.Sin(rad);
    120.             //float x = outer * c;
    121.             //float y = inner * c;
    122.  
    123.             pos[0] = prevX;
    124.             pos[1] = new Vector2(outer * c, outer * s);
    125.  
    126.             if (fill)
    127.             {
    128.                 pos[2] = Vector2.zero;
    129.                 pos[3] = Vector2.zero;
    130.             }
    131.             else
    132.             {
    133.                 pos[2] = new Vector2(inner * c, inner * s);
    134.                 pos[3] = prevY;
    135.             }
    136.  
    137.             // Set values for uiVertices
    138.             for (int j = 0; j < 4; j++)
    139.             {
    140.                 uiVertices[j].color = color;
    141.                 uiVertices[j].position = pos[j];
    142.                 uiVertices[j].uv0 = uvs[j];
    143.             }
    144.  
    145.             // Get the current vertex count
    146.             int vCount = vh.currentVertCount;
    147.  
    148.             // If filled, we only need to create one triangle
    149.             vh.AddVert(uiVertices[0]);
    150.             vh.AddVert(uiVertices[1]);
    151.             vh.AddVert(uiVertices[2]);
    152.  
    153.             // Create triangle from added vertices
    154.             vh.AddTriangle(vCount, vCount + 2, vCount + 1);
    155.  
    156.             // If not filled we need to add the 4th vertex and another triangle
    157.             if (!fill)
    158.             {
    159.                 vh.AddVert(uiVertices[3]);
    160.                 vh.AddTriangle(vCount, vCount + 3, vCount + 2);
    161.             }
    162.  
    163.             prevX = pos[1];
    164.             prevY = pos[2];
    165.  
    166.             // Removed so we can just use a single triangle when not filled
    167.             //vh.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
    168.         }
    169.  
    170.         // Removed because we don't need it any more with new OnPopulateMesh using a VertexHelper instead of a Mesh
    171.         //if (vbo.currentVertCount > 3)
    172.         //{
    173.         //    vbo.FillMesh(toFill);
    174.         //}
    175.     }
    176. }
     
    Last edited: Sep 18, 2016
    Nastomeya, victorbadea and stickywes like this.
  22. Kretin1

    Kretin1

    Joined:
    Sep 29, 2015
    Posts:
    6
    The earlier version had it's normals backwards, so a single sided material might not have showed up? The normals in the latest one are facing forwards.
     
    stickywes likes this.
  23. yudixiaok

    yudixiaok

    Joined:
    Aug 29, 2014
    Posts:
    17
  24. yudixiaok

    yudixiaok

    Joined:
    Aug 29, 2014
    Posts:
    17
    may be you can use vh.AddUIVertexStream(m_vertices, m_indices); make it much better performance
     
  25. Kretin1

    Kretin1

    Joined:
    Sep 29, 2015
    Posts:
    6
    That would be great, except I haven't had any luck getting AddUIVertexStream to work. Do you know how it can work for this?
     
  26. nico69

    nico69

    Joined:
    Oct 15, 2016
    Posts:
    7
    Hi

    I have tried your way (@Kretin1) to draw a custom sharp in the canvas UI, but i can't make it work.

    For now, i want to draw a quad with a constant width at the center of the screen, with a color.

    This is my code :
    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. class UILineDrawer : MaskableGraphic
    7. {
    8.     private List<Vector2> m_points = new List<Vector2>();
    9.     private Color m_color = Color.white;
    10.     private const float m_width = 50;
    11.  
    12.     public void setPoints(List<Vector2> points)
    13.     {
    14.         Debug.Log("Points set");
    15.         m_points = points;
    16.         SetAllDirty();
    17.     }
    18.  
    19.     public void clearPoints()
    20.     {
    21.         Debug.Log("Points clear");
    22.         m_points.Clear();
    23.         SetAllDirty();
    24.     }
    25.  
    26.     public void setColor(Color color)
    27.     {
    28.         Debug.Log("Color set");
    29.         m_Material.color = color;
    30.         SetAllDirty();
    31.     }
    32.  
    33.     protected override void OnPopulateMesh(VertexHelper vh)
    34.     {
    35.         var point = new Vector2(0, 0);
    36.  
    37.         vh.Clear();
    38.         UIVertex[] quad = new UIVertex[4];
    39.         quad[0].position = new Vector3(point.x - m_width / 2, 0, point.y - m_width / 2);
    40.         quad[1].position = new Vector3(point.x + m_width / 2, 0, point.y - m_width / 2);
    41.         quad[2].position = new Vector3(point.x + m_width / 2, 0, point.y + m_width / 2);
    42.         quad[3].position = new Vector3(point.x - m_width / 2, 0, point.y + m_width / 2);
    43.  
    44.         quad[0].color = m_color;
    45.         quad[1].color = m_color;
    46.         quad[2].color = m_color;
    47.         quad[3].color = m_color;
    48.  
    49.         quad[0].uv0 = new Vector2(0, 0);
    50.         quad[1].uv0 = new Vector2(1, 0);
    51.         quad[2].uv0 = new Vector2(1, 1);
    52.         quad[3].uv0 = new Vector2(0, 1);
    53.  
    54.         for (int i = 0; i < 4; i++)
    55.             vh.AddVert(quad[i]);
    56.         vh.AddTriangle(0, 1, 2);
    57.         vh.AddTriangle(1, 2, 3);
    58.  
    59.         Debug.Log("Drawed");
    60.     }
    61. }
    62.  
    All the logs are fired correctly, the function OnPopulateMesh is called all the times i ask to change the color or points list.
    I have set a valid material (the default material) in the unity editor to this object, i don't think the material is a problem.

    Why it can't be visible ?

    Thanks for your help.
     
  27. Kretin1

    Kretin1

    Joined:
    Sep 29, 2015
    Posts:
    6
    Sorry for the late reply.

    What is happening? Are the polygons being drawn (can you see them with wireframes turned on)? Have you looked from both sides to make sure the normals aren't facing backwards?
     
  28. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    You need to call "SetDirty()" (or "SetALLDirty()") after changing any properties to force the UI system to redraw the component.
     
  29. Deankovitch

    Deankovitch

    Joined:
    Sep 28, 2012
    Posts:
    31
    Hi all, I'd like to share with you my plugin to create 2D Primitives.
    You can create these primitives
    • Circle
    • Star
    • Rounded square
    • Arrow
    • Spiral
    • N-Sides Polygon
    • Gradient Quad
    • 4 Corner Gradient Quad
    All primitives types are inherited from default Graphic class so it's behave as any canvas element.
    The main pros to use those primitives are:
    • They are resolution independent;
    • Fully animable;
    • Lightweight;
    • Fully configurable;
    • Source code included;
    • You can use the shapes included in the pack (as a static class Shape2DHelper) for many implementations: spawn objects, emit particles, draw, animation etc.
    • Fast and easy to prototype or create UI / UX;
    To next version I'm adding support to create 2D Sprite Primitives as well.

    Here is the link to the plugin on Asset Store
    2D Primitives