先分享一下,做物品tips时候的穿透问题。
首先,物品tips的关闭规则
UICamer的原理:
UICamer就是通过在触摸/鼠标移动的位置的地方发射射线(就是Unity的Raycast),然后获取射线撞击的碰撞体(collider)信息,然后发射消息(通过Unity的SendMessage函数)给该碰撞体关联的GameObject的所有脚本。
原本的做法是,给tips加一层背景遮罩(遮罩上加上Box Collider),当触发到遮罩Box Collider时,发送关闭tips事件,然后通过UICamera.lastHit.collider.gameObject获取到射线最后的碰撞对象。给这个对象SendMessage("OnClick")
local function onMask()
EventManager:dispatchEvent(EventName.CloseTips)
local hoverobject = nil
local ishit = UICamera.Raycast(Input.mousePosition)
if ishit then
hoverobject = UICamera.lastHit.collider.gameObject
print("-------objectname---",hoverobject)
if UIEventListener.Get(hoverobject).onClick then
print(UIEventListener.Get(hoverobject).onClick)
hoverobject:SendMessage("OnClick")
end
end
end
self:addUIEvent(self.maskBg, onMask)
但是这种做法不能满足文档里面的新需求。
接下来说一下现在的做法,直接把tips的遮罩去掉。
给tips界面预设定一个新的Layer。
然后,Camera里面的Culling Mask增加新的Layer。
这样做的目的是为了让tips界面与其他的UI界面的Layer区分开来。
接下来就是要到TouchManager.lua
这个函数里面会不停刷新点击碰撞事件,
我的处理方法是在这个函数里面加入
if (Input.GetMouseButtonDown(0)) then
if (UICamera.lastHit.collider.gameObject.layer ~= LayerManager.Tips_Layer) and TipsManager.isShow then
EventManager:dispatchEvent(EventName.CloseTips)
end
End
UICamera.lastHit.collider.gameObject.layer是获取射线最后的碰撞对象的Layer,判断不属于LayerManager.Tips_Layer就发送关闭Tips事件。
接着说一下Scroll view里面空白处添加滑动的问题。
如下图,有时候scroll view里面的item不足以填充满当前显示的滚动区域(蓝色框为滚动区域)是,在黄色区域空白区进行拖动的时候,并不会进行滚动。
处理方法是,在scrollviewPanel外面多加一个背景层,
添加
关键一步是,把UIDrag Scroll View脚本的Scroll View设置为panel:
Drag Scroll View的Scroll View属性可以指定特定的带有Scroll View的拖拽层,如果不指定在运行时会自动匹配父节点带有绑定Scroll View的拖拽层。
用NGUI生成ScrollView 有两个重要的类UIScrollview和UIPanel(NGUI Version 3.7.8)
ScrollviewPanel上面挂上UIPanel的作用是用来渲染,当有一块需要特殊处理的模块需要渲染时需要用到UIPanel专门控制。
在UIPanel上有一个Clipping功能,用作裁剪用。
实现可以让某些item显示某些不显示,某些item只显示一部分。
首先,可以看UIPanel的LateUpdate()函数。
UIPanel里面每帧都会更新这个LateUpdate()函数。
LateUpdate()函数里主要看UpdateSelf()和UpdateDrawCalls()
UpdateSelf()
这里会有好几个的Update函数,主要说一下UpdateWidget()。为什么要UpdateWidget
因为NGUI是根据UIWidget来作为渲染单位。
在UIWidget的OnStart()函数中,只做了CreatePanel()操作。
这里有一个panel.AddWidget(this),CreatePanel其实不是真的去Create一个Panel,而是找父节点或者自己节点上的UIPanel,去把自己的Widget添加到Panel的WidgetList中。
一个UIWidget是不会渲染的,所有的控件是通过找到UIPanel,再在UIPanel去渲染。
这就是要UpdateWidget的原因。
接着就是UpdateSelf()里面的FillAllDrawcalls()函数
这个函数是用来合并可以合并的drawcall。
首先,会调用SortWidgets()排序Widget。
这里会根据Depth和Material进行排序
排序之后,会获取每一个widget的Material、Texture和Shader
如果不一样的话就,就调drawCalls.Add(),把这个drawcall加到这个drawcallList中。
接着看这些属性是否为空,如果为空就去Create一个新的DrawCall,
如果不为空就改变这个Drawcall的Depth从depthStart和depthEnd
接着回来最开始的LateUpdate()函数
看到这个地方,
不管是什么渲染队列方式,都是调用UpdateDrawCalls().
直接看到UpdateDrawCalls()里面,
这里会计算出这个transform的位置旋转缩放在显示屏中的各种属性数据还有这个Drawcall的渲染方式队列裁剪方式裁剪范围等。
这就完成了每帧刷新每一个widget的transform和drawcall数据刷新。
为什么可以让某些item显示某些不显示,某些item只显示一部分。
回到刚刚的FillAllDrawCalls()中
这里的dc.UpdateGeometry(),一直跟踪下去,最后会到UIDrawcall的CreateMaterial()这个函数。
这里进行判断,根据设置好的裁剪来选择不同的shader。
由于不同的shader会产生不同的动态材质mDynamicMat
也是由于这个不同的动态材质使得下一步的渲染裁剪得到实现。