原因应该是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
如果要使被遮挡的按钮都可以点击触发事件的发生。
可以将遮挡的元素设置为按钮的子元素,但是整个被遮挡的物体都会被当成了按钮了。
在遮挡的元素上添加Canvas Group组件,取消勾选Blocks Raycasts,忽略射线检测。
在遮挡元素上添加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)页。
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; } }
通过两次拖拽位置作比较,算出偏移多少,直接让目标对象也偏移多少,当然是本地坐标作移动