IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Unity游戏选/创建角色界面中 职业能力图六角形

    u010019717发表于 2016-08-22 23:11:11
    love 0

    2016.8.22               孙广东

    游戏选/创建角色界面中 职业能力图六角形

            主要说的是 UGUI 的实现方式!!!!!!

     

         下面这个六边形在很多游戏中都存在了(随便在网上找的, 一会就使用这个图片)。:

     

     

    在  Asset Store 上搜索,  Chart  会得到相关的结果

         

     

     

    先使用最简单的方式绘制Mesh多边形。

    using UnityEngine;
    
    using System.Collections;
    
     
    
    public class MeshTest : MonoBehaviour {
    
     
    
        privateMeshFilter m_MeshFilter;
    
     
    
        void Start() {
    
            Meshmesh = new Mesh();
    
     
    
           mesh.vertices = new Vector3[] {
    
               new Vector3(0, 0, 0),       // 顶点:0
    
               new Vector3(1, 0, 0),       // 顶点:1
    
               new Vector3(1.5f, 1, 0),    // 顶点:2
    
               new Vector3(0.6f, 1.5f, 0), // 顶点:3
    
               new Vector3(-0.8f, 1, 0),    //顶点:4
    
            };
    
     
    
            //遵循顺时针三顶点确定一面
    
           mesh.triangles = new int[] {
    
               0, 2, 1, // 面:0, 2, 1
    
               0, 4, 2, // 面:0, 4, 2
    
               4, 3, 2, // 面:4, 3, 2
    
            };
    
     
    
     
    
           m_MeshFilter = GetComponent<MeshFilter>();
    
           m_MeshFilter.mesh = mesh;
    
        }
    
    }
    

     

    顶点和三角面 就组成这个多边形。 

     

     

    这个功能肯定是 放在UI上,  对 UGUI

               首先大概介绍下uGUI的体系结构。在uGUI中,所有的UI组件都要放置在Canvas组件下,由Canvas来管理它的渲染和自适应屏幕等。uGUI提供了Graphic基类,运行时,Canvas会使用CanvasRenderer来渲染它的子级中全部的Graphic组件。所以,如果要自定义外观的控件,从Graphic继承是一个不错的选择。

     

     

            我们都知道,在unity中3d物体最终是转化成若干网格数据来渲染的,其实ui的渲染也是一样的方法(注意 是Unity5.3之后UGUI才引入  Mesh这个东东)。比如一个Image组件,内部其实是使用4个顶点构成的2个三角形网格外加一个Texture贴图来渲染的。那么要改变控件的外形,只要改变网格数据就可以了,对,就是这么个思路。

     

     

    Graphic提供了一个虚方法

               protected virtual void OnPopulateMesh(VertexHelper vh);

     

               通过重写这个方法,即可修改或重新生成控件的网格数据,从而达到自定义控件显示外观的需求(Unity4为另一个接口OnFillVBO,不过原理是一致的)。而VertexHelper是unity提供的简化网格操作的辅助类,它提供的接口也很简单,诸如添加顶点、添加三角形、添加Quad等。

     

                 需要注意的一点是,顶点的坐标是由控件的位置、大小和锚点等决定的,计算时需要综合考虑这些因素。

     

     

    看 UGUI的源代码 :

                   说的是当该控件(例如Text,Image,RawImage)需要改变顶点的时候,就会自动调用  OnPopulateMesh。

                不过该函数是只有在该Craphic组件需要修改的时候才会调用,比如你修改Image的大小,或者它加载的时候才会调用。

              这样就导致我们没法及时在编辑器状态下看到我们对mesh的修改,  比如用是将一张Image的四个角分别用四个对象表示,这四个对象的移动,会让这种Image发生形变。但是没法及时更新就没办法了。

    还好有   SetNativeSize()   这个方法,其实跟刷新差不多。

     

    直接上代码:

    /// CreditCiaccoDavide
    
    /// Sourced from - http://ciaccodavi.de/unity/UIPolygon
    
     
    
    namespace UnityEngine.UI.Extensions
    
    {
    
       [AddComponentMenu("UI/Extensions/Primitives/UI Polygon")]
    
       public class UIPolygon : UIPrimitiveBase
    
       {
    
            public bool fill = true;
    
            public float thickness = 5;
    
            [Range(3, 360)]
    
            public int sides = 3;
    
            [Range(0, 360)]
    
            public float rotation = 0;
    
            [Range(0, 1)]
    
            public float[] VerticesDistances = newfloat[3];
    
            private float size = 0;
    
     
    
            public void DrawPolygon(int _sides)
    
            {
    
                sides = _sides;
    
                VerticesDistances = newfloat[_sides + 1];
    
                for (int i = 0; i < _sides; i++)VerticesDistances[i] = 1; ;
    
                rotation = 0;
    
            }
    
            public void DrawPolygon(int _sides,float[] _VerticesDistances)
    
            {
    
                sides = _sides;
    
                VerticesDistances =_VerticesDistances;
    
                rotation = 0;
    
            }
    
            public void DrawPolygon(int _sides,float[] _VerticesDistances, float _rotation)
    
            {
    
                sides = _sides;
    
                VerticesDistances =_VerticesDistances;
    
                rotation = _rotation;
    
            }
    
            void Update()
    
            {
    
                size = rectTransform.rect.width;
    
                if (rectTransform.rect.width >rectTransform.rect.height)
    
                    size =rectTransform.rect.height;
    
                else
    
                    size =rectTransform.rect.width;
    
                thickness =(float)Mathf.Clamp(thickness, 0, size / 2);
    
            }
    
     
    
            protected override voidOnPopulateMesh(VertexHelper vh)
    
            {
    
                vh.Clear();
    
     
    
                Vector2 prevX = Vector2.zero;
    
                Vector2 prevY = Vector2.zero;
    
                Vector2 uv0 = new Vector2(0, 0);
    
                Vector2 uv1 = new Vector2(0, 1);
    
                Vector2 uv2 = new Vector2(1, 1);
    
                Vector2 uv3 = new Vector2(1, 0);
    
                Vector2 pos0;
    
                Vector2 pos1;
    
                Vector2 pos2;
    
                Vector2 pos3;
    
                float degrees = 360f / sides;
    
                int vertices = sides + 1;
    
                if (VerticesDistances.Length !=vertices)
    
                {
    
                    VerticesDistances = newfloat[vertices];
    
                    for (int i = 0; i < vertices- 1; i++) VerticesDistances[i] = 1;
    
                }
    
                // last vertex is also the first!
    
                VerticesDistances[vertices - 1] =VerticesDistances[0];
    
                for (int i = 0; i < vertices;i++)
    
                {
    
                    float outer =-rectTransform.pivot.x * size * VerticesDistances[i];
    
                    float inner =-rectTransform.pivot.x * size * VerticesDistances[i] + thickness;
    
                    float rad = Mathf.Deg2Rad * (i* degrees + rotation);
    
                    float c = Mathf.Cos(rad);
    
                    float s = Mathf.Sin(rad);
    
                    uv0 = new Vector2(0, 1);
    
                    uv1 = new Vector2(1, 1);
    
                    uv2 = new Vector2(1, 0);
    
                    uv3 = new Vector2(0, 0);
    
                    pos0 = prevX;
    
                    pos1 = new Vector2(outer * c,outer * s);
    
                    if (fill)
    
                    {
    
                        pos2 = Vector2.zero;
    
                        pos3 = Vector2.zero;
    
                    }
    
                    else
    
                    {
    
                        pos2 = new Vector2(inner *c, inner * s);
    
                        pos3 = prevY;
    
                    }
    
                    prevX = pos1;
    
                    prevY = pos2;
    
                    vh.AddUIVertexQuad(SetVbo(new[]{ pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
    
                }
    
            }
    
       }
    
    }
    

     

    它继承自封装的基类:

    using System;
    
     
    
    namespaceUnityEngine.UI.Extensions
    
    {
    
        public class UIPrimitiveBase :MaskableGraphic, ILayoutElement, ICanvasRaycastFilter
    
        {
    
     
    
            [SerializeField]
    
            private Sprite m_Sprite;
    
            public Sprite sprite { get { returnm_Sprite; } set { if (SetPropertyUtility.SetClass(ref m_Sprite, value))SetAllDirty(); } }
    
     
    
            [NonSerialized]
    
            private Sprite m_OverrideSprite;
    
            public Sprite overrideSprite { get {return m_OverrideSprite == null ? sprite : m_OverrideSprite; } set { if(SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) SetAllDirty(); } }
    
     
    
            // Not serialized until we supportread-enabled sprites better.
    
            internal float m_EventAlphaThreshold =1;
    
            public float eventAlphaThreshold { get{ return m_EventAlphaThreshold; } set { m_EventAlphaThreshold = value; } }
    
     
    
     
    
     
    
            /// <summary>
    
            /// Image's texture comes from theUnityEngine.Image.
    
            /// </summary>
    
            public override Texture mainTexture
    
            {
    
                get
    
                {
    
                    if (overrideSprite == null)
    
                    {
    
                        if (material != null&& material.mainTexture != null)
    
                        {
    
                            returnmaterial.mainTexture;
    
                        }
    
                        return s_WhiteTexture;
    
                    }
    
     
    
                    return overrideSprite.texture;
    
                }
    
            }
    
     
    
            public float pixelsPerUnit
    
            {
    
                get
    
                {
    
                    float spritePixelsPerUnit =100;
    
                    if (sprite)
    
                        spritePixelsPerUnit =sprite.pixelsPerUnit;
    
     
    
                    float referencePixelsPerUnit =100;
    
                    if (canvas)
    
                        referencePixelsPerUnit =canvas.referencePixelsPerUnit;
    
     
    
                    return spritePixelsPerUnit /referencePixelsPerUnit;
    
                }
    
            }
    
     
    
     
    
            protected UIVertex[] SetVbo(Vector2[]vertices, Vector2[] uvs)
    
            {
    
                UIVertex[] vbo = new UIVertex[4];
    
                for (int i = 0; i <vertices.Length; i++)
    
                {
    
                    var vert = UIVertex.simpleVert;
    
                    vert.color = color;
    
                    vert.position = vertices[i];
    
                    vert.uv0 = uvs[i];
    
                    vbo[i] = vert;
    
                }
    
                return vbo;
    
            }
    
     
    
     
    
            #region ILayoutElement Interface
    
     
    
            public virtual voidCalculateLayoutInputHorizontal() { }
    
            public virtual voidCalculateLayoutInputVertical() { }
    
     
    
            public virtual float minWidth { get {return 0; } }
    
     
    
            public virtual float preferredWidth
    
            {
    
                get
    
                {
    
                    if (overrideSprite == null)
    
                        return 0;
    
                    returnoverrideSprite.rect.size.x / pixelsPerUnit;
    
                }
    
            }
    
     
    
            public virtual float flexibleWidth {get { return -1; } }
    
     
    
            public virtual float minHeight { get {return 0; } }
    
     
    
            public virtual float preferredHeight
    
            {
    
                get
    
                {
    
                    if (overrideSprite == null)
    
                        return 0;
    
                    returnoverrideSprite.rect.size.y / pixelsPerUnit;
    
                }
    
            }
    
     
    
            public virtual float flexibleHeight {get { return -1; } }
    
     
    
            public virtual int layoutPriority { get{ return 0; } }
    
     
    
            #endregion
    
     
    
            #region ICanvasRaycastFilter Interface
    
            public virtual boolIsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    
            {
    
                if (m_EventAlphaThreshold >= 1)
    
                    return true;
    
     
    
                Sprite sprite = overrideSprite;
    
                if (sprite == null)
    
                    return true;
    
     
    
                Vector2 local;
    
               RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform,screenPoint, eventCamera, out local);
    
     
    
                Rect rect = GetPixelAdjustedRect();
    
     
    
                // Convert to have lower leftcorner as reference point.
    
                local.x += rectTransform.pivot.x *rect.width;
    
                local.y += rectTransform.pivot.y *rect.height;
    
     
    
                local = MapCoordinate(local, rect);
    
     
    
                // Normalize local coordinates.
    
                Rect spriteRect =sprite.textureRect;
    
                Vector2 normalized = newVector2(local.x / spriteRect.width, local.y / spriteRect.height);
    
     
    
                // Convert to texture space.
    
                float x = Mathf.Lerp(spriteRect.x,spriteRect.xMax, normalized.x) / sprite.texture.width;
    
                float y = Mathf.Lerp(spriteRect.y,spriteRect.yMax, normalized.y) / sprite.texture.height;
    
     
    
                try
    
                {
    
                    returnsprite.texture.GetPixelBilinear(x, y).a >= m_EventAlphaThreshold;
    
                }
    
                catch (UnityException e)
    
                {
    
                    Debug.LogError("UsingclickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read." + e.Message + " Also make sure to disable sprite packing for thissprite.", this);
    
                    return true;
    
                }
    
            }
    
     
    
            /// <summary>
    
            /// Return image adjusted position
    
            /// **Copied from Unity's Imagecomponent for now and simplified for UI Extensions primatives
    
            /// </summary>
    
            /// <paramname="local"></param>
    
            /// <paramname="rect"></param>
    
            /// <returns></returns>
    
            private Vector2 MapCoordinate(Vector2local, Rect rect)
    
            {
    
                Rect spriteRect = sprite.rect;
    
                    return new Vector2(local.x *spriteRect.width / rect.width, local.y * spriteRect.height / rect.height);
    
            }
    
     
    
            Vector4 GetAdjustedBorders(Vector4border, Rect rect)
    
            {
    
                for (int axis = 0; axis <= 1;axis++)
    
                {
    
                    float combinedBorders =border[axis] + border[axis + 2];
    
                    if (rect.size[axis] <combinedBorders && combinedBorders != 0)
    
                    {
    
                        float borderScaleRatio =rect.size[axis] / combinedBorders;
    
                        border[axis] *=borderScaleRatio;
    
                        border[axis + 2] *=borderScaleRatio;
    
                    }
    
                }
    
                return border;
    
            }
    
     
    
            #endregion
    
     
    
     
    
        }
    
    }
    

     

     

    将脚本拖拽到  Canvas 下的一个空对象上。

                然后设置参数(六边形,随便着色、为了和背景图一直旋转了一下,然后就是六个值的设定)

     

    效果就和一开始的截图一样了!

     

     

     

    提到的  OnFillVBO  的实现方式(这个方法是 Unity5.3 之前的版本和 SetNativeSize一个原理)

    file:///D:/Program%20Files/Unity%205.2.0b4/Unity/Editor/Data/Documentation/en/ScriptReference/UI.Graphic.html

     

    using UnityEngine;
    
    using UnityEngine.UI;
    
    using System.Collections.Generic;
    
     
    
     
    
    [ExecuteInEditMode]
    
    public class SimpleImage : Graphic 
    
    {
    
     
    
    protected override voidOnFillVBO (List<UIVertex> vbo)
    
    {
    
    Vector2 corner1 =Vector2.zero;
    
    Vector2 corner2 =Vector2.zero;
    
     
    
    corner1.x = 0f;
    
    corner1.y = 0f;
    
    corner2.x = 1f;
    
    corner2.y = 1f;
    
     
    
    corner1.x -=rectTransform.pivot.x;
    
    corner1.y -=rectTransform.pivot.y;
    
    corner2.x -=rectTransform.pivot.x;
    
    corner2.y -=rectTransform.pivot.y;
    
     
    
    corner1.x *=rectTransform.rect.width;
    
    corner1.y *=rectTransform.rect.height;
    
    corner2.x *=rectTransform.rect.width;
    
    corner2.y *=rectTransform.rect.height;
    
     
    
    vbo.Clear();
    
     
    
    UIVertex vert =UIVertex.simpleVert;
    
     
    
    vert.position = newVector2(corner1.x, corner1.y);
    
    vert.color = color;
    
    vbo.Add(vert);
    
     
    
    vert.position = newVector2(corner1.x, corner2.y);
    
    vert.color = color;
    
    vbo.Add(vert);
    
     
    
    vert.position = newVector2(corner2.x, corner2.y);
    
    vert.color = color;
    
    vbo.Add(vert);
    
     
    
    vert.position = newVector2(corner2.x, corner1.y);
    
    vert.color = color;
    
    vbo.Add(vert);
    
    }
    
    }
    

     

     

     










沪ICP备19023445号-2号
友情链接