读一读

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相对原点处了。


GameObject go = new GameObject("Panels",typeof(RectTransform));


第一个参数是名字,第二个后面的是要给空物体添加的组件,这里天剑RectTransform,RectTransform就会替代Transform组件。


如果单单的调用点击的方法,按钮被点击的效果是不会被看到的。

private void OnGUI()
{
    if (GUILayout.Button("Button Click")) {
        // ExecuteEvents.Execute<IPointerClickHandler>(button.gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerClickHandler);
        //会有按钮点击的效果出现
        ExecuteEvents.Execute<ISubmitHandler>(button.gameObject , 
        new PointerEventData(EventSystem.current), ExecuteEvents.submitHandler);
    }

    if (GUILayout.Button("Image Click")) {
        //单单就是触发点击事件,添加了EventTrigger的Image
        ExecuteEvents.Execute<IPointerClickHandler>(img.gameObject , 
        new PointerEventData(EventSystem.current), ExecuteEvents.pointerClickHandler);
    }
}

ExecuteEvents等类都是在UnityEngine.EventSystems命名空间中


EventTrigger组件类在UnityEngine.EventSystems命名空间中

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public Image img;

void Start () {
    EventTrigger trigger = img.GetComponent<EventTrigger>();
    trigger.triggers = new List<EventTrigger.Entry>();
    EventTrigger.Entry entry = new EventTrigger.Entry();
    entry.eventID = EventTriggerType.PointerClick;
    UnityAction<BaseEventData> action = new UnityAction<BaseEventData>(OnImageClick);
    entry.callback.AddListener(action);
    trigger.triggers.Add(entry);
}

private void OnImageClick(BaseEventData data)
{
    Debug.Log("图片点击");
}

可以看出,EventTrigger可以有一个List的触发器,每一个触发器Entry都有它的类型eventID,和被触发后的回调方法callback

回调方法是一个unity的委托方法(在UnityEngine.Events命名空间),参数必须是BaseEventData


如果要使被遮挡的按钮都可以点击触发事件的发生。

  1. 可以将遮挡的元素设置为按钮的子元素,但是整个被遮挡的物体都会被当成了按钮了。

  2. 在遮挡的元素上添加Canvas Group组件,取消勾选Blocks Raycasts,忽略射线检测。

  3. 在遮挡元素上添加UIFilter,重写过滤射线检测。

using UnityEngine;

public class UIFilter : MonoBehaviour, ICanvasRaycastFilter
{
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        return false;
    }
}

通过属性horizontalNormalizedPosition和verticalNormalizedPosition可以知道滑动到的区域。

范围都是[0,1],就是将垂直或则水平可滑动的区域划分为[0,1],0为开始,1为结束

可以用来将区域划分为页,注意1表示的是最后一页,所以每页的长度其实是1/(总页数-1),也就是把1-0的区域划分为(总页数-1)页。


blob.png

blob.png


Scroll添加上Scroll Rect的脚本,设置其的content为Content,Viewport设置为view

view就是遮罩层,添加Mask,能够看到东西的区域,主要的位置自适应都通过设置它来解决。

Content就是要存放物体的区域了。必须大过view层才有实际滑动的需要。主要要关注的就是长度和起点的位置了,起点设置好view层并将Content放置在view的区域就行了,长度的话可以根据类容来设置,没有长度是划不动的。


启动:调用单例模式的UIManager的Init()方法。初始化三个字典,存放panel路径,panel实例,正在显示的panel,都为空。调用解析json方法,json存储了panelPrefab的路径,存储到panel路径字典中。初始化panel的父节点和Menu,生成一个存放panel实例的父节点,添加脚本Menu,Menu会通过panel的名字加上特定的规范去搜索打开这个panel的按钮,如果找到了,添加监听事件。

Panel的打开,Panel需要一个Panel类需要继承自BasePanel的,Menu类已经为特定的按钮添加了打开Panel的事件,此时,直接点击按钮,就会调用UIManager的PushPanel方法,这个方法首先去panel实例中获取panel,获取到panle后,判断它是否在显示,如果在显示,则调用BasePanel的OnFocus方法,将它放到最前面,如果没有显示,则调用OnEnter方法,表示出来(可以做动画)。

Panel的关闭,照理说每个Panel都会有一个关闭按钮,在子物体里面命名为CloseBtn,BasePanel会自动的找到它,并添加关闭事件OnExit,实现关闭。

Panel的层次和拖拽,BasePanel实现了UGUI的事件接口,继承自BasePanel都可以自由的拖拽,点击选择到最上层。


public class BasePanel : IDragHandler,IBeginDragHandler {
       
        private Vector2 prePosition;
        
        public void OnDrag (PointerEventData eventData){
        	Vector2 offset = eventData.position - prePosition;
        	this.transform.localPosition += new Vector3(offset.x,offset.y,0f);
        	prePosition = eventData.position;
        }
        
        public void OnBeginDrag (PointerEventData eventData){
        	prePosition = eventData.position;
        }

}


通过两次拖拽位置作比较,算出偏移多少,直接让目标对象也偏移多少,当然是本地坐标作移动