马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?注册帐号
x
将RootCanvas设置为Screen Space - Camera。
把下层需要分层的UI组件放在多个子Canvas内,并且打上Override Sorting的勾(禁用Canvas的特殊排序)
之后调整各层的z值,就能进行正常排序。除了需要分一下Z的区间外,没有副作用。
我本来以为还要做一些工作,结果什么都不用做。导致这个误解的原因是我并没有使用过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混排。
红色图标是ZCanvas,Z值靠前,所以在钟前面。药水图标是SpriteRenderer,Z值靠后,所以在所有界面后面
多增加一层UI
总之 - 不要修改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也可以省略,由需求决定。
遮罩
效率最高方法的其实是分开多个摄像机,通过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)
但要注意,这种做法和Stencil不同,遮罩外部的图像只是透明度被设为了0,但还是会照常渲染。Stencil虽然不会导致这种情况,但是Stencil会增加两次遮罩的绘制操作(一次绘制,一次擦除)
一定要用Stencil的做法,可以添加一个和UI遮挡一样大的Sprite Mask,它对粒子系统和SpriteRenderer有效。
粒子系统需要打开遮罩选项(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
|