UI的实现借鉴Unity 新手引导 遮罩效果_Liam's blog-CSDN博客_unity遮罩
目的:新手引导为独立模块,通过配置表来动态的实现。降低和其他模块的耦合。
配置表设计:
1、引导组表,包含 引导组id,引导id,是否是强引导
2、引导表,包含 id,组id,UI界面的id,引导的UI的Name,遮罩类型,触发条件,触发值,需要执行的动作,执行动作的参数,完成条件,完成后的动作
单个引导的逻辑
1、判断是否触发
2、显示遮罩
3、执行动作
4、判断是否完成
5、执行完成后的动作
主要的几个难点:
1、查找UI
2D的UI通过获取当前的UI的根节点,通过GetChild来获得
/// <summary>
/// 在层级未知的情况下查找子物体
/// </summary>
/// <param name="parentTF">父物体变换组件</param>
/// <param name="childName">子物体名称</param>
/// <returns></returns>
public static Transform GetChild(Transform parentTF, string childName)
{
//在子物体中查找
Transform childTF = parentTF.Find(childName);
if (childTF != null)
{
return childTF;
}
//将问题交由子物体
int count = parentTF.childCount;
for (int i = 0; i < count; i++)
{
childTF = GetChild(parentTF.GetChild(i), childName);
if (childTF != null)
{
return childTF;
}
}
return null;
}
3D的物体
//尽量将路径写全,可以减少查询时间
GameObject.Find(UiName)
对于动态加载的scrollview中的view,可以先找出一个view,然后查找他的父transform下的第几个view
2、遮罩的坐标转换
查找出需要遮罩的UI,显示和点击的透传,都需要转换成UIPos.
//工具类用到的方法
public static Vector2 WorldPosToUIPos(Camera world_cam, Vector3 world_pos, Camera ui_cam, RectTransform ui_rect)
{
Vector2 mouseDown = world_cam.WorldToScreenPoint(world_pos);
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(ui_rect, mouseDown, ui_cam, out localPoint);
return localPoint;
}
public static Vector3[] GetWorldCorners(RectTransform rt)
{
Vector3[] v = new Vector3[4];
rt.GetWorldCorners(v);
return v;
}
下面是lua的代码
--注意self.GuideCarmera,如果是3D的需要传入3D相机,2D的为UI相机
self.maskTargetRTF = targetObj:GetComponent("RectTransform")
local center = Vector4(0, 0, 0, 0)
local tempV3s = LuaUtility.GetWorldCorners(self.maskTargetRTF)
local screenV2s = {} --转换世界坐标到屏幕坐标
for i = 0, 3, 1 do
local sp = LuaUtility.WorldPosToUIPos(self.GuideCarmera, tempV3s[i], self.UICamera, self.UICanvasRTF)
screenV2s[i] = {}
screenV2s[i].x = sp.x
screenV2s[i].y = sp.y
end
--计算中心点
center.x = screenV2s[0].x + (screenV2s[3].x - screenV2s[0].x) / 2
center.y = screenV2s[0].y + (screenV2s[1].y - screenV2s[0].y) / 2
--计算宽高
local width = (screenV2s[3].x - screenV2s[0].x) / 2
local height = (screenV2s[1].y - screenV2s[0].y) / 2
--下面是设置遮罩的UI
--设置中心点
self.material:SetVector("_Center", center)
--设置宽高
self.material:SetFloat("_SliderX", width)
self.material:SetFloat("_SliderY", height)
3、点击透传的问题
首先,需要拦截点击
//实现 lua 点击透传的过滤问题
public class UIFocus : MonoBehaviour, ICanvasRaycastFilter
{
public bool IsFocus = false;
private LuaFuncIsRaycastDelegate isRaycastDelegate;
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (isRaycastDelegate != null)
{
return isRaycastDelegate(sp, eventCamera);
}
return IsFocus;
}
public void SetIsRaycastFunction(LuaFuncIsRaycastDelegate isRaycastDelegate)
{
this.isRaycastDelegate = isRaycastDelegate;
}
}
然后,将这个添加到引导的UI上
self.uiFocus = self.maskImgGo:AddComponent(typeof(CS.UIFocus))
设置回调方法
self.panel.uiFocus:SetIsRaycastFunction(
function(sp, camera)
return self:IsRaycastLocationValid(sp, camera)
end
)
判断方法
--点击是否需要透传
function GuideFormCtrl:IsRaycastLocationValid(sp, eventCamera)
if self.guide ~= nil then
if self.maskTargetRTF then
local isRay = self:IsRaycastInMask(sp)
return not isRay
end
return true
end
return false
end
--点击是否需要透传
function GuideFormCtrl:IsRaycastInMask(sp)
if self.maskTargetRTF then
local isRay =
RectTransformUtility.RectangleContainsScreenPoint(
self.maskTargetRTF,
sp,
(self.GuideCarmera or self.UICamera)
)
return isRay
end
return false
end
总结:
还是需要在其他界面进行埋点,回调发送消息,业务逻辑在新手引导中完成。后续还有问题再记录下