[Unity] 新手引导3 - 镂空遮罩

作者: _Walker__ | 来源:发表于2017-06-19 17:35 被阅读1614次
效果

  实现的效果就是上图的样子,先有一个直观的感受,才好理解这里写得是个什么东西~
  HollowOutMask刚做出来的时候挺开心,觉得用到引导上是个挺好的工具。但是后面测试时发现一个严重的问题,它就变的有些鸡肋了Orz……

1、核心说明

这个组件做了两件事情:表现上镂空一块区域;不拦截镂空范围上的事件

1.1 镂空显示

这里的做法是,自己创建有8个顶点的Mesh,内外边界都是四边形(矩形)。只生成内、外边之间的Mesh,内层矩形就产生了镂空效果(高亮部分)。
  外层的4个顶点,是组件自身RectTransform的四个顶点;内层的4个顶点,使用镂空目标(_target)RectTransform的四个顶点。确定内层的顶点的时候需要注意,多数情况下_target和HollowOutMask都不在同一个本地坐标空间,所以需要使用CalculateRelativeRectTransformBounds计算出HollowOutMask空间下坐标。
  这种镂空的表现,可以稍稍提高下性能。因为镂空的位置不参与渲染,Overdraw会降低。

1.2 事件穿透

UGUI提供了ICanvasRaycastFilter接口,我们实现IsRaycastLocationValid方法,就可以很方便的控制,HollowOutMask是否要拦截下在某一点触发的事件。

2、代码

/// <summary>
/// 实现镂空效果的Mask组件
/// </summary>
public class HollowOutMask : MaskableGraphic, ICanvasRaycastFilter
{
    [SerializeField]
    private RectTransform _target;

    private Vector3 _targetMin = Vector3.zero;
    private Vector3 _targetMax = Vector3.zero;

    private bool _canRefresh = true;
    private Transform _cacheTrans = null;

    /// <summary>
    /// 设置镂空的目标
    /// </summary>
    public void SetTarget(RectTransform target)
    {
        _canRefresh = true;
        _target = target;
        _RefreshView();
    }

    private void _SetTarget(Vector3 tarMin, Vector3 tarMax)
    {
        if (tarMin == _targetMin && tarMax == _targetMax)
            return;
        _targetMin = tarMin;
        _targetMax = tarMax;
        SetAllDirty();
    }

    private void _RefreshView()
    {
        if(!_canRefresh) return;
        _canRefresh = false;

        if (null == _target)
        {
            _SetTarget(Vector3.zero, Vector3.zero);
            SetAllDirty();
        }
        else
        {
            Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(_cacheTrans, _target);
            _SetTarget(bounds.min, bounds.max);
        }
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (_targetMin == Vector3.zero && _targetMax == Vector3.zero)
        {
            base.OnPopulateMesh(vh);
            return;
        }
        vh.Clear();

        // 填充顶点
        UIVertex vert = UIVertex.simpleVert;
        vert.color = color;

        Vector2 selfPiovt = rectTransform.pivot;
        Rect selfRect = rectTransform.rect;
        float outerLx = -selfPiovt.x*selfRect.width;
        float outerBy = -selfPiovt.y*selfRect.height;
        float outerRx = (1 - selfPiovt.x)*selfRect.width;
        float outerTy = (1 - selfPiovt.y)*selfRect.height;
        // 0 - Outer:LT
        vert.position = new Vector3(outerLx, outerTy);
        vh.AddVert(vert);
        // 1 - Outer:RT
        vert.position = new Vector3(outerRx, outerTy);
        vh.AddVert(vert);
        // 2 - Outer:RB
        vert.position = new Vector3(outerRx, outerBy);
        vh.AddVert(vert);
        // 3 - Outer:LB
        vert.position = new Vector3(outerLx, outerBy);
        vh.AddVert(vert);

        // 4 - Inner:LT
        vert.position = new Vector3(_targetMin.x, _targetMax.y);
        vh.AddVert(vert);
        // 5 - Inner:RT
        vert.position = new Vector3(_targetMax.x, _targetMax.y);
        vh.AddVert(vert);
        // 6 - Inner:RB
        vert.position = new Vector3(_targetMax.x, _targetMin.y);
        vh.AddVert(vert);
        // 7 - Inner:LB
        vert.position = new Vector3(_targetMin.x, _targetMin.y);
        vh.AddVert(vert);
            
        // 设定三角形
        vh.AddTriangle(4, 0, 1);
        vh.AddTriangle(4, 1, 5);
        vh.AddTriangle(5, 1, 2);
        vh.AddTriangle(5, 2, 6);
        vh.AddTriangle(6, 2, 3);
        vh.AddTriangle(6, 3, 7);
        vh.AddTriangle(7, 3, 0);
        vh.AddTriangle(7, 0, 4);
    }

    bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 screenPos, Camera eventCamera)
    {
        if (null == _target) return true;
        // 将目标对象范围内的事件镂空(使其穿过)
        return !RectTransformUtility.RectangleContainsScreenPoint(_target, screenPos, eventCamera);
    }

    protected override void Awake()
    {
        base.Awake();
        _cacheTrans = GetComponent<RectTransform>();
    }

#if UNITY_EDITOR
    void Update()
    {
        _canRefresh = true;
        _RefreshView();
    }
#endif
}

最后说一下,把它变成鸡肋的问题……
  引导过程中通常都会有领奖的地方,而且是在一个可滑动的奖励列表里领取其中一个。HollowOutMask把高亮区域的所有事件都透过去了,使得这些地方玩家可以拖动列表,然后产生了两个问题。

  • 表现上的穿帮:奖励被拖出显示区域后,引导的高亮区域也跟着出去了
  • 引导卡死:一般项目中(为了提高性能)都会使用Wrap的方式做道具、奖励的列表。在这种方式下,奖励被拖出显示区域后会被deactive,玩家就不能把它再拖回来,也就没法领奖,然后引导卡死在这里Orz……

相关文章

  • [Unity] 新手引导3 - 镂空遮罩

    实现的效果就是上图的样子,先有一个直观的感受,才好理解这里写得是个什么东西~HollowOutMask刚做出来的时...

  • Egret游戏实现反遮罩,镂空操作实例

    游戏中反遮罩常用于场景切换、新手引导镂空高亮区域等。 使用Egret开发,主要用到橡皮擦功能,即混合模式egret...

  • 引导页面的蒙版镂空和屏幕适配

    作者:饶尧;标签: 引导页面的蒙版镂空和屏幕适配,技巧 需求 实现一个遮罩蒙版引导 实现难点 镂空蒙版实现方案 箭...

  • 新手引导系统

    新手引导是游戏中必不可少的系统。 原理:1.添加一个灰色的遮罩层2.高亮显示引导玩家的内容,比如需要点击的按钮3....

  • 自适应软键盘的Dialog以及监听软键盘弹起

    最近项目中遇到一个需求:新手引导。跟一般的新手引导没有什么太大区别,思路都是搞一个带阴影的遮罩层,然后在上边儿给一...

  • iOS中镂空效果

    最近开发的app增加了一个新手引导的需求。为了实现这个需求需要界面镂空掉一部分。现在就说下iOS中实现界面镂空的...

  • 史上最轻量级的新手引导库

    这是一款轻量级的新手引导库,能够快速为任何一个 View 创建一个遮罩层,支持单个页面,多个引导提示,支持为高亮区...

  • 联想--次留影响因素

    1、新手引导 新手引导的分节点流失统计 不同新手引导方案的次留比较 2、机型适配 次日流失用户的机型分布 3、美术...

  • cocos2dx遮罩

    遮罩是很多地方都能用到的东西,新手教程,截图等等,都能用遮罩来实现。 遮罩的原理知其然,知其所以然。遮罩其实可以理...

  • iOS - 镂空、裁剪效果实现

    裁剪部分区域 利用 mask view 实现,遮罩层实现 UIView 实现 CALayer 实现 镂空部分区域 ...

网友评论

    本文标题:[Unity] 新手引导3 - 镂空遮罩

    本文链接:https://www.haomeiwen.com/subject/fjkgqxtx.html