开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2212

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 3718|回复: 38

[士郎] UGUI和特效模型混排的完美解决方案

[复制链接]  [移动端链接]
排名
3
昨日变化

6378

主题

6892

帖子

2万

积分

Rank: 16

UID
1231
好友
185
蛮牛币
2775
威望
30
注册时间
2013-7-29
在线时间
3267 小时
最后登录
2018-5-25

社区QQ达人活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

发表于 2018-2-6 10:13:33 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
将RootCanvas设置为Screen Space - Camera。

1.jpg


把下层需要分层的UI组件放在多个子Canvas内,并且打上Override Sorting的勾(禁用Canvas的特殊排序)

2.jpg


3.jpg


之后调整各层的z值,就能进行正常排序。除了需要分一下Z的区间外,没有副作用。

4.jpg


5.jpg

我本来以为还要做一些工作,结果什么都不用做。导致这个误解的原因是我并没有使用过Unity自己的2D系统,不太理解sortingOrder的实际含义,以为这个和Z排是互斥的。而事实上sortingOrder只是透明物体排序时的一个权重值,第一权重是renderQueue,第二权重是sortingOrder,最后是Z。所以只要让sortingOrder保持为0,就是纯粹的Z排了。


这里也能看出,虽然Canvas并没有继承自Renderer,但是在渲染管线里同样是被当做Renderer处理的。因为在Z排的时候,是根据其包围盒的中心点排序的。如果Canvas内不同UI组件的Z值不等于0,虽然不会影响到内部排序,却会导致Canvas包围盒的中心点的Z值变化,影响到Canvas的排序。


总之,Canvas(不管RootCanvas还是子Canvas)其实就是个普通的Renderer,所以该怎么排序就怎么做就是了。但是Canvas内的UI组件并不是Renderer,它们只能用UGUI的那套排序和合批机制,最后拼合成一套Mesh和材质,整体投入渲染管线。


如果用NGUI的概念来描述,Canvas就是UIPanel,Graphic就是UIWiget。但Canvas还更进一步,并没有使用特殊的RenderQueue,而是走了正规程序,利用系统的SortingOrder来决定渲染顺序。当选中Override Sorting,SortingOrder和其他物体一样固定为0的时候,就成为了和其他Renderer完全一样的东西。


新Z排方案和旧方案的混合使用

此外,还要注意这种利用Z排管理的Canvas,和普通的Canvas混合使用的情况。

我们先把勾选了Override Sorting的Canvas称为ZCanvas(RootCanvas本身也是ZCanvas)
如果取消勾选Override Sorting,那么这个普通Canvas的包围盒就会和上一级的ZCanvas合并在一起(建议把它的Z恢复成0,防止总体Z值和预期不符),成为了ZCanvas的一部分,内部用Hierarchy顺序决定排序。


而ZCanvas还是可以和其他的ZCanvas,以及模型特效正常按Z混排。


6.jpg


红色图标是ZCanvas,Z值靠前,所以在钟前面。药水图标是SpriteRenderer,Z值靠后,所以在所有界面后面

多增加一层UI
7.jpg

总之
  • 不要修改Sorting Order,它的优先级太高
  • 不要修改未勾选Override Sorting的Canvas,以及其他UI组件的Z值。要让他们相对Z值为0,否则会影响ZCanvas的包围盒,造成预期之外的情况。

满足这两个要求,再用Override Sorting的开闭来决定是否要从上一级Canvas的管理中解放出来。不被管理的Canvas都按Z排序,还是比较容易理解的。


实际应用便是,每级窗口一个ZCanvas,UI内的特效希望显示在UI前方,相对Z值便小于0,否则就大于零(注意Z值越小越靠前)。内部UI(包括普通Canvas)相对Z值保持为0。如果想把特效“夹”在中间,就内部新建一个ZCanvas,修改相对Z值把自己推上来。


不必要的ZCanvas也可以省略,由需求决定。

遮罩

但非UI物体混排时,还会遇到遮罩的问题

效率最高方法的其实是分开多个摄像机,通过rect改变相机的渲染范围。但多摄像机管理比较麻烦。单摄像机情况下,要不RenderToTexture,要不用Shader解决。


Shader方面有两种方法,一种是利用Stencil,但UGUI的Mask组件绘制的Stencil并不能被外界利用。所以比较方便的是传入Shader一个ClipRect,Shader内将Rect外部的像素透明度设成0。


[AppleScript] 纯文本查看 复制代码
using UnityEngine;
namespace UGUIExtend
{
    /// <summary>
    /// 按RectTransform裁切Renderer
    /// </summary>
    [ExecuteInEditMode]
    public class UIClipAble : MonoBehaviour
    {
        static readonly int clipRectId = Shader.PropertyToID("_ClipRect");

        [SerializeField] public RectTransform mask;

        MaterialPropertyBlock materialBlock;
        Renderer[] renderers;
        Vector4 clipRect;

        private void OnEnable()
        {
            materialBlock = new MaterialPropertyBlock();
            renderers = GetComponentsInChildren<Renderer>();
        }

        static readonly Vector3[] corners = new Vector3[4];
        private void Update()
        {
            if (mask != null)
            {
                mask.GetWorldCorners(corners);
                Vector4 r = new Vector4(corners[0].x, corners[0].y, corners[2].x, corners[2].y);
                if (r != clipRect)
                {
                    clipRect = r;
                    materialBlock.SetVector(clipRectId, clipRect);
                    foreach (Renderer renderer in renderers)
                    {
                        renderer.SetPropertyBlock(materialBlock);
                    }
                }
            }
        }
    }
}


(需要手动更换材质Shader。mask可以指定任意RectTransfrom,不需要限定为RectMask2D)

1.1.gif


但要注意,这种做法和Stencil不同,遮罩外部的图像只是透明度被设为了0,但还是会照常渲染。Stencil虽然不会导致这种情况,但是Stencil会增加两次遮罩的绘制操作(一次绘制,一次擦除)


一定要用Stencil的做法,可以添加一个和UI遮挡一样大的Sprite Mask,它对粒子系统和SpriteRenderer有效。

8.jpg

粒子系统需要打开遮罩选项(5.5+支持)不方便把界面拆成多个Canvas的情况

拆Canvas毕竟会导致断批,如果特效和UI的交叠情况特别复杂的话,可以选择直接用特殊的UI组件来“模仿”特效的实现。


以UI组件的方式重新实现的LineRenderer
[AppleScript] 纯文本查看 复制代码
using System.Collections.Generic;

namespace UnityEngine.UI.Extensions
{
    [AddComponentMenu("UI/Extensions/Primitives/UILineRenderer")]
    [RequireComponent(typeof(RectTransform))]
    public class UILineRenderer : UIPrimitiveBase
        {
                private enum SegmentType
                {
                        Start,
            Middle,
            End,
            Full,
                }

                public enum JoinType
                {
                        Bevel,
            Miter
                }

                public enum BezierType
                {
                        None,
            Quick,
            Basic,
            Improved,
            Catenary,
        }

                private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;

                // A bevel 'nice' join displaces the vertices of the line segment instead of simply rendering a
                // quad to connect the endpoints. This improves the look of textured and transparent lines, since
                // there is no overlapping.
        private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;

                private static Vector2 UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_TOP_CENTER_LEFT, UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_RIGHT, UV_BOTTOM_RIGHT;
                private static Vector2[] startUvs, middleUvs, endUvs, fullUvs;

        [SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
        internal Vector2[] m_points;
        [SerializeField, Tooltip("Segments to be drawn\n This is a list of arrays of points")]
                internal List<Vector2[]> m_segments;

        [SerializeField, Tooltip("Thickness of the line")]
        internal float lineThickness = 2;
        [SerializeField, Tooltip("Use the relative bounds of the Rect Transform (0,0 -> 0,1) or screen space coordinates")]
        internal bool relativeSize;
        [SerializeField, Tooltip("Do the points identify a single line or split pairs of lines")]
        internal bool lineList;
        [SerializeField, Tooltip("Add end caps to each line\nMultiple caps when used with Line List")]
        internal bool lineCaps;
        [SerializeField, Tooltip("Resolution of the Bezier curve, different to line Resolution")]
        internal int bezierSegmentsPerCurve = 10;

        public float LineThickness
        {
            get { return lineThickness; }
            set { lineThickness = value; SetAllDirty(); }
        }

        public bool RelativeSize
        {
            get { return relativeSize; }
            set { relativeSize = value; SetAllDirty(); }
        }

        public bool LineList
        {
            get { return lineList; }
            set { lineList = value; SetAllDirty(); }
        }

        public bool LineCaps
        {
            get { return lineCaps; }
            set { lineCaps = value; SetAllDirty(); }
        }

        [Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")]
                public JoinType LineJoins = JoinType.Bevel;

        [Tooltip("Bezier method to apply to line, see docs for options\nCan't be used in conjunction with Resolution as Bezier already changes the resolution")]
        public BezierType BezierMode = BezierType.None;

        public int BezierSegmentsPerCurve
        {
            get { return bezierSegmentsPerCurve; }
            set { bezierSegmentsPerCurve = value; }
        }

        [HideInInspector]
        public bool drivenExternally = false;


                /// <summary>
                /// Points to be drawn in the line.
                /// </summary>
        public Vector2[] Points
                {
                        get
                        {
                                return m_points;
                        }

                        set
                        {
                                if (m_points == value)
                                        return;
                                m_points = value;
                                SetAllDirty();
                        }
                }

                /// <summary>
                /// List of Segments to be drawn.
                /// </summary>
        public List<Vector2[]> Segments
                {
                        get
                        {
                                return m_segments;
                        }

                        set
                        {
                                m_segments = value;
                                SetAllDirty();
                        }
                }

                private void PopulateMesh(VertexHelper vh, Vector2[] pointsToDraw)
                {
                        //If Bezier is desired, pick the implementation
                        if (BezierMode != BezierType.None && BezierMode != BezierType.Catenary && pointsToDraw.Length > 3) {
                                BezierPath bezierPath = new BezierPath ();

                                bezierPath.SetControlPoints (pointsToDraw);
                                bezierPath.SegmentsPerCurve = bezierSegmentsPerCurve;
                                List<Vector2> drawingPoints;
                                switch (BezierMode) {
                                case BezierType.Basic:
                                        drawingPoints = bezierPath.GetDrawingPoints0 ();
                                        break;
                                case BezierType.Improved:
                                        drawingPoints = bezierPath.GetDrawingPoints1 ();
                                        break;
                                default:
                                        drawingPoints = bezierPath.GetDrawingPoints2 ();
                                        break;
                                }

                                pointsToDraw = drawingPoints.ToArray ();
                        }
                        if (BezierMode == BezierType.Catenary && pointsToDraw.Length == 2) {
                                CableCurve cable = new CableCurve (pointsToDraw);
                                cable.slack = Resoloution;
                                cable.steps = BezierSegmentsPerCurve;
                                pointsToDraw = cable.Points ();
                        }

                        if (ImproveResolution != ResolutionMode.None) {
                                pointsToDraw = IncreaseResolution (pointsToDraw);
                        }

                        // scale based on the size of the rect or use absolute, this is switchable
                        var sizeX = !relativeSize ? 1 : rectTransform.rect.width;
                        var sizeY = !relativeSize ? 1 : rectTransform.rect.height;
                        var offsetX = -rectTransform.pivot.x * sizeX;
                        var offsetY = -rectTransform.pivot.y * sizeY;

                        // Generate the quads that make up the wide line
                        var segments = new List<UIVertex[]> ();
                        if (lineList) {
                                for (var i = 1; i < pointsToDraw.Length; i += 2) {
                                        var start = pointsToDraw [i - 1];
                                        var end = pointsToDraw ;
                                        start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
                                        end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);

                                        if (lineCaps) {
                                                segments.Add (CreateLineCap (start, end, SegmentType.Start));
                                        }

                                        //segments.Add(CreateLineSegment(start, end, SegmentType.Full));
                                        segments.Add (CreateLineSegment (start, end, SegmentType.Middle));

                                        if (lineCaps) {
                                                segments.Add (CreateLineCap (start, end, SegmentType.End));
                                        }
                                }
                        } else {
                                for (var i = 1; i < pointsToDraw.Length; i++) {
                                        var start = pointsToDraw [i - 1];
                                        var end = pointsToDraw ;
                                        start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
                                        end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);

                                        if (lineCaps && i == 1) {
                                                segments.Add (CreateLineCap (start, end, SegmentType.Start));
                                        }

                                        segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
                                        //segments.Add(CreateLineSegment(start, end, SegmentType.Full));

                                        if (lineCaps && i == pointsToDraw.Length - 1) {
                                                segments.Add (CreateLineCap (start, end, SegmentType.End));
                                        }
                                }
                        }

                        // Add the line segments to the vertex helper, creating any joins as needed
                        for (var i = 0; i < segments.Count; i++) {
                                if (!lineList && i < segments.Count - 1) {
                                        var vec1 = segments  [1].position - segments  [2].position;
                                        var vec2 = segments [i + 1] [2].position - segments [i + 1] [1].position;
                                        var angle = Vector2.Angle (vec1, vec2) * Mathf.Deg2Rad;

                                        // Positive sign means the line is turning in a 'clockwise' direction
                                        var sign = Mathf.Sign (Vector3.Cross (vec1.normalized, vec2.normalized).z);

                                        // Calculate the miter point
                                        var miterDistance = lineThickness / (2 * Mathf.Tan (angle / 2));
                                        var miterPointA = segments  [2].position - vec1.normalized * miterDistance * sign;
                                        var miterPointB = segments  [3].position + vec1.normalized * miterDistance * sign;

                                        var joinType = LineJoins;
                                        if (joinType == JoinType.Miter) {
                                                // Make sure we can make a miter join without too many artifacts.
                                                if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN) {
                                                        segments  [2].position = miterPointA;
                                                        segments  [3].position = miterPointB;
                                                        segments [i + 1] [0].position = miterPointB;
                                                        segments [i + 1] [1].position = miterPointA;
                                                } else {
                                                        joinType = JoinType.Bevel;
                                                }
                                        }

                                        if (joinType == JoinType.Bevel) {
                                                if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN) {
                                                        if (sign < 0) {
                                                                segments  [2].position = miterPointA;
                                                                segments [i + 1] [1].position = miterPointA;
                                                        } else {
                                                                segments  [3].position = miterPointB;
                                                                segments [i + 1] [0].position = miterPointB;
                                                        }
                                                }

                                                var join = new UIVertex[] { segments  [2], segments  [3], segments [i + 1] [0], segments [i + 1] [1] };
                                                vh.AddUIVertexQuad (join);
                                        }
                                }

                                vh.AddUIVertexQuad (segments );
                        }
                        if (vh.currentVertCount > 64000) {
                                Debug.LogError ("Max Verticies size is 64000, current mesh vertcies count is [" + vh.currentVertCount + "] - Cannot Draw");
                                vh.Clear ();
                                return;
                        }

                }

        protected override void OnPopulateMesh(VertexHelper vh)
                {
                        if (m_points != null && m_points.Length > 0) {
                                GeneratedUVs ();
                                vh.Clear ();

                                PopulateMesh (vh, m_points);

                        }
                        else if (m_segments != null && m_segments.Count > 0) {
                                GeneratedUVs ();
                                vh.Clear ();

                                for (int s = 0; s < m_segments.Count; s++) {
                                        Vector2[] pointsToDraw = m_segments ;
                                        PopulateMesh (vh, pointsToDraw);
                                }
                        } 


        }

                private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
                {
                        if (type == SegmentType.Start)
                        {
                                var capStart = start - ((end - start).normalized * lineThickness / 2);
                                return CreateLineSegment(capStart, start, SegmentType.Start);
                        }
                        else if (type == SegmentType.End)
                        {
                                var capEnd = end + ((end - start).normalized * lineThickness / 2);
                                return CreateLineSegment(end, capEnd, SegmentType.End);
                        }

                        Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
                        return null;
                }

                private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type)
                {
                        Vector2 offset = new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;

                        var v1 = start - offset;
                        var v2 = start + offset;
                        var v3 = end + offset;
                        var v4 = end - offset;
            //Return the VDO with the correct uvs
            switch (type)
            {
                case SegmentType.Start:
                    return SetVbo(new[] { v1, v2, v3, v4 }, startUvs);
                case SegmentType.End:
                    return SetVbo(new[] { v1, v2, v3, v4 }, endUvs);
                case SegmentType.Full:
                    return SetVbo(new[] { v1, v2, v3, v4 }, fullUvs);
                default:
                    return SetVbo(new[] { v1, v2, v3, v4 }, middleUvs);
            }
                }

        protected override void GeneratedUVs()
        {
            if (activeSprite != null)
            {
                var outer = Sprites.DataUtility.GetOuterUV(activeSprite);
                var inner = Sprites.DataUtility.GetInnerUV(activeSprite);
                UV_TOP_LEFT = new Vector2(outer.x, outer.y);
                UV_BOTTOM_LEFT = new Vector2(outer.x, outer.w);
                UV_TOP_CENTER_LEFT = new Vector2(inner.x, inner.y);
                UV_TOP_CENTER_RIGHT = new Vector2(inner.z, inner.y);
                UV_BOTTOM_CENTER_LEFT = new Vector2(inner.x, inner.w);
                UV_BOTTOM_CENTER_RIGHT = new Vector2(inner.z, inner.w);
                UV_TOP_RIGHT = new Vector2(outer.z, outer.y);
                UV_BOTTOM_RIGHT = new Vector2(outer.z, outer.w);
            }
            else
            {
                UV_TOP_LEFT = Vector2.zero;
                UV_BOTTOM_LEFT = new Vector2(0, 1);
                UV_TOP_CENTER_LEFT = new Vector2(0.5f, 0);
                UV_TOP_CENTER_RIGHT = new Vector2(0.5f, 0);
                UV_BOTTOM_CENTER_LEFT = new Vector2(0.5f, 1);
                UV_BOTTOM_CENTER_RIGHT = new Vector2(0.5f, 1);
                UV_TOP_RIGHT = new Vector2(1, 0);
                UV_BOTTOM_RIGHT = Vector2.one;
            }


            startUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
            middleUvs = new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
            endUvs = new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
            fullUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
        }

        protected override void ResolutionToNativeSize(float distance)
        {
            if (UseNativeSize)
            {
                m_Resolution = distance / (activeSprite.rect.width / pixelsPerUnit);
                lineThickness = activeSprite.rect.height / pixelsPerUnit;
            }
        }
    }
}


将任意粒子实时转换成UI组件数据显示出来(性能和GC Allow都很糟糕)
[AppleScript] 纯文本查看 复制代码
namespace UnityEngine.UI.Extensions
{
#if UNITY_5_3_OR_NEWER
    [ExecuteInEditMode]
    [RequireComponent(typeof(CanvasRenderer), typeof(ParticleSystem))]
    [AddComponentMenu("UI/Effects/Extensions/UIParticleSystem")]
    public class UIParticleSystem : MaskableGraphic
    {
        [Tooltip("Having this enabled run the system in LateUpdate rather than in Update making it faster but less precise (more clunky)")]
        public bool fixedTime = true;

        private Transform _transform;
        private ParticleSystem pSystem;
        private ParticleSystem.Particle[] particles;
        private UIVertex[] _quad = new UIVertex[4];
        private Vector4 imageUV = Vector4.zero;
        private ParticleSystem.TextureSheetAnimationModule textureSheetAnimation;
        private int textureSheetAnimationFrames;
        private Vector2 textureSheetAnimationFrameSize;
        private ParticleSystemRenderer pRenderer;

        private Material currentMaterial;

        private Texture currentTexture;

#if UNITY_5_5_OR_NEWER
        private ParticleSystem.MainModule mainModule;
#endif

        public override Texture mainTexture
        {
            get
            {
                return currentTexture;
            }
        }

        protected bool Initialize()
        {
            // initialize members
            if (_transform == null)
            {
                _transform = transform;
            }
            if (pSystem == null)
            {
                pSystem = GetComponent<ParticleSystem>();

                if (pSystem == null)
                {
                    return false;
                }

#if UNITY_5_5_OR_NEWER
                mainModule = pSystem.main;
                if (pSystem.main.maxParticles > 14000)
                {
                    mainModule.maxParticles = 14000;
                }
#else
                    if (pSystem.maxParticles > 14000)
                        pSystem.maxParticles = 14000;
#endif

                pRenderer = pSystem.GetComponent<ParticleSystemRenderer>();
                if (pRenderer != null)
                    pRenderer.enabled = false;

                Shader foundShader = Shader.Find("UI Extensions/Particles/Additive");
                Material pMaterial = new Material(foundShader);

                if (material == null)
                    material = pMaterial;

                currentMaterial = material;
                if (currentMaterial && currentMaterial.HasProperty("_MainTex"))
                {
                    currentTexture = currentMaterial.mainTexture;
                    if (currentTexture == null)
                        currentTexture = Texture2D.whiteTexture;
                }
                material = currentMaterial;
                // automatically set scaling
#if UNITY_5_5_OR_NEWER
                mainModule.scalingMode = ParticleSystemScalingMode.Hierarchy;
#else
                    pSystem.scalingMode = ParticleSystemScalingMode.Hierarchy;
#endif

                particles = null;
            }
#if UNITY_5_5_OR_NEWER
            if (particles == null)
                particles = new ParticleSystem.Particle[pSystem.main.maxParticles];
#else
                if (particles == null)
                    particles = new ParticleSystem.Particle[pSystem.maxParticles];
#endif

            imageUV = new Vector4(0, 0, 1, 1);

            // prepare texture sheet animation
            textureSheetAnimation = pSystem.textureSheetAnimation;
            textureSheetAnimationFrames = 0;
            textureSheetAnimationFrameSize = Vector2.zero;
            if (textureSheetAnimation.enabled)
            {
                textureSheetAnimationFrames = textureSheetAnimation.numTilesX * textureSheetAnimation.numTilesY;
                textureSheetAnimationFrameSize = new Vector2(1f / textureSheetAnimation.numTilesX, 1f / textureSheetAnimation.numTilesY);
            }

            return true;
        }

        protected override void Awake()
        {
            base.Awake();
            if (!Initialize())
                enabled = false;
        }


        protected override void OnPopulateMesh(VertexHelper vh)
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                if (!Initialize())
                {
                    return;
                }
            }
#endif
            // prepare vertices
            vh.Clear();

            if (!gameObject.activeInHierarchy)
            {
                return;
            }

            Vector2 temp = Vector2.zero;
            Vector2 corner1 = Vector2.zero;
            Vector2 corner2 = Vector2.zero;
            // iterate through current particles
            int count = pSystem.GetParticles(particles);

            for (int i = 0; i < count; ++i)
            {
                ParticleSystem.Particle particle = particles;

                // get particle properties
#if UNITY_5_5_OR_NEWER
                Vector2 position = (mainModule.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
#else
                    Vector2 position = (pSystem.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
#endif
                float rotation = -particle.rotation * Mathf.Deg2Rad;
                float rotation90 = rotation + Mathf.PI / 2;
                Color32 color = particle.GetCurrentColor(pSystem);
                float size = particle.GetCurrentSize(pSystem) * 0.5f;

                // apply scale
#if UNITY_5_5_OR_NEWER
                if (mainModule.scalingMode == ParticleSystemScalingMode.Shape)
                    position /= canvas.scaleFactor;
#else
                    if (pSystem.scalingMode == ParticleSystemScalingMode.Shape)
                        position /= canvas.scaleFactor;
#endif

                // apply texture sheet animation
                Vector4 particleUV = imageUV;
                if (textureSheetAnimation.enabled)
                {
#if UNITY_5_5_OR_NEWER
                    float frameProgress = 1 - (particle.remainingLifetime / particle.startLifetime);

                    if (textureSheetAnimation.frameOverTime.curveMin != null)
                    {
                        frameProgress = textureSheetAnimation.frameOverTime.curveMin.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
                    }
                    else if (textureSheetAnimation.frameOverTime.curve != null)
                    {
                        frameProgress = textureSheetAnimation.frameOverTime.curve.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
                    }
                    else if (textureSheetAnimation.frameOverTime.constant > 0)
                    {
                        frameProgress = textureSheetAnimation.frameOverTime.constant - (particle.remainingLifetime / particle.startLifetime);
                    }
#else
                    float frameProgress = 1 - (particle.lifetime / particle.startLifetime);
#endif

                    frameProgress = Mathf.Repeat(frameProgress * textureSheetAnimation.cycleCount, 1);
                    int frame = 0;

                    switch (textureSheetAnimation.animation)
                    {

                        case ParticleSystemAnimationType.WholeSheet:
                            frame = Mathf.FloorToInt(frameProgress * textureSheetAnimationFrames);
                            break;

                        case ParticleSystemAnimationType.SingleRow:
                            frame = Mathf.FloorToInt(frameProgress * textureSheetAnimation.numTilesX);

                            int row = textureSheetAnimation.rowIndex;
                            //                    if (textureSheetAnimation.useRandomRow) { // FIXME - is this handled internally by rowIndex?
                            //                        row = Random.Range(0, textureSheetAnimation.numTilesY, using: particle.randomSeed);
                            //                    }
                            frame += row * textureSheetAnimation.numTilesX;
                            break;

                    }

                    frame %= textureSheetAnimationFrames;

                    particleUV.x = (frame % textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.x;
                    particleUV.y = Mathf.FloorToInt(frame / textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.y;
                    particleUV.z = particleUV.x + textureSheetAnimationFrameSize.x;
                    particleUV.w = particleUV.y + textureSheetAnimationFrameSize.y;
                }

                temp.x = particleUV.x;
                temp.y = particleUV.y;

                _quad[0] = UIVertex.simpleVert;
                _quad[0].color = color;
                _quad[0].uv0 = temp;

                temp.x = particleUV.x;
                temp.y = particleUV.w;
                _quad[1] = UIVertex.simpleVert;
                _quad[1].color = color;
                _quad[1].uv0 = temp;

                temp.x = particleUV.z;
                temp.y = particleUV.w;
                _quad[2] = UIVertex.simpleVert;
                _quad[2].color = color;
                _quad[2].uv0 = temp;

                temp.x = particleUV.z;
                temp.y = particleUV.y;
                _quad[3] = UIVertex.simpleVert;
                _quad[3].color = color;
                _quad[3].uv0 = temp;

                if (rotation == 0)
                {
                    // no rotation
                    corner1.x = position.x - size;
                    corner1.y = position.y - size;
                    corner2.x = position.x + size;
                    corner2.y = position.y + size;

                    temp.x = corner1.x;
                    temp.y = corner1.y;
                    _quad[0].position = temp;
                    temp.x = corner1.x;
                    temp.y = corner2.y;
                    _quad[1].position = temp;
                    temp.x = corner2.x;
                    temp.y = corner2.y;
                    _quad[2].position = temp;
                    temp.x = corner2.x;
                    temp.y = corner1.y;
                    _quad[3].position = temp;
                }
                else
                {
                    // apply rotation
                    Vector2 right = new Vector2(Mathf.Cos(rotation), Mathf.Sin(rotation)) * size;
                    Vector2 up = new Vector2(Mathf.Cos(rotation90), Mathf.Sin(rotation90)) * size;

                    _quad[0].position = position - right - up;
                    _quad[1].position = position - right + up;
                    _quad[2].position = position + right + up;
                    _quad[3].position = position + right - up;
                }

                vh.AddUIVertexQuad(_quad);
            }
        }

        void Update()
        {
            if (!fixedTime && Application.isPlaying)
            {
                pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
                SetAllDirty();

                if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
                    (material != null && currentMaterial != null && material.shader != currentMaterial.shader))
                {
                    pSystem = null;
                    Initialize();
                }
            }
        }

        void LateUpdate()
        {
            if (!Application.isPlaying)
            {
                SetAllDirty();
            }
            else
            {
                if (fixedTime)
                {
                    pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
                    SetAllDirty();
                    if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
                        (material != null && currentMaterial != null && material.shader != currentMaterial.shader))
                    {
                        pSystem = null;
                        Initialize();
                    }
                }
            }
            if (material == currentMaterial)
                return;
            pSystem = null;
            Initialize();
        }
    }
#endif
}


如果特效复杂度很低,只是少量使用,又不希望用上面那种拆Canvas用Z排进行管理的混排方法,可以考虑一下。

知乎@flashyiyi


本帖被以下淘专辑推荐:


跟我念“站长妹纸萌萌哒!”我说站长,你说YO!爱你们么么哒~
回复

使用道具 举报

6蛮牛粉丝
1488/1500
排名
3210
昨日变化
12

37

主题

674

帖子

1488

积分

Rank: 6Rank: 6Rank: 6

UID
219600
好友
7
蛮牛币
2177
威望
0
注册时间
2017-4-27
在线时间
373 小时
最后登录
2018-5-26
发表于 2018-2-6 10:32:01 | 显示全部楼层
感谢分享

回复

使用道具 举报

排名
1068
昨日变化
5

13

主题

1031

帖子

2761

积分

Rank: 9Rank: 9Rank: 9

UID
68430
好友
7
蛮牛币
9819
威望
0
注册时间
2015-1-14
在线时间
741 小时
最后登录
2018-5-25
发表于 2018-2-6 11:01:59 | 显示全部楼层
这个非常有用,,解决 特效 穿过 界面问题~~66666666

回复 支持 反对

使用道具 举报

5熟悉之中
644/1000
排名
4885
昨日变化
26

6

主题

60

帖子

644

积分

Rank: 5Rank: 5

UID
44052
好友
0
蛮牛币
186
威望
0
注册时间
2014-9-8
在线时间
322 小时
最后登录
2018-5-25
发表于 2018-2-6 13:12:18 | 显示全部楼层
经过尝试认为调整Z轴只是一个思路,但算不上完美解决方案。z的调整是要根据特效的情况而调整,而不是只要图片比特效Z轴小就在其前面,这对于复杂的UI界面是致命的,对于多个image、特效、模型混在一起,实时生成且数量众多的的排序维护起来是不现实的。
[发帖际遇]: 一个袋子砸在了 kicwzy 头上,kicwzy 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复 支持 1 反对 0

使用道具 举报

7日久生情
2692/5000
排名
2969
昨日变化
14

0

主题

1888

帖子

2692

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
219676
好友
0
蛮牛币
2488
威望
0
注册时间
2017-7-12
在线时间
370 小时
最后登录
2018-5-26

活力之星

发表于 2018-2-6 13:44:41 | 显示全部楼层
谢谢分享
[发帖际遇]: 一个袋子砸在了 夜雨微凉 头上,夜雨微凉 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

5熟悉之中
978/1000
排名
3371
昨日变化
16

1

主题

367

帖子

978

积分

Rank: 5Rank: 5

UID
122160
好友
1
蛮牛币
1602
威望
0
注册时间
2015-9-10
在线时间
228 小时
最后登录
2018-5-25
发表于 2018-2-6 14:02:56 | 显示全部楼层
感谢分享

回复

使用道具 举报

3偶尔光临
170/300
排名
10768
昨日变化
7

0

主题

30

帖子

170

积分

Rank: 3Rank: 3Rank: 3

UID
247696
好友
0
蛮牛币
46
威望
0
注册时间
2017-10-9
在线时间
58 小时
最后登录
2018-5-24
发表于 2018-2-6 14:15:49 | 显示全部楼层
厉害了,大兄弟
[发帖际遇]: 一个袋子砸在了 PeiYF 头上,PeiYF 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

5熟悉之中
520/1000
排名
4712
昨日变化
32

1

主题

93

帖子

520

积分

Rank: 5Rank: 5

UID
236305
好友
0
蛮牛币
704
威望
0
注册时间
2017-8-7
在线时间
160 小时
最后登录
2018-5-25
发表于 2018-2-6 14:24:01 | 显示全部楼层
学习学习 ui和特效做的头疼

回复 支持 反对

使用道具 举报

4四处流浪
340/500

0

主题

215

帖子

340

积分

Rank: 4

UID
109564
好友
0
蛮牛币
94
威望
0
注册时间
2015-6-20
在线时间
125 小时
最后登录
2018-2-9
发表于 2018-2-6 15:02:03 | 显示全部楼层

感谢分享

回复

使用道具 举报

6蛮牛粉丝
1377/1500
排名
31738
昨日变化
14

3

主题

699

帖子

1377

积分

Rank: 6Rank: 6Rank: 6

UID
63377
好友
0
蛮牛币
12
威望
0
注册时间
2014-12-24
在线时间
668 小时
最后登录
2018-5-21
发表于 2018-2-6 19:04:47 | 显示全部楼层
感谢分享

回复

使用道具 举报

7日久生情
2533/5000
排名
3334
昨日变化
2

2

主题

1780

帖子

2533

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
10193
威望
0
注册时间
2017-9-6
在线时间
360 小时
最后登录
2018-5-23
发表于 2018-2-7 07:04:52 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

5熟悉之中
639/1000
排名
3264
昨日变化
14

1

主题

75

帖子

639

积分

Rank: 5Rank: 5

UID
194214
好友
0
蛮牛币
1834
威望
0
注册时间
2016-12-19
在线时间
169 小时
最后登录
2018-5-25
发表于 2018-2-7 08:47:40 | 显示全部楼层
感谢分享
[发帖际遇]: Mars1235 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
2082/5000
排名
3552
昨日变化
15

7

主题

1093

帖子

2082

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
168159
好友
4
蛮牛币
6066
威望
0
注册时间
2016-9-12
在线时间
614 小时
最后登录
2018-5-25
发表于 2018-2-7 09:33:22 | 显示全部楼层

回复

使用道具 举报

7日久生情
1922/5000
排名
3429
昨日变化
15

0

主题

1331

帖子

1922

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
185339
好友
0
蛮牛币
2730
威望
0
注册时间
2016-11-20
在线时间
213 小时
最后登录
2018-5-25
发表于 2018-2-7 09:35:05 | 显示全部楼层
谢谢分享

回复

使用道具 举报

6蛮牛粉丝
1344/1500
排名
5035
昨日变化
8

6

主题

821

帖子

1344

积分

Rank: 6Rank: 6Rank: 6

UID
236677
好友
0
蛮牛币
2197
威望
0
注册时间
2017-8-9
在线时间
269 小时
最后登录
2018-3-30
发表于 2018-2-7 10:28:53 | 显示全部楼层
感谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

关闭

站长推荐 上一条 /1 下一条

快速回复 返回顶部 返回列表