读一读

using UnityEngine;
using UnityEngine.UI;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class UIGray : MonoBehaviour {

    private bool _isGray = false;
    public bool isGray
    {
        get { return _isGray; }
        set
        {
            if (_isGray != value)
            {
                _isGray = value;
                SetGray(_isGray);
            }
        }
    }

    static private Material _defaultGrayMaterial;
    static private Material grayMaterial
    {
        get
        {
            if (_defaultGrayMaterial == null)
            {
                _defaultGrayMaterial = new Material(Shader.Find("UI/MyGray"));
            }
            return _defaultGrayMaterial;
        }
    }

    public void SetGray(bool isGray)
    {
        Image[] images = transform.GetComponentsInChildren<Image>();
        for (int i = 0; i < images.Length; i++)
        {
            Image g = images[i];
            if (isGray)
            {
                g.material = grayMaterial;
            }
            else
            {
                g.material = null;
            }
        }
    }

#if UNITY_EDITOR //提供编辑器预览
    [CustomEditor(typeof(UIGray))]
    public class UIGrayInspector : Editor
    {
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
            UIGray gray = target as UIGray;
            gray.isGray = GUILayout.Toggle(gray.isGray, "Is Gray");
            if (GUI.changed)
            {
                EditorUtility.SetDirty(target);
            }
        }
    }
#endif
}


Shader "UI/MyGray"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                float gray = dot(col.xyz, float3(0.299, 0.587, 0.114));
                col.xyz = float3(gray, gray, gray);

                return col;
            }
            ENDCG
        }
    }
}


图片.png


相对于以前的自动打包图集,2017提供了可以自行打包和管理图集,只需要在Project视图中Create创建Sprite Atlas,选择图片或则整个文件夹打包到这个图集中。

还可以创建图集的变种,就是新建一个图集,复制事先打包好的图集,设置缩放,使图集变小,优化内存。

图片.png

图片.png


二、图集的加载

using UnityEditor;
using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.UI;

public class Script_8 : MonoBehaviour {

    Sprite sprite = null;
    Sprite sprite1 = null;
    Image image = null;

    void Start () {
        image = this.transform.parent.GetChild(0).GetComponent<Image>();

        SpriteAtlas atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>("Assets/8/New Sprite Atlas.spriteatlas");
        SpriteAtlas atlasMin = AssetDatabase.LoadAssetAtPath<SpriteAtlas>("Assets/8/New Sprite Atlas 1.spriteatlas");

        sprite = atlas.GetSprite("TIM图片20190401133117");
        sprite1 = atlasMin.GetSprite("TIM图片20190401133117");

    }


    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (image.sprite != sprite)
                image.sprite = sprite;
            else
                image.sprite = sprite1;
        }
    }
}

在Layout根结点添加Horizontal Layout Group和Content Size Fiter组件,设置好参数。添加需要布局的物体到根节点下面。

图片.png

为Text物体也添加一个Content Size Fitter组件,设置你要适应的方向,因为这个物体的大小也是需要重新计算的,动态设置文字长度。

using UnityEngine;
using UnityEngine.UI;

public class Script_8 : MonoBehaviour {
    
    void Start () {
        GetComponent<Text>().text = "吃ua谁会丢爱u恢复到大街上的风格i偶会若干个的疯狂购ID防静电服";
        //Layout的根节点,立刻重新计算布局
        LayoutRebuilder.ForceRebuildLayoutImmediate(this.transform.parent.GetComponent<RectTransform>());
    }
}

图片.png


在Image中添加AspectRatioFitter,就会将图片全屏显示在父物体中。

WidthControlsHeight,让Height随着Width自动调节

HeightControlsWidth,让Width随着Height自动调节

FitInParent,宽度、高度、位置和锚点都会被自动调整,以使得该矩形拟合父物体的矩形内,同时保持宽高比例

EnvelopeParent,宽度、高度、位置和锚点都会被自动调整,以使得该矩形覆盖父物体的整个区域,同时保持宽高比

AspectRatio,为宽高比例

using UnityEngine;
using UnityEngine.UI;

public class Script_aspect : MonoBehaviour {
    
    void Start () {
        Rect rect = this.GetComponent<Image>().sprite.rect;
        AspectRatioFitter aspect = this.GetComponent<AspectRatioFitter>();
        aspect.aspectMode = AspectRatioFitter.AspectMode.EnvelopeParent;
        aspect.aspectRatio = rect.width / rect.height;
    }
}

图片.png

图片.png


using UnityEngine;
using UnityEngine.UI;

public class Script_05_11 : MonoBehaviour
{
    //需要聚合的对象(例子中的Unity图标)
    public Image target;
    //Canvas对象
    public Canvas canvas;

    private Vector4 m_Center;
    private Material m_Material;
    private float m_Diameter; // 直径
    private float m_Current =0f;

    Vector3[] corners = new Vector3[4]; 

    void Awake () 
    {

        target.rectTransform.GetWorldCorners (corners); //计算四个角的坐标 左下 左上 右上 右下
        m_Diameter = Vector2.Distance (WordToCanvasPos(canvas,corners [0]), WordToCanvasPos(canvas,corners [2])) / 2f;

        float x =corners [0].x + ((corners [3].x - corners [0].x) / 2f);
        float y =corners [0].y + ((corners [1].y - corners [0].y) / 2f);

        Vector3 center = new Vector3 (x, y, 0f);
        Vector2 position = Vector2.zero;
        //坐标转换到相对canvas下的局部坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, center, canvas.GetComponent<Camera>(), out position);




        center = new Vector4 (position.x,position.y,0f,0f);
        m_Material = GetComponent<Image>().material;
        m_Material.SetVector ("_Center", center);



        (canvas.transform as RectTransform).GetWorldCorners (corners);
        for (int i = 0; i < corners.Length; i++) {
            m_Current = Mathf.Max(Vector3.Distance (WordToCanvasPos(canvas,corners [i]), center),m_Current);
        }

        m_Material.SetFloat ("_Silder", m_Current);
    }


    float yVelocity = 0f;
    void Update () {
        float value = Mathf.SmoothDamp(m_Current, m_Diameter, ref yVelocity, 0.3f);
        if (!Mathf.Approximately (value, m_Current)) {//两个数是否相近
            m_Current = value;
            m_Material.SetFloat ("_Silder", m_Current);
        }
    }

    void OnGUI(){
        if(GUILayout.Button("Test")){
            Awake ();
        }
    }


    Vector2 WordToCanvasPos(Canvas canvas,Vector3 world){
        Vector2 position = Vector2.zero;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, world, canvas.GetComponent<Camera>(), out position);
        return position;
    }
}


Shader "UI/Default_Mask"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        
        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255
 
        _ColorMask ("Color Mask", Float) = 15
 
 
        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
 
 
    //-------------------add----------------------
      _Center("Center", vector) = (0, 0, 0, 0)
      _Silder ("_Silder", Range (0,1000)) = 1000 // sliders
    //-------------------add----------------------
    }
 
    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }
        
        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp] 
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }
 
        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]
 
        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
 
            #include "UnityCG.cginc"
            #include "UnityUI.cginc"
 
            #pragma multi_compile __ UNITY_UI_ALPHACLIP
            
            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };
 
            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
 
            };
            
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            //-------------------add----------------------
            float _Silder;
            float2 _Center;
            //-------------------add----------------------
            v2f vert(appdata_t IN)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(IN);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = IN.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
 
                OUT.texcoord = IN.texcoord;
                
                OUT.color = IN.color * _Color;
                return OUT;
            }
 
            sampler2D _MainTex;
 
            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                
                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif
                //-------------------add----------------------
                   color.a*=(distance(IN.worldPosition.xy,_Center.xy) > _Silder);
                   color.rgb*= color.a;
                   //-------------------add----------------------
                return color;
            
            }
        ENDCG
        }
    }
}


将所有的坐标都转换到相对于Canvas父物体下的局部坐标,统一在同一个坐标系中计算距离。计算目标焦点的直径和中心点等,根据距离修改遮罩的透明度。

GIF.gif


using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class Script_5 : MonoBehaviour,IPointerClickHandler {

    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.LogError("图片点击");
        PassEvent(eventData, ExecuteEvents.pointerClickHandler);
    }

    public void PassEvent<T>(PointerEventData data, ExecuteEvents.EventFunction<T> function) where T : IEventSystemHandler
    {
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(data, results);
        GameObject current = data.pointerCurrentRaycast.gameObject;
        for (int i = 0; i < results.Count; i++)
        {
            if (current != results[i].gameObject)
            {
                ExecuteEvents.Execute(results[i].gameObject, data, function);
                //break;
            }
        }
    }
}

//按钮的脚本
using UnityEngine;

public class Script_5_1 : MonoBehaviour {

    public void OnButtonClick()//方法在Inspector中绑定给了按钮
    {
        Debug.LogError("按钮点击");
    }

}


图片.png

图片.png


UGUI的点击事件是基于射线的。如果不需要响应事件,千万不要在Image和Text组件上勾选RaycastTarget。UI事件会在EventSystem的Update()方法中调用Process时触发。UGUI会遍历屏幕中所有Raycasttarget是true的UI,接着就会发射线,并且排序找到玩家最先触发的UI,再抛出事件给逻辑层去响应,这样无形中就会带来很多开销。我可以通过辅助方法,标记那些勾选了RaycastTarget的ui,这样就可以去发现那些不必要勾选了。

using UnityEngine;
using UnityEngine.UI;

public class ShowRayCatchUI : MonoBehaviour {

#if UNITY_EDITOR
    static Vector3[] fourCorners = new Vector3[4];
    private void OnDrawGizmos()
    {
        foreach (MaskableGraphic g in GameObject.FindObjectsOfType<MaskableGraphic>())
        {
            if (g.raycastTarget)
            {
                RectTransform rectTransform = g.rectTransform;
                rectTransform.GetWorldCorners(fourCorners);
                Gizmos.color = Color.blue;
                for (int i = 0; i < 4; i++)
                    Gizmos.DrawLine(fourCorners[i], fourCorners[(i + 1) % 4]);
            }
        }
    }
#endif
}


图片.png


UGUI的动态字体会动态生成材质,开始是256*256,然后根据字体的使用情况慢慢扩大。直到4096*4096。当文字太多不够放的时候,会触发UGUI内部重建字体贴图命令,接着就可能造成文字花屏了。可以监听重建的事件,然后刷新一下当前场景中的所有字体即可。

using UnityEngine;
using UnityEngine.UI;

public class FontRebuild : MonoBehaviour {

    private Font m_NeedRebuildFont = null;
    
    void Start () {
        Font.textureRebuilt += delegate (Font font)
        {
            m_NeedRebuildFont = font;
            Debug.LogError(font.name);
        };
    }
    
    void Update () {
        if (m_NeedRebuildFont)
        {
            Text[] texts = GameObject.FindObjectsOfType<Text>();
            if (texts != null)
            {
                foreach (Text text in texts)
                {
                    text.FontTextureChanged();
                }
            }
            m_NeedRebuildFont = null;
        }
    }
}

blob.png


原因应该是4边的透明边缘留得太少了,有颜色的像素粘到了边缘,偏移的时候Raw Image用的边缘颜色来填充未定义的区域。

解决办法就是,重新作图,将图片的边缘的透明位置预留出来。


Raw Image修改顶点信息的源码

OnPopulateMesh重写的是从Graphic继承来的方法,使用它可以修改UI元素显示的顶点信息。

protected override void OnPopulateMesh(VertexHelper vh)
{
    Texture tex = mainTexture;
    vh.Clear();
    if (tex != null)
    {
        var r = GetPixelAdjustedRect();
        var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);
        var scaleX = tex.width * tex.texelSize.x;
        var scaleY = tex.height * tex.texelSize.y;
        {
            var color32 = color;
            vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMin * scaleY));
            vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMax * scaleY));
            vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMax * scaleY));
            vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMin * scaleY));

            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(2, 3, 0);
        }
    }
}


Rect的源码看不到了,设置的是x,y,w,z值,但是用的时候是xMin,xMax等的,xMin,yMin就是x和y值,而xMax = xMin+w;yMax = yMin + z。


GameObject go = new GameObject("Panels",typeof(RectTransform));
go.GetComponent<RectTransform>().position = Vector3.zero;
go.transform.SetParent(c.transform,false);


因为ugui的ui物体都是在Canvas下面的,所以一定是需要经过设置父物体的情况的

调节设置父物体不保持世界坐标的位置,这样就大小就不会错乱了

如果位置还没有对,在设置父子关系前,重置位置,这样就会直接会在Canvas相对原点处了。