• UGUI中粒子特效的裁剪显示


      在Unity游戏中,常会需要粒子特效需要显示在UI上的情况。对于列表中的粒子特效,则需要自己实现裁剪,如图 所示。

      实现粒子裁剪第一版:

      1. 向带SystemParticle的结点挂一个MonoBehaviour脚本。

      2. 修改粒子渲染shader,添加_ClipRect, 与UI/Default中类似。

      3. 在脚本中获取上层Mask或Mask2D的rect, 传入_ClipRect。此处需要注意的是,需要调用rect.GetWorldCorners(wcs)方法将UI坐标系下在区域转换为世界坐标系。

      当在多层列表的情况下,以上方法就出问题了,它并没有被上上层列表裁剪掉。比如这样:

      于是有了处理第二版:向上取多次Mask2D,再取它们的区域交集,传入Shader中。

      那么,有没有其它方法呢? 其实我们可以参考UGUI中其它组件的实现方式,以下是第三版。

      1. 脚本继承于UIBehaviour, IClippable。

      2. 实现SetClipRect方法,并在OnEnable和OnTransformParentChanged回调中,向Mask2D进行注册。此时SetClipRect函数中的Rect就是多个Mask2D的交集了。

      3. 将SetClipRect中的Rect转换为世界坐标系,传给Shader。

    using UnityEngine;
    using UnityEngine.EventSystems;
    using UnityEngine.UI;
    
    public class RD_ParticleClip : UIBehaviour, IClippable
    {
        [SerializeField]
        private Renderer[] renderers; // 支持多个粒子特效同时设置
        static Vector3[] wcs = new Vector3[2];
        private static MaterialPropertyBlock block;
        private RectMask2D m_ParentMask = null;
    
        [XLua.BlackList]
      // 由于美术在特效中使用的Transform, 故取父结点的RectTransform
    public RectTransform rectTransform => transform.parent as RectTransform; protected override void Awake() { ParticleSystem[] particleSystems = this.GetComponentsInChildren<ParticleSystem>(true); if (particleSystems.Length > 0) { renderers = new Renderer[particleSystems.Length]; for (int i = 0; i < particleSystems.Length; ++i) { renderers[i] = particleSystems[i].GetComponent<Renderer>(); } } if (block == null) block = new MaterialPropertyBlock(); } protected override void OnEnable() { base.OnEnable(); if (rectTransform != null) UpdateClipParent(); } protected override void OnDisable() { base.OnDisable(); if (rectTransform != null) UpdateClipParent(); } protected override void OnTransformParentChanged() { base.OnTransformParentChanged(); if (!isActiveAndEnabled) return; if (rectTransform != null) UpdateClipParent(); } // 代码来自UISystem/MaskableGraphic private void UpdateClipParent() { var newParent = IsActive() ? MaskUtilities.GetRectMaskForClippable(this) : null; // if the new parent is different OR is now inactive if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive())) m_ParentMask.RemoveClippable(this); // don't re-add it if the newparent is inactive if (newParent != null && newParent.IsActive()) newParent.AddClippable(this); m_ParentMask = newParent; } public void RecalculateClipping() { //Debug.Log("RecalculateClipping:" + gameObject.name); } public void Cull(Rect clipRect, bool validRect) { //Debug.Log("Cull:" + gameObject.name); } public void SetClipRect(Rect value, bool validRect) { wcs[0] = new Vector3(value.xMin, value.yMin, 0); wcs[1] = new Vector3(value.xMax, value.yMax, 0); var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); if (rootCanvas != null) {
           // 坐标系转换 Matrix4x4 mat
    = rootCanvas.transform.localToWorldMatrix; for (int i = 0; i < 2; ++i) wcs[i] = mat.MultiplyPoint(wcs[i]); for (int i = 0; i < renderers.Length; ++i) { Renderer render = renderers[i]; render.GetPropertyBlock(block); block.SetVector("_ClipRect", new Vector4(wcs[0].x, wcs[0].y, wcs[1].x, wcs[1].y)); block.SetFloat("_UseClipRect", 1); render.SetPropertyBlock(block); } } } }
  • 相关阅读:
    Authentication with SignalR and OAuth Bearer Token
    [Web API] 如何让 Web API 统一回传格式以及例外处理[转]
    EF6 Database First (DbContext)
    DbContext运行时动态附加上一个dbset
    命令模式
    责任链模式
    策略模式
    Sql Server isnull() 用法
    状态者模式
    dom元素改变监听
  • 原文地址:https://www.cnblogs.com/hghhe/p/14860427.html
Copyright © 2020-2023  润新知