精华内容
下载资源
问答
  • Unity 新手引导

    2018-12-06 16:20:47
    GuideMaskDemo.GuideMaskDemo. 自定义遮罩位置和大小,点击穿透。
  • Unity新手引导

    2020-01-13 20:05:30
    介绍 这里要介绍的是通过着色器(Shader)来实现高亮某一UI 效果 先看效果 一个圆形范围高亮显示,一个矩形.../// 圆形遮罩镂空引导 /// </summary> public class CircleGuidanceController : MonoBeha...

    介绍

    这里要介绍的是通过着色器(Shader)来实现高亮某一UI

    效果

    先看效果

    一个圆形范围高亮显示,一个矩形范围显示

    圆形

    这里先上C#代码,后面会给出Shader的代码

    /// <summary>
    /// 圆形遮罩镂空引导
    /// </summary>
    public class CircleGuidanceController : MonoBehaviour
    {
    	/// <summary>
    	/// 要高亮显示的目标
    	/// </summary>
    	public RectTransform Target;
    	
    	/// <summary>
    	/// 区域范围缓存
    	/// </summary>
    	private Vector3[] _corners = new Vector3[4];
    
    	/// <summary>
    	/// 镂空区域圆心
    	/// </summary>
    	private Vector4 _center;
    
    	/// <summary>
    	/// 镂空区域半径
    	/// </summary>
    	private float _radius;
    
    	/// <summary>
    	/// 遮罩材质
    	/// </summary>
    	private Material _material;
    
    	/// <summary>
    	/// 当前高亮区域的半径
    	/// </summary>
    	private float _currentRadius;
    
    	/// <summary>
    	/// 高亮区域缩放的动画时间
    	/// </summary>
    	private float _shrinkTime = 0.5f;
    
        /// <summary>
        /// 时间渗透组件
        /// </summary>
        private GuidanceEventPenetrate _eventPenetrate;
    
        /// <summary>
        /// 世界坐标向画布坐标转换
        /// </summary>
        /// <param name="canvas">画布</param>
        /// <param name="world">世界坐标</param>
        /// <returns>返回画布上的二维坐标</returns>
        private Vector2 WorldToCanvasPos(Canvas canvas, Vector3 world)
    	{
    		Vector2 position;
    
    		RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform,
    			world, canvas.GetComponent<Camera>(), out position);
    		return position;
    	}
    
        public void SetTarget(RectTransform target,bool isPauseGame, float timer=0.5f)
        {
            this.isPauseGame = isPauseGame;
            Target = target;
            _shrinkTime = timer;
            _eventPenetrate = GetComponent<GuidanceEventPenetrate>();
            if (_eventPenetrate != null)
                _eventPenetrate.SetTarget(Target);
            //获取画布
            Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
            //获取高亮区域的四个顶点的世界坐标
            Target.GetWorldCorners(_corners);
            //计算最终高亮显示区域的半径
            _radius = Vector2.Distance(WorldToCanvasPos(canvas, _corners[0]), WorldToCanvasPos(canvas, _corners[2])) / 2f;
            //计算高亮显示区域的圆心
            float x = _corners[0].x + ((_corners[3].x - _corners[0].x) / 2f);
            float y = _corners[0].y + ((_corners[1].y - _corners[0].y) / 2f);
            Vector3 centerWorld = new Vector3(x, y, 0);
            Vector2 center = WorldToCanvasPos(canvas, centerWorld);
            //设置遮罩材料中的圆心变量
            Vector4 centerMat = new Vector4(center.x, center.y, 0, 0);
            _material = GetComponent<Image>().material;
            _material.SetVector("_Center", centerMat);
            //计算当前高亮显示区域的半径
            RectTransform canRectTransform = canvas.transform as RectTransform;
            if (canRectTransform != null)
            {
                //获取画布区域的四个顶点
                canRectTransform.GetWorldCorners(_corners);
                //将画布顶点距离高亮区域中心最远的距离作为当前高亮区域半径的初始值
                foreach (Vector3 corner in _corners)
                {
                    _currentRadius = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, corner), center), _currentRadius);
                }
            }
            _material.SetFloat("_Slider", _currentRadius);
    
            if (mShink != null)
            {
                StopCoroutine(mShink);
            }
            if (isPauseGame)
            {
                mShink=StartCoroutine(StartShink());
            }
        }
        private Coroutine mShink;
        private bool isPauseGame;
        /// <summary>
        /// 脱离timeScale影响
        /// </summary>
        /// <returns></returns>
        IEnumerator StartShink()
        {
            float length = _currentRadius - _radius;
    
            for (float i = 0; i < _shrinkTime; i+= Time.unscaledDeltaTime)
            {
                float scale = i / _shrinkTime;
                if (scale >= 1) scale = 1;
                float value = _currentRadius - (length * scale);
                _material.SetFloat("_Slider", value);
                yield return 0;
            }
            _material.SetFloat("_Slider", _radius);
        }
    
    	/// <summary>
    	/// 收缩速度
    	/// </summary>
    	private float _shrinkVelocity = 0f;
    
    	private void Update()
    	{
            if (isPauseGame) return;
    
            if (Target == null) return;
    
    		//从当前半径到目标半径差值显示收缩动画
    		float value = Mathf.SmoothDamp(_currentRadius, _radius, ref _shrinkVelocity, _shrinkTime);
                 
    		if (!Mathf.Approximately(value, _currentRadius))
    		{
    			_currentRadius = value;
    			_material.SetFloat("_Slider", _currentRadius);
    		}
    	}
    
        public void ClearTaget()
        {
            Target = null;
        }
    }

    这里由于我项目中有的引导会把游戏暂停(timeScale),这样会导致收缩动画也被暂停,所以我增加了一个协程通道,通过协程来播放收缩动画。下面是圆形着色器代码:

    
    Shader "UI/BeginnerGuidance/CircleGuidance"
    {
        Properties
        {
            [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
            _Color ("Tint", Color) = (1,1,1,1)
    
            _StencilComp ("Stencil Comparison", Float) = 8
            _Stencil ("Stencil ID", Float) = 0
            _StencilOp ("Stencil Operation", Float) = 0
            _StencilWriteMask ("Stencil Write Mask", Float) = 255
            _StencilReadMask ("Stencil Read Mask", Float) = 255
    
            _ColorMask ("Color Mask", Float) = 15
    
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
            
            _Center("Center",vector) = (0,0,0,0)
            _Slider("Slider",Range(0,1500)) = 1500
        }
    
        SubShader
        {
            Tags
            {
                "Queue"="Transparent"
                "IgnoreProjector"="True"
                "RenderType"="Transparent"
                "PreviewType"="Plane"
                "CanUseSpriteAtlas"="True"
            }
    
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
            }
    
            Cull Off
            Lighting Off
            ZWrite Off
            ZTest [unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask [_ColorMask]
    
            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0
    
                #include "UnityCG.cginc"
                #include "UnityUI.cginc"
    
                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP
    
                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color    : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float2 _Center;
                float _Slider;
    
                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
    
                    OUT.texcoord = v.texcoord;
    
                    OUT.color = v.color * _Color;
                    return OUT;
                }
    
                sampler2D _MainTex;
    
                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    
                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif
    
                    #ifdef UNITY_UI_ALPHACLIP
                    clip (color.a - 0.001);
                    #endif
                    
                    color.a *= (distance(IN.worldPosition.xy, _Center.xy) > _Slider);
                    color.rgb *= color.a;
    
                    return color;
                }
            ENDCG
            }
        }
    }
    

    矩形

    代码基本都有注释,挂载在UI上即可,UI材质球着色器使用对应的Shader

    /// <summary>
    /// 矩形引导组件
    /// </summary>
    public class RectGuidanceController : MonoBehaviour
    {
    
    	/// <summary>
    	/// 高亮显示的目标
    	/// </summary>
    	public RectTransform Target;
    	
    	/// <summary>
    	/// 区域范围缓存
    	/// </summary>
    	private Vector3[] _corners = new Vector3[4];
    
    	/// <summary>
    	/// 镂空区域中心
    	/// </summary>
    	private Vector4 _center;
    
    	/// <summary>
    	/// 最终的偏移值X
    	/// </summary>
    	private float _targetOffsetX = 0f;
    
    	/// <summary>
    	/// 最终的偏移值Y
    	/// </summary>
    	private float _targetOffsetY = 0f;
    
    	/// <summary>
    	/// 遮罩材质
    	/// </summary>
    	private Material _material;
    	
    	/// <summary>
    	/// 当前的偏移值X
    	/// </summary>
    	private float _currentOffsetX = 0f;
    
    	/// <summary>
    	/// 当前的偏移值Y
    	/// </summary>
    	private float _currentOffsetY = 0f;
    
    	/// <summary>
    	/// 动画收缩时间
    	/// </summary>
    	private float _shrinkTime = 0.5f;
    
    	/// <summary>
    	/// 时间渗透组件
    	/// </summary>
    	private GuidanceEventPenetrate _eventPenetrate;
    
    	/// <summary>
    	/// 世界坐标到画布坐标的转换
    	/// </summary>
    	/// <param name="canvas">画布</param>
    	/// <param name="world">世界坐标</param>
    	/// <returns>转换后在画布的坐标</returns>
    	private Vector2 WorldToCanvasPos(Canvas canvas, Vector3 world)
    	{
    		Vector2 position;
    		RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, world,
    			canvas.GetComponent<Camera>(), out position);
    		return position;
    	}
    
        public void SetTarget(RectTransform rectTransform, bool isPauseGame, float timer=0.5f)
        {
            this.isPauseGame = isPauseGame;
            Target = rectTransform;
            _shrinkTime = timer;
            _eventPenetrate = GetComponent<GuidanceEventPenetrate>();
            if (_eventPenetrate != null)
                _eventPenetrate.SetTarget(Target);
            //获取画布
            Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
            //获取高亮区域四个顶点的世界坐标
            Target.GetWorldCorners(_corners);
            //计算高亮显示区域咋画布中的范围
            _targetOffsetX = Vector2.Distance(WorldToCanvasPos(canvas, _corners[0]), WorldToCanvasPos(canvas, _corners[3])) / 2f;
            _targetOffsetY = Vector2.Distance(WorldToCanvasPos(canvas, _corners[0]), WorldToCanvasPos(canvas, _corners[1])) / 2f;
            //计算高亮显示区域的中心
            float x = _corners[0].x + ((_corners[3].x - _corners[0].x) / 2f);
            float y = _corners[0].y + ((_corners[1].y - _corners[0].y) / 2f);
            Vector3 centerWorld = new Vector3(x, y, 0);
            Vector2 center = WorldToCanvasPos(canvas, centerWorld);
            //设置遮罩材料中中心变量
            Vector4 centerMat = new Vector4(center.x, center.y, 0, 0);
            _material = GetComponent<Image>().material;
            _material.SetVector("_Center", centerMat);
            //计算当前偏移的初始值
            RectTransform canvasRectTransform = (canvas.transform as RectTransform);
            if (canvasRectTransform != null)
            {
                //获取画布区域的四个顶点
                canvasRectTransform.GetWorldCorners(_corners);
                //求偏移初始值
                for (int i = 0; i < _corners.Length; i++)
                {
                    if (i % 2 == 0)
                        _currentOffsetX = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, _corners[i]), center), _currentOffsetX);
                    else
                        _currentOffsetY = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, _corners[i]), center), _currentOffsetY);
                }
            }
            //设置遮罩材质中当前偏移的变量
            _material.SetFloat("_SliderX", _currentOffsetX);
            _material.SetFloat("_SliderY", _currentOffsetY);
    
            if (mShink != null)
            {
                StopCoroutine(mShink);
            }
            if (isPauseGame)
            {
                mShink=StartCoroutine(StartShink());
            }
        }
    
    	private float _shrinkVelocityX = 0f;
    	private float _shrinkVelocityY = 0f;
        private Coroutine mShink;
        private bool isPauseGame; //是否受时间影响 true 影响 false 不影响
        /// <summary>
        /// 脱离timeScale影响
        /// </summary>
        /// <returns></returns>
        IEnumerator StartShink()
        {
            float lengthX = _currentOffsetX - _targetOffsetX;
            float lengthY = _currentOffsetY - _targetOffsetY;
            for (float i = 0; i < _shrinkTime; i += Time.unscaledDeltaTime)
            {
                float scale = i / _shrinkTime;
                if (scale >= 1) scale = 1;
                float valueX = _currentOffsetX - (lengthX * scale);
                _material.SetFloat("_SliderX", valueX);
                float valueY= _currentOffsetY - (lengthY * scale);
                _material.SetFloat("_SliderY", valueY);
                yield return 0;
            }
            _material.SetFloat("_SliderX", _targetOffsetX);
            _material.SetFloat("_SliderY", _targetOffsetY);
        }
        private void Update()
    	{
            if (isPauseGame) return;
    		//从当前偏移值到目标偏移值差值显示收缩动画
    		float valueX = Mathf.SmoothDamp(_currentOffsetX, _targetOffsetX, ref _shrinkVelocityX, _shrinkTime);
    		float valueY = Mathf.SmoothDamp(_currentOffsetY, _targetOffsetY, ref _shrinkVelocityY, _shrinkTime);
    		if (!Mathf.Approximately(valueX, _currentOffsetX))
    		{
    			_currentOffsetX = valueX;
    			_material.SetFloat("_SliderX",_currentOffsetX);
    		}
    
    		if (!Mathf.Approximately(valueY, _currentOffsetY))
    		{
    			_currentOffsetY = valueY;
    			_material.SetFloat("_SliderY",_currentOffsetY);
    		}
    	}
        public void ClearTaget()
        {
            Target = null;
        }
    }

    矩形着色器代码:

    
    Shader "UI/BeginnerGuidance/Rect"
    {
        Properties
        {
            [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
            _Color ("Tint", Color) = (1,1,1,1)
    
            _StencilComp ("Stencil Comparison", Float) = 8
            _Stencil ("Stencil ID", Float) = 0
            _StencilOp ("Stencil Operation", Float) = 0
            _StencilWriteMask ("Stencil Write Mask", Float) = 255
            _StencilReadMask ("Stencil Read Mask", Float) = 255
    
            _ColorMask ("Color Mask", Float) = 15
    
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
            
            _Center("Center",vector) = (0,0,0,0)
            _SliderX("SliderX",Range(0,1500)) = 1500
            _SliderY("SliderY",Range(0,1500)) = 1500
        }
    
        SubShader
        {
            Tags
            {
                "Queue"="Transparent"
                "IgnoreProjector"="True"
                "RenderType"="Transparent"
                "PreviewType"="Plane"
                "CanUseSpriteAtlas"="True"
            }
    
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
            }
    
            Cull Off
            Lighting Off
            ZWrite Off
            ZTest [unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask [_ColorMask]
    
            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0
    
                #include "UnityCG.cginc"
                #include "UnityUI.cginc"
    
                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP
    
                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color    : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                
                float2 _Center;
                float _SliderX;
                float _SliderY;
    
                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
    
                    OUT.texcoord = v.texcoord;
    
                    OUT.color = v.color * _Color;
                    return OUT;
                }
    
                sampler2D _MainTex;
    
                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    
                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif
    
                    #ifdef UNITY_UI_ALPHACLIP
                    clip (color.a - 0.001);
                    #endif
    
                    float2 dis = IN.worldPosition.xy - _Center.xy;
                    color.a *= (abs(dis.x) > _SliderX) || (abs(dis.y) > _SliderY);
                    color.rgb *= color.a;
    
                    return color;
                }
            ENDCG
            }
        }
    }
    

    UI事件向下穿透

    上面的内容只是实现了一个高亮缓动的动画,但是Raycast是被屏蔽了的,这个时候我们需要高亮的地方实现UI事件的穿透,让事件能向下传递。我们只需要实现Unity的ICanvasRaycastFilter接口

    public class GuidanceEventPenetrate : MonoBehaviour, ICanvasRaycastFilter
    {
        private RectTransform mTagrget;
    
    	public void SetTarget(RectTransform target)
    	{
            mTagrget = target;
    	}
    	public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    	{
    		if (mTagrget == null)
    			return true;
    
    		return !RectTransformUtility.RectangleContainsScreenPoint(mTagrget, sp, eventCamera);
    	}
    }

    RectangleContainsScreenPoint判断一个屏幕点是否在目标Rect范围内,如果在,则返回真

    IsRaycastLocationValid为true时,事件向下传递是无效的,被拦截在当前UI界面,为false,则在当前界面是无效的

    原理是当点击的位置在高亮目标的范围内,我们让当前的UI遮罩能向下传递事件,否则屏蔽事件

    展开全文
  • unity 新手引导

    千次阅读 2017-12-27 11:02:30
    //是不是所有新手引导都已经完成 comp :complete UIGuideModel.Instance.mIsGuideComp = comp ; if (! comp ) { UIGuideCtrl.Instance.GuideRespStep(pMsg); //处理新手引导的信息 } return (Int32)...
                case (int)GSToGC.MsgID.eMsgToGCFromGS_GuideResp:
                print ("eMsgToGCFromGS_GuideResp");
                    OnNetMsg_NotifyGuideResp(stream);
                    break;

        Int32 OnNetMsg_NotifyGuideResp(Stream stream)
        {
            GSToGC.GuideCSStepInfo pMsg;
            if (!ProtoDes(out pMsg, stream))
            {
                return PROTO_DESERIALIZE_ERROR;
            }
            bool comp = pMsg.allcomp;  //是不是所有新手引导都已经完成  comp :complete 
            UIGuideModel.Instance.mIsGuideComp = comp;
            if (!comp)
            {
                UIGuideCtrl.Instance.GuideRespStep(pMsg); //处理新手引导的信息
            }
    
            return (Int32)EErrorCode.eNormal;
        }

    UIGuideCtrl.cs

            public void GuideRespStep(GSToGC.GuideCSStepInfo pMsg)
            {
                UIGuideModel.Instance.GuideFinishModelList(pMsg.taskid);
            }

    UIGuideModel.cs

       public void GuideFinishModelList(List<uint> pList)
            {
                mGuideFinishList = pList;
                mCurrentTaskModelId  = mGuideStepInfo.GuideStepNull;
                foreach (mGuideStepInfo step in Enum.GetValues(typeof(mGuideStepInfo)))
                {
                    if (step == mGuideStepInfo.GuideStepNull)
                    {
                        continue;
                    }
                    if (!mGuideFinishList.Contains((uint)step))
                    {
                        mCurrentTaskModelId = step;
                        break;
                    }
                }
                if (mCurrentTaskModelId != mGuideStepInfo.GuideStepNull)
                {
                    GamePlayGuideCtrl.Instance.StartModelTask(mCurrentTaskModelId);
                    UIGuideCtrl.Instance.Enter();
                }
            }
    

    GamePlayGuideCtrl.cs

            /// <summary>
            /// 开始执行模块的引导
            /// </summary>
            /// <param name="modelId"></param>
            public void StartModelTask(mGuideStepInfo modelId)
            {
                GamePlayGuideModel.Instance.StartModelTask(modelId);
            }

    UIGuideCtrl.cs

        public class UIGuideCtrl : Singleton<UIGuideCtrl>
        {
            public void Enter()
            {
                EventCenter.Broadcast(EGameEvent.eGameEvent_PlayGuideEnter);
            }

    GamePlayGuideModel.cs

            /// <summary>
            /// 开始模块Id
            /// </summary>
            /// <param name="modelId"></param>
            public void StartModelTask(mGuideStepInfo modelId)
            {
                foreach (var item in ConfigReader.GuideTaskMgrInfoDict)
                {
                    if ((mGuideStepInfo)item.Value.mTasktype == modelId)
                    {
                        NowTaskId = item.Key;
                        return;
                    }
                }
            }

    ConfigReader.cs

     public static Dictionary<int, GuideMgrInfo> GuideTaskMgrInfoDict
        {
            get
            {
                if (guideTaskMgrInfoDict.Count == 0)
                {
                    ReadGuideManagerTaskConfig rdTaskMgr = new ReadGuideManagerTaskConfig("Config/taskmanager");
                }
                return guideTaskMgrInfoDict;
            }
        }
    

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <taskmanager xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <info id="90001">
            <childtype>17</childtype>
            <childid>17000</childid>
            <endtype>2</endtype>
            <endid>17000</endid>
            <nexttaskid>90002</nexttaskid>
            <tasktype>1001</tasktype>
        </info>
        <info id="90002">
            <childtype>17</childtype>
            <childid>17001</childid>
            <endtype>2</endtype>
            <endid>17001</endid>
            <nexttaskid>90003</nexttaskid>
            <tasktype>1001</tasktype>
        </info>

    上面xml文本解析:

                string typeName = (infoNodeList[i] as XmlElement).GetAttributeNode("id").InnerText;
    
                GuideMgrInfo mgrInfo = new GuideMgrInfo();
                mgrInfo.TaskId = Convert.ToInt32(typeName); //请看这里
    
                        case "childtype":
                            mgrInfo.ChildTaskType = GameMethod.ResolveToIntList(xEle.InnerText);
                            break;  
                        case "childid":
                            mgrInfo.ChildTaskId = GameMethod.ResolveToIntList(xEle.InnerText);
                            break;
                        case "endtype":
                            mgrInfo.TaskEndType = (TaskCheckType)Convert.ToInt32(xEle.InnerText);
                            break;
                        case "endid":
                            mgrInfo.EndTaskChildId = Convert.ToInt32(xEle.InnerText);
                            break;
                        case "nexttaskid":
                            mgrInfo.NextTaskId = Convert.ToInt32(xEle.InnerText);
                            break;
                        case "close":
                            mgrInfo.mToServerType = Convert.ToInt32(xEle.InnerText);
                            break;
                        case "tasktype":
                            mgrInfo.mTasktype = Convert.ToInt32(xEle.InnerText);
                            break;
                        case "moduleend":
                            mgrInfo.moduleend = Convert.ToInt32(xEle.InnerText) == 1 ? true : false;
                            break;
    ConfigReader.guideTaskMgrInfoDict.Add(mgrInfo.TaskId, mgrInfo);


    UIGuideCtrl.cs

        public class UIGuideCtrl : Singleton<UIGuideCtrl>
        {
            public void Enter()
            {
                EventCenter.Broadcast(EGameEvent.eGameEvent_PlayGuideEnter);
            }

    GamePlayGuideWindow.cs

            public override void Init()
            {
                EventCenter.AddListener(EGameEvent.eGameEvent_PlayGuideEnter, Show);
                EventCenter.AddListener(EGameEvent.eGameEvent_PlayGuideExit, Hide);
                //EventCenter.AddListener<GameObject>(EGameEvent.eGameEvent_UIGuideEvent, OnUiGuideAddButtonEvent);
            }


    show 方法中 执行 onenable
    GamePlayGuideWindow.cs

       public override void OnEnable()
            {
                EventCenter.AddListener(EGameEvent.eGameEvent_PlayTaskModelFinish, ExecuteNextGuide);
                EventCenter.AddListener<GuideTaskType, int>(EGameEvent.eGameEvent_PlayChildTaskFinish, OnFinishChildTask);
                EventCenter.AddListener(EGameEvent.eGameEvent_SdkLogOff, SdkLogOff);
                if (GamePlayGuideModel.Instance.GuideTaskExecuteList.Count == 0)
                {
                    ExecuteNextGuide();  //注意这里
                }
            }
    



    GamePlayGuideWindow.cs

      /// <summary>
            /// 或许模块要执行的Id
            /// </summary>
            private void ExecuteNextGuide()
            {
                int taskId = GamePlayGuideModel.Instance.NowTaskId;
                if (!ConfigReader.GuideTaskMgrInfoDict.ContainsKey(taskId))
                {
                    return;
                }
                List<int> idList = ConfigReader.GuideTaskMgrInfoDict[taskId].ChildTaskId;
                List<int> TypeList = ConfigReader.GuideTaskMgrInfoDict[taskId].ChildTaskType;
                for (int tp = 0; tp < TypeList.Count; tp++)
                {
                    GuideTaskBase task = null;
                    GuideTaskType type = (GuideTaskType)TypeList[tp];
                    switch (type)
                    {
                        case GuideTaskType.ClickButtonTask:
                            task = new GuideClickButtonTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.PathTask:
                            task = new GuidePathTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.TimeCtrlTask:
                            task = new GuideTimeCtrlTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.MoveCameraTask:
                            task = new GuideCameraTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.TipTask:
                            task = new GuideTipTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.PopTipTask:
                            task = new GuidePopTipTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.ObstructTask:
                            task = new GuideObstructTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.VoiceTask:
                            task = new GuideVoiceTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.ObjFlashTask:
                            task = new GuideFlashTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.ObjShowTask:
                            task = new GuideShowObjTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.AbsorbTask:
                            task = new GuideAbsorbTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.SenderSoldierTask:
                            task = new GuideSendNpcTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.SenderHeroTask:
                            task = new GuideSendHeroTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.KillTask:
                            task = new GuideKillTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.RewardTipTask:
                            task = new GuideRewardTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.ForceClickTask:
                            task = new GuideForceClick(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.ScreenClickTask:
                            task = new GuideScreenClickTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.KillHeroTask:
                            task = new GuideKillHeroTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.GetHeroTask:
                            task = new GuideGetHeroTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.GetGuideToAdGuide:
                            task = new GuideToAdGuideTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.LevelToBuyRunes:
                            task = new GuideLevelToBuyRuneTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.GetRuneTask:
                            task = new GuideGetRuneTask(idList[tp], type, mRoot.gameObject);
                            break;
                        case GuideTaskType.EquipRuneTask:
                            task = new GuideEquipRuneTask(idList[tp], type, mRoot.gameObject);
                            break;
    
                    }
                    task.EnterTask();
                    GamePlayGuideModel.Instance.GuideTaskExecuteList.Add(task);
                }
            }

    GamePlayGuideWindow.cs脚本

            //每帧更新
            public override void Update(float deltaTime)
            {
                base.Update(deltaTime);
                for (int i = GamePlayGuideModel.Instance.GuideTaskExecuteList.Count - 1; i >= 0; i--)
                {
                    GamePlayGuideModel.Instance.GuideTaskExecuteList[i].ExcuseTask();
                }
            }


    经过测试,首次会执行:

    taskid :90001
    type : ForceClickTask


    case GuideTaskType.ForceClickTask:
        task = new GuideForceClick(idList[tp], type, mRoot.gameObject);
        break;


    GuideForceClick.cs

     public override void EnterTask()
     {
          EventCenter.AddListener<GameObject>(EGameEvent.eGameEvent_UIGuideEvent, OnUiGuideAddButtonEvent);
                DeltTask();
     }
      public override void ExcuseTask()
      {
                Debug.Log ("GuideForceClick  :   ExcuseTask()"); 
      }




         /// <summary>
            /// 将要触发事件的新手引导的按钮放入列表
            /// </summary>
            /// <param name="gobj"></param>
            public void AddUiGuideEventBtn(GameObject gobj)
            {
                if (gobj == null || UIGuideModel.Instance.UiGuideButtonGameObjectList.Contains(gobj))
                {
                    return;
                }
                Debug.Log ("UIGuideCtrl --- AddUiGuideEventBtn---gobj:  "+gobj.name);
                UIGuideModel.Instance.UiGuideButtonGameObjectList.Add(gobj);
                EventCenter.Broadcast(EGameEvent.eGameEvent_UIGuideEvent, gobj);//注意这一行
            }


    这里写图片描述



    FR:海涛高软(hunk Xu) QQ技术交流群:386476712

    展开全文
  • Unity新手引导完整代码,圆形,矩形,带新手引导渐变动画 单例管理 方便快捷 快下载吧 内含新手引导shader
  • 该资源为Unity新手引导Shader遮罩完整工程。在网上现有资源的基础上做了一些整合和修正,可以直接打开DEMO场景看到效果展示。具体的实现流程如有不会可以咨询我本人。
  • Unity新手引导(圆形指引、矩形指引) 声明:中心镂空为圆形或矩形,增加指引动画,基于UGUI 一、Shader 建立两个shader,命名为RectGuide、CircleGuide。 RectGuide Shader "UI/RectGuide" { Properties { ...

    Unity新手引导(圆形指引、矩形指引)

    声明:中心镂空为圆形或矩形,增加指引动画,基于UGUI

    一、Shader

    建立两个shader,命名为RectGuide、CircleGuide。

    • RectGuide
    Shader "UI/RectGuide"
    {
        Properties
        {
            [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
            _Color("Tint", Color) = (1,1,1,1)
    
            _StencilComp("Stencil Comparison", Float) = 8
            _Stencil("Stencil ID", Float) = 0
            _StencilOp("Stencil Operation", Float) = 0
            _StencilWriteMask("Stencil Write Mask", Float) = 255
            _StencilReadMask("Stencil Read Mask", Float) = 255
    
            _ColorMask("Color Mask", Float) = 15
    
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
    
            _Center("Center",vector) = (0,0,0,0)
            _SliderX("SliderX",Range(0,1500)) = 1500
            _SliderY("SliderY",Range(0,1500)) = 1500
        }
    
            SubShader
            {
                Tags
                {
                    "Queue" = "Transparent"
                    "IgnoreProjector" = "True"
                    "RenderType" = "Transparent"
                    "PreviewType" = "Plane"
                    "CanUseSpriteAtlas" = "True"
                }
    
                Stencil
                {
                    Ref[_Stencil]
                    Comp[_StencilComp]
                    Pass[_StencilOp]
                    ReadMask[_StencilReadMask]
                    WriteMask[_StencilWriteMask]
                }
    
                Cull Off
                Lighting Off
                ZWrite Off
                ZTest[unity_GUIZTestMode]
                Blend SrcAlpha OneMinusSrcAlpha
                ColorMask[_ColorMask]
    
                Pass
                {
                    Name "Default"
                CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    #pragma target 2.0
    
                    #include "UnityCG.cginc"
                    #include "UnityUI.cginc"
    
                    #pragma multi_compile __ UNITY_UI_CLIP_RECT
                    #pragma multi_compile __ UNITY_UI_ALPHACLIP
    
                    struct appdata_t
                    {
                        float4 vertex   : POSITION;
                        float4 color    : COLOR;
                        float2 texcoord : TEXCOORD0;
                        UNITY_VERTEX_INPUT_INSTANCE_ID
                    };
    
                    struct v2f
                    {
                        float4 vertex   : SV_POSITION;
                        fixed4 color : COLOR;
                        float2 texcoord  : TEXCOORD0;
                        float4 worldPosition : TEXCOORD1;
                        UNITY_VERTEX_OUTPUT_STEREO
                    };
    
                    sampler2D _MainTex;
                    fixed4 _Color;
                    fixed4 _TextureSampleAdd;
                    float4 _ClipRect;
                    float4 _MainTex_ST;
    
                    float2 _Center;
                    float _SliderX;
                    float _SliderY;
    
                    v2f vert(appdata_t v)
                    {
                        v2f OUT;
                        UNITY_SETUP_INSTANCE_ID(v);
                        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                        OUT.worldPosition = v.vertex;
                        OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
    
                        OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    
                        OUT.color = v.color * _Color;
                        return OUT;
                    }
    
                    fixed4 frag(v2f IN) : SV_Target
                    {
                        half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    
                        #ifdef UNITY_UI_CLIP_RECT
                        color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                        #endif
    
                        #ifdef UNITY_UI_ALPHACLIP
                        clip(color.a - 0.001);
                        #endif
                        float2 dis = IN.worldPosition.xy - _Center.xy;
                        color.a *= (abs(dis.x) > _SliderX) || (abs(dis.y) > _SliderY);
                        color.rgb *= color.a;
    
                        return color;
                    }
                ENDCG
                }
            }
    }
    
    • CircleGuide
    Shader "UI/CircleGuide"
    {
        Properties
        {
            [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
            _Color("Tint", Color) = (1,1,1,1)
    
            _StencilComp("Stencil Comparison", Float) = 8
            _Stencil("Stencil ID", Float) = 0
            _StencilOp("Stencil Operation", Float) = 0
            _StencilWriteMask("Stencil Write Mask", Float) = 255
            _StencilReadMask("Stencil Read Mask", Float) = 255
    
            _ColorMask("Color Mask", Float) = 15
    
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
            _Center("Center",vector) = (0,0,0,0)
            _Slider("Slider",Range(0,2500)) = 2500
        }
    
            SubShader
            {
                Tags
                {
                    "Queue" = "Transparent"
                    "IgnoreProjector" = "True"
                    "RenderType" = "Transparent"
                    "PreviewType" = "Plane"
                    "CanUseSpriteAtlas" = "True"
                }
    
                Stencil
                {
                    Ref[_Stencil]
                    Comp[_StencilComp]
                    Pass[_StencilOp]
                    ReadMask[_StencilReadMask]
                    WriteMask[_StencilWriteMask]
                }
    
                Cull Off
                Lighting Off
                ZWrite Off
                ZTest[unity_GUIZTestMode]
                Blend SrcAlpha OneMinusSrcAlpha
                ColorMask[_ColorMask]
    
                Pass
                {
                    Name "Default"
                CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    #pragma target 2.0
    
                    #include "UnityCG.cginc"
                    #include "UnityUI.cginc"
    
                    #pragma multi_compile __ UNITY_UI_CLIP_RECT
                    #pragma multi_compile __ UNITY_UI_ALPHACLIP
    
                    struct appdata_t
                    {
                        float4 vertex   : POSITION;
                        float4 color    : COLOR;
                        float2 texcoord : TEXCOORD0;
                        UNITY_VERTEX_INPUT_INSTANCE_ID
                    };
    
                    struct v2f
                    {
                        float4 vertex   : SV_POSITION;
                        fixed4 color : COLOR;
                        float2 texcoord  : TEXCOORD0;
                        float4 worldPosition : TEXCOORD1;
                        UNITY_VERTEX_OUTPUT_STEREO
                    };
    
                    sampler2D _MainTex;
                    fixed4 _Color;
                    fixed4 _TextureSampleAdd;
                    float4 _ClipRect;
                    float4 _MainTex_ST;
                    float2 _Center;
                    float _Slider;
    
                    v2f vert(appdata_t v)
                    {
                        v2f OUT;
                        UNITY_SETUP_INSTANCE_ID(v);
                        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                        OUT.worldPosition = v.vertex;
                        OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
    
                        OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    
                        OUT.color = v.color * _Color;
                        return OUT;
                    }
    
                    fixed4 frag(v2f IN) : SV_Target
                    {
                        half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
    
                        #ifdef UNITY_UI_CLIP_RECT
                        color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                        #endif
    
                        #ifdef UNITY_UI_ALPHACLIP
                        clip(color.a - 0.001);
                        #endif
                        color.a *= (distance(IN.worldPosition.xy,_Center.xy) > _Slider);
                        color.rgb *= color.a;
    
                        return color;
                    }
                ENDCG
                }
            }
    }
    

    二、前期准备

    • 建立两个材质球,命名为rectMat、circleMat,将对应shader赋值给材质。

    在这里插入图片描述

    • 创建UI-Image,将Image填充满屏幕,将其中一个材质赋值给Image上的material,修改Image的透明度,一般为纯黑半透明。

    在这里插入图片描述

    三、脚本实现

    • 创建基类GuideBase脚本,用来管理两个shader,因为中心镂空的效果,除了半 径或者长宽的计算方式不一样,其他的都一样。
    using UnityEngine;
    using UnityEngine.UI;
    
    [RequireComponent(typeof(Image))]
    public class GuideBase : MonoBehaviour
    {
        protected Material material;//材质
        protected Vector3 center;//镂空中心
        protected RectTransform target;//被引导的目标对象
        protected Vector3[] targetCorners = new Vector3[4];//引导目标的边界
    
        protected float timer;//计时器,来达到动画匀速播放
        protected float time;//整体动画时间
        protected bool isScaling;//是否正在缩放
       //虚方法,子类可以去重写,里面用来判断动画是否播放,如果播放,就按照既定的时间匀速完成
        protected virtual void Update()
        {
            if (isScaling)
            {
                timer += Time.deltaTime * 1 / time;
                if (timer >= 1)
                {
                    timer = 0;
                    isScaling = false;
                }
            }
        }
       //这里是来获取目标物体的四个点来计算中心点,因为对于矩形或者圆形效果,他们面对的中心点是确定的
        public virtual void Guide(Canvas canvas, RectTransform target)
        {
            material = GetComponent<Image>().material;
            this.target = target;
            //获取四个点的世界坐标
            target.GetWorldCorners(targetCorners);
            //世界坐标转屏幕坐标
            for (int i = 0; i < targetCorners.Length; i++)
            {
                targetCorners[i] = WorldToScreenPoints(canvas, targetCorners[i]);
            }
            //计算中心点
            center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
            center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
            //设置中心点
            material.SetVector("_Center", center);
        }
       //为了让子类继承的时候直接重写就可以,因为矩形和圆形的动画方式不一样,跟长宽或者半径有关
        public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
        {
    
        }
       //坐标的转换
        public Vector2 WorldToScreenPoints(Canvas canvas, Vector3 world)
        {
            //把世界转屏幕
            Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
            Vector2 localPoint;
            //屏幕转局部坐标
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), screenPoint, canvas.worldCamera, out localPoint);
            return localPoint;
        }
    }
    
    • 创建CircleGuide和RectGuide脚本来重写基类GuideBase脚本里的通用方法。
    • CircleGuide脚本
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class CircleGuide : GuideBase
    {
        private float r;//镂空半径
        private float scaleR;//变化之后的半径大小
    
       //继承GuideBase基类,重写他的获取目标位置同时修改半径的方法
        public override void Guide(Canvas canvas, RectTransform target)
        {
            base.Guide(canvas, target);//继承基类里面获取中心点的计算
            //计算半径
            float width = (targetCorners[3].x - targetCorners[0].x) / 2;
            float height = (targetCorners[1].y - targetCorners[0].y) / 2;
            r=Mathf.Sqrt(width*width+height*height);
            this.material.SetFloat("_Slider", r);
        }
        //重写基类动画方法,获取半径值来达到动画效果
        public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
        {
            this.Guide(canvas, target);//需要中心点,所以直接调用上一个方法
            scaleR = r * scale;
            this.material.SetFloat("_Slider", scaleR);
    
            this.time = time;
            isScaling = true;
            timer = 0;
        }
    
        protected override void Update()
        {
            base.Update();
            if (isScaling)
            {
                this.material.SetFloat("_Slider", Mathf.Lerp(scaleR,r,timer));
            }
        }
    
    }
    
    • RectGuide脚本
    using UnityEngine;
    using UnityEngine.UI;
    
    public class RectGuide : GuideBase
    {
        protected float width;//镂空宽
        protected float height;//镂空高
    
        float scalewidth;
        float scaleheight;
        public override void Guide(Canvas canvas,RectTransform target)
        {
            base.Guide(canvas, target);
            //计算宽高
            width = (targetCorners[3].x - targetCorners[0].x) / 2;
            height = (targetCorners[1].y - targetCorners[0].y) / 2;
            material.SetFloat("_SliderX", width);
            material.SetFloat("_SliderY", height);
        }
    
        public override void Guide(Canvas canvas, RectTransform target,float scale,float time)
        {
            this.Guide(canvas, target);
    
            scalewidth = width * scale;
            scaleheight = height * scale;
            material.SetFloat("_SliderX", scalewidth);
            material.SetFloat("_SliderY", scaleheight);
    
            this.time = time;
            isScaling = true;
            timer = 0;
            
        }
    
        protected override void Update()
        {
            base.Update();
            if (isScaling)
            {
                this.material.SetFloat("_SliderX", Mathf.Lerp(scalewidth, width, timer));
                this.material.SetFloat("_SliderY", Mathf.Lerp(scaleheight, height, timer));
            }
        }
    }
    
    • 创建GuideController脚本来实现方法被调用,从而达到相应效果。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public enum GuideType
    {
        Rect,
        Circle,
    }
    
    [RequireComponent(typeof(CircleGuide))]
    [RequireComponent(typeof(RectGuide))]
    public class GuideController : MonoBehaviour,ICanvasRaycastFilter
    {
        private CircleGuide circleGuide;
        private RectGuide rectGuide;
    
        public Material rectMat;
        public Material circleMat;
        private Image mask;//就是本身,纯黑半透明
        private RectTransform target;
        private void Awake()
        {
            mask = transform.GetComponent<Image>();
            if (mask == null) { throw new System.Exception("mask初始化失败"); }
            if (rectMat == null || circleMat == null) { throw new System.Exception("材质未赋值"); }
            rectGuide = transform.GetComponent<RectGuide>();
            circleGuide = transform.GetComponent<CircleGuide>();
        }
    
        public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
        {
            this.target = target;
            switch (guideType)
            {
                case GuideType.Rect:
                    mask.material = rectMat;
                    rectGuide.Guide(canvas, target);
                    break;
                case GuideType.Circle:
                    mask.material = circleMat;
                    circleGuide.Guide(canvas, target);
                    break;
    
            }
        }
    
        public void Guide(Canvas canvas, RectTransform target, GuideType guideType,float scale,float time)
        {
            this.target = target;
            switch (guideType)
            {
                case GuideType.Rect:
                    mask.material = rectMat;
                    rectGuide.Guide(canvas, target, scale,time);
                    break;
                case GuideType.Circle:
                    mask.material = circleMat;
                    circleGuide.Guide(canvas, target, scale, time);
                    break;
    
            }
        }
    //这里的方法代表是否镂空内容可被点击,返回false则可以,true则不可以
        public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
        {
            if (target==null) { return true; }
            return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
        }
    }
    

    四、测试

    1. 在Image上挂载RectGuide、CircleGuide、GuideController脚本,同时创建测试脚本GuidePanel并挂载到Image身上。

    在这里插入图片描述

    1. GuidePanel的实现
    using UnityEngine;
    
    public class GuidePanel : MonoBehaviour
    {
        GuideController guideController;
        Canvas canvas;
    
        private void Start()
        {
            canvas = transform.GetComponentInParent<Canvas>();
            guideController = transform.GetComponent<GuideController>();
            //这句代码的参数代表(哪个画布,哪个对象需要被镂空引导,镂空的类型,镂空缩放动画前的比例,镂空缩放动画的时长)
            guideController.Guide(canvas, GameObject.Find("GuideUI").GetComponent<RectTransform>(), GuideType.Circle,2,0.5f);
        }
    }
    
    展开全文
  • Unity新手引导镂空

    2020-07-09 17:55:47
    转载链接: https://www.jb51.net/article/180262.htm
    展开全文
  • Unity 新手引导黑屏对话和按键提示 新建一个unity 3D项目 创建一个平面(plane)和一个方块(cube)当作我们最基础的场景 在左侧的空白处右键新建一个面板,【右键】- 【UI】- 【Panel】 点击面板,在右侧的属性中...
  • //引导目标的边界 protected float timer; protected float time; protected bool isScaling; protected virtual void Update() { if (isScaling) { timer += Time.deltaTime * 1 / time; if (timer >= 1) { timer =...
  • Unity新手引导相关遮罩处理

    千次阅读 2016-11-30 11:12:44
    项目中需要添加新手引导的强制引导遮罩。 之前通过实现UnityEngine.ICanvasRaycastFilter这个接口 并且实现方法public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) { //返回当前鼠标出发的...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 133
精华内容 53
关键字:

unity新手引导