关于UGUI里按钮用动画实现点击缩小后,按住按钮边缘会不停抖动的问题

3 篇文章 0 订阅

项目里给很多按钮做了一个 按下缩小的处理。 用Animation做的。
测试觉得下面的是有问题的,按住边缘就会不停的抖啊抖:

解决方案肯定个旧市, 按钮整体缩放时,点击区域不要缩放。
目里已经做了很多UI了,要全部重新制作一套规则不太可能。
想到一个方案 按钮父节点下挂一个点击区域, 按钮缩小时,这个区域反比放大。

 

这样对原来UI的改动最小。

不过项目里还是有很多这样按钮,需要做一个工具批量添加这个节点。
牵涉到如何批量修改prefab,并且对这个prefab增加一个节点的处理。
代码如下:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class AddBtnScaleRaycast : EditorWindow
{
    static List<Object> errorObjList = new List<Object>();

    const string BtnHitAreaName = "BtnHitArea";
    delegate void PrefabHandler(GameObject prefab);
    /// <summary>
    /// 给缩放按钮添加一个固定大小的点击区域
    /// </summary>
    [MenuItem("Tools/Art/给缩放按钮添加点击区域", false, 1000)]
    static void OptimizeNullAnimator()
    {
        HandlePrefabs(delegate (GameObject go)
        {
            Animator[] animators = go.GetComponentsInChildren<Animator>(true);
            if (animators == null || animators.Length == 0)
            {
                return;
            }
            var inst_go = PrefabUtility.InstantiatePrefab(go) as GameObject;    //直接在prefab上挂 子节点 SetParent时会报错, 需要实例化一个对象出来 - -
            animators = inst_go.GetComponentsInChildren<Animator>(true);
            bool modify = false;
            foreach (var anim in animators)
            {
                if (anim.runtimeAnimatorController != null)
                {
                    var name = anim.runtimeAnimatorController.name;
                    if (name == "FrameButton" || name == "NormalButton" || name == "ScaleButton")    //项目里一些缩放按钮用到的动画控制器名字
                    {
                        Transform _bht_tf = anim.transform.Find(BtnHitAreaName);
                        if (_bht_tf == null)     //不存在这个子节点,则自动在下面添加一个 BtnHitAreaName 节点
                        {
                            //先判断按钮节点上有没有图片? 有则上面的点击接收可以去掉了,用新加的节点来判断  : 如果没有图片则是特殊情况要特殊处理
                            var img = anim.gameObject.GetComponent<MaskableGraphic>();  //不一定图片,可能其他点击脚本有就行
                            if (img != null)
                            {
                                if (img.enabled == false)
                                {
                                    Debug.LogErrorFormat(go, "prefab {0}, 这个{1}节点Image enabled 为false,记得确认一下", go.name, anim.gameObject.name);
                                }
                                else if (img.raycastTarget == false)
                                {
                                    Debug.LogErrorFormat(go, "prefab {0}, 这个{1}节点Image raycastTarget 已经为false,记得确认一下", go.name, anim.gameObject.name);
                                }
                                else if (img is Empty4Raycast)   //放在后面直接删除
                                {
                                    //img.enabled = false;    //点击区域可以移除
                                    Debug.LogWarningFormat(go, "【提示】 prefab {0}, 这个{1}节点 Empty4Raycast脚本,用BtnHitArea子节点代替了,原脚本移除了!!!", go.name, anim.gameObject.name);
                                }
                                img.raycastTarget = false;
                            }
                            else
                            {
                                Debug.LogErrorFormat(go, "prefab {0}, 这个{1}节点没有Image图片,记得确认一下", go.name, anim.gameObject.name);
                            }

                            //新加节点,绑脚本,社布局
                            var _bht_go = new GameObject();
                            _bht_go.transform.SetParent(anim.transform);

                            _bht_go.name = BtnHitAreaName;

                            _bht_go.AddComponent<Empty4Raycast>();  //让其能被点击

                            var rt = _bht_go.GetComponent<RectTransform>();
                            if (rt == null)
                            {
                                rt = _bht_go.AddComponent<RectTransform>(); //添加一个RectTransform
                            }

                            bool set_full_size = true;
                            if (img == null)
                            {
                                //按钮上没有图片节点,则尝试找它的子节点第一个有效Image, 尺寸设成一样
                                for (int i = 0; i < anim.transform.childCount; ++i)
                                {
                                    var child = anim.transform.GetChild(i);
                                    var child_img = child.GetComponent<Image>();
                                    var img_rt = child.GetComponent<RectTransform>();
                                    if (child_img != null && img_rt != null && child_img.enabled && child_img.raycastTarget)
                                    {
                                        child_img.raycastTarget = false;    //这个image的点击关闭

                                        //尝试新节点布局和这个img一致
                                        rt.anchorMin = img_rt.anchorMin;
                                        rt.anchorMax = img_rt.anchorMax;
                                        rt.pivot = img_rt.pivot;                //沿用原来的对齐方式
                                        rt.anchoredPosition = img_rt.anchoredPosition;
                                        rt.sizeDelta = img_rt.sizeDelta;
                                        rt.localScale = img_rt.localScale;
                                        rt.localRotation = img_rt.localRotation;


                                        if (img_rt.pivot != Vector2.one * 0.5f) //没中心对齐可能要再处理一下。
                                        {
                                            //rt.pivot = Vector2.one - img_rt.pivot;  //这个还是需要手动调整
                                            Debug.LogErrorFormat(go, "【【手动调整提示】】prefab {0}, 这个{1}节点没有中心对齐", go.name, anim.gameObject.name);
                                        }

                                        set_full_size = false;
                                        break;
                                    }
                                }
                            }

                            if (set_full_size)
                            {
                                //布局设为自动撑满父节点的面积
                                rt.anchorMin = Vector2.zero;
                                rt.anchorMax = Vector2.one;
                                rt.anchoredPosition = Vector2.zero;     //这里要用anchoredPosition 对齐,否则用LocalPosition 不同的布局会有差异
                                rt.sizeDelta = Vector2.zero;
                                rt.localScale = Vector3.one;
                                var parent_rt = anim.gameObject.GetComponent<RectTransform>();
                                if (parent_rt != null)
                                {
                                    rt.pivot = parent_rt.pivot;         //pivot对齐方式要和父节点一致,这样缩放时位置不会偏。
                                }
                            }
                            if (img != null && img is Empty4Raycast)
                            {
                                DestroyImmediate(img);
                            }
                            modify = true;


                        }
                    }
                }
            }

            if (modify)
            {
                errorObjList.Add(go);
                Debug.Log("prefab " + go.name + " 已修改", go);
                PrefabUtility.ReplacePrefab(inst_go, go, ReplacePrefabOptions.ConnectToPrefab);
            }
            DestroyImmediate(inst_go);  // 实例的对象要销毁
        });
    }

    static string path = Path.Combine(Application.dataPath, "log.txt");
    public static void Log(string content)
    {
        File.AppendAllText(path, content + "\r\n", Encoding.UTF8);
    }


    static void HandlePrefabs(PrefabHandler handler)
    {
        List<Object> objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets).ToList().Where(a => Path.GetExtension(AssetDatabase.GetAssetPath(a)).ToLower() == ".prefab").ToList();
        if (objs.Count == 0)
        {
            Debug.Log("请先选中一个要处理的父目录");
            return;
        }
        errorObjList.Clear();

        int index = 0;
        EditorApplication.update = delegate ()
        {
            Object obj = objs[index];
            EditorUtility.DisplayProgressBar("拼命处理中", string.Format("prefab:{0} 进度:{1}/{2}", obj.name, index, objs.Count), (float)index / objs.Count);
            handler(obj as GameObject);
            if (++index >= objs.Count)
            {
                EditorUtility.ClearProgressBar();
                EditorApplication.update = null;
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                if (errorObjList.Count > 0)
                {
                    AssetListWindow.Show(errorObjList, "已处理的列表");
                }
                Debug.Log("Finish");
            }
        };
    }
}



另外附带一个实现空白点击的替换透明Image 方案的脚本:
 

namespace UnityEngine.UI
{
    /// <summary>
    /// 代替空的Image接受点击
    /// </summary>
    public class Empty4Raycast : MaskableGraphic
    {
        protected Empty4Raycast()
        {
            useLegacyMeshGeneration = false;
        }

        protected override void OnPopulateMesh(VertexHelper toFill)
        {
            toFill.Clear();
        }
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值