using System; #if UNITY_EDITOR using System.Reflection; #endif using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.Serialization; using UnityEngine.UI.CoroutineTween; namespace UnityEngine.UI { /// /// Base class for all UI components that should be derived from when creating new Graphic types. /// [DisallowMultipleComponent] [RequireComponent(typeof(CanvasRenderer))] [RequireComponent(typeof(RectTransform))] [ExecuteInEditMode] public abstract class Graphic : UIBehaviour, ICanvasElement { static protected Material s_DefaultUI = null; static protected Texture2D s_WhiteTexture = null; /// /// Default material used to draw everything if no explicit material was specified. /// static public Material defaultGraphicMaterial { get { if (s_DefaultUI == null) s_DefaultUI = Canvas.GetDefaultCanvasMaterial(); return s_DefaultUI; } } // Cached and saved values [FormerlySerializedAs("m_Mat")] [SerializeField] protected Material m_Material; [SerializeField] private Color m_Color = Color.white; public virtual Color color { get { return m_Color; } set { if (SetPropertyUtility.SetColor(ref m_Color, value)) SetVerticesDirty(); } } [SerializeField] private bool m_RaycastTarget = true; public virtual bool raycastTarget { get { return m_RaycastTarget; } set { m_RaycastTarget = value; } } [NonSerialized] private RectTransform m_RectTransform; [NonSerialized] private CanvasRenderer m_CanvasRender; [NonSerialized] private Canvas m_Canvas; [NonSerialized] private bool m_VertsDirty; [NonSerialized] private bool m_MaterialDirty; [NonSerialized] protected UnityAction m_OnDirtyLayoutCallback; [NonSerialized] protected UnityAction m_OnDirtyVertsCallback; [NonSerialized] protected UnityAction m_OnDirtyMaterialCallback; [NonSerialized] protected static Mesh s_Mesh; [NonSerialized] private static readonly VertexHelper s_VertexHelper = new VertexHelper(); // Tween controls for the Graphic [NonSerialized] private readonly TweenRunner m_ColorTweenRunner; protected bool useLegacyMeshGeneration { get; set; } // Called by Unity prior to deserialization, // should not be called by users protected Graphic() { if (m_ColorTweenRunner == null) m_ColorTweenRunner = new TweenRunner(); m_ColorTweenRunner.Init(this); useLegacyMeshGeneration = true; } public virtual void SetAllDirty() { SetLayoutDirty(); SetVerticesDirty(); SetMaterialDirty(); } public virtual void SetLayoutDirty() { if (!IsActive()) return; LayoutRebuilder.MarkLayoutForRebuild(rectTransform); if (m_OnDirtyLayoutCallback != null) m_OnDirtyLayoutCallback(); } public virtual void SetVerticesDirty() { if (!IsActive()) return; m_VertsDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyVertsCallback != null) m_OnDirtyVertsCallback(); } public virtual void SetMaterialDirty() { if (!IsActive()) return; m_MaterialDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyMaterialCallback != null) m_OnDirtyMaterialCallback(); } protected override void OnRectTransformDimensionsChange() { if (gameObject.activeInHierarchy) { // prevent double dirtying... if (CanvasUpdateRegistry.IsRebuildingLayout()) SetVerticesDirty(); else { SetVerticesDirty(); SetLayoutDirty(); } } } protected override void OnBeforeTransformParentChanged() { GraphicRegistry.UnregisterGraphicForCanvas(canvas, this); LayoutRebuilder.MarkLayoutForRebuild(rectTransform); } protected override void OnTransformParentChanged() { base.OnTransformParentChanged(); m_Canvas = null; if (!IsActive()) return; CacheCanvas(); GraphicRegistry.RegisterGraphicForCanvas(canvas, this); SetAllDirty(); } // 在hierachy里的深度,依次增加 /// /// Absolute depth of the graphic, used by rendering and events -- lowest to highest. /// public int depth { get { return canvasRenderer.absoluteDepth; } } /// /// Transform gets cached for speed. /// public RectTransform rectTransform { get { return m_RectTransform ?? (m_RectTransform = GetComponent()); } } public Canvas canvas { get { if (m_Canvas == null) CacheCanvas(); return m_Canvas; } } // 找到父节点中最近的canvas private void CacheCanvas() { var list = ListPool.Get(); // 找到所有父节点中所有canvas,从里到外 gameObject.GetComponentsInParent(false, list); if (list.Count > 0) { // Find the first active and enabled canvas. for (int i = 0; i < list.Count; ++i) { if (list[i].isActiveAndEnabled) { m_Canvas = list[i]; break; } } } else m_Canvas = null; ListPool.Release(list); } /// /// UI Renderer component. /// public CanvasRenderer canvasRenderer { get { if (m_CanvasRender == null) m_CanvasRender = GetComponent(); return m_CanvasRender; } } public virtual Material defaultMaterial { get { return defaultGraphicMaterial; } } /// /// Returns the material used by this Graphic. /// public virtual Material material { get { return (m_Material != null) ? m_Material : defaultMaterial; } set { if (m_Material == value) return; m_Material = value; SetMaterialDirty(); } } /// /// 提交到CanvasRenderer用这个,要应用IMaterialModifier的修改结果 /// public virtual Material materialForRendering { get { // 在这里调用IMaterialModifier的修改 var components = ListPool.Get(); GetComponents(typeof(IMaterialModifier), components); var currentMat = material; for (var i = 0; i < components.Count; i++) currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat); ListPool.Release(components); return currentMat; } } /// /// Returns the texture used to draw this Graphic. /// public virtual Texture mainTexture { get { return s_WhiteTexture; } } /// /// Mark the Graphic and the canvas as having been changed. /// protected override void OnEnable() { base.OnEnable(); CacheCanvas(); GraphicRegistry.RegisterGraphicForCanvas(canvas, this); #if UNITY_EDITOR GraphicRebuildTracker.TrackGraphic(this); #endif if (s_WhiteTexture == null) s_WhiteTexture = Texture2D.whiteTexture; SetAllDirty(); } /// /// Clear references. /// protected override void OnDisable() { #if UNITY_EDITOR GraphicRebuildTracker.UnTrackGraphic(this); #endif GraphicRegistry.UnregisterGraphicForCanvas(canvas, this); CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this); if (canvasRenderer != null) canvasRenderer.Clear(); LayoutRebuilder.MarkLayoutForRebuild(rectTransform); base.OnDisable(); } protected override void OnCanvasHierarchyChanged() { // Use m_Cavas so we dont auto call CacheCanvas Canvas currentCanvas = m_Canvas; // Clear the cached canvas. Will be fetched below if active. m_Canvas = null; if (!IsActive()) return; CacheCanvas(); if (currentCanvas != m_Canvas) { GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this); // Only register if we are active and enabled as OnCanvasHierarchyChanged can get called // during object destruction and we dont want to register ourself and then become null. if (IsActive()) GraphicRegistry.RegisterGraphicForCanvas(canvas, this); } } // canvas重建 public virtual void Rebuild(CanvasUpdate update) { if (canvasRenderer.cull) return; switch (update) { case CanvasUpdate.PreRender: if (m_VertsDirty) { UpdateGeometry(); m_VertsDirty = false; } if (m_MaterialDirty) { UpdateMaterial(); m_MaterialDirty = false; } break; } } public virtual void LayoutComplete() {} public virtual void GraphicUpdateComplete() {} /// /// Update the renderer's material. /// protected virtual void UpdateMaterial() { if (!IsActive()) return; canvasRenderer.materialCount = 1; canvasRenderer.SetMaterial(materialForRendering, 0); canvasRenderer.SetTexture(mainTexture); // 设置_MainTex,会覆盖材质上设置的_MainTex } //c 顶点重建 /// /// Update the renderer's vertices. /// protected virtual void UpdateGeometry() { if (useLegacyMeshGeneration) DoLegacyMeshGeneration(); else DoMeshGeneration(); } // 顶点重建,生成mesh private void DoMeshGeneration() { if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) OnPopulateMesh(s_VertexHelper); // 填充vertexHelper else s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw. // 可以通过实现IMeshModifer修改mesh var components = ListPool.Get(); GetComponents(typeof(IMeshModifier), components); for (var i = 0; i < components.Count; i++) ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper); ListPool.Release(components); s_VertexHelper.FillMesh(workerMesh); // 传入canvasRenderer作为mesh canvasRenderer.SetMesh(workerMesh); } private void DoLegacyMeshGeneration() { if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) { #pragma warning disable 618 OnPopulateMesh(workerMesh); #pragma warning restore 618 } else { workerMesh.Clear(); } // 自定义流程 var components = ListPool.Get(); GetComponents(typeof(IMeshModifier), components); for (var i = 0; i < components.Count; i++) { #pragma warning disable 618 ((IMeshModifier)components[i]).ModifyMesh(workerMesh); #pragma warning restore 618 } ListPool.Release(components); canvasRenderer.SetMesh(workerMesh); } protected static Mesh workerMesh { get { if (s_Mesh == null) { s_Mesh = new Mesh(); s_Mesh.name = "Shared UI Mesh"; s_Mesh.hideFlags = HideFlags.HideAndDontSave; } return s_Mesh; } } [Obsolete("Use OnPopulateMesh instead.", true)] protected virtual void OnFillVBO(System.Collections.Generic.List vbo) {} [Obsolete("Use OnPopulateMesh(VertexHelper vh) instead.", false)] protected virtual void OnPopulateMesh(Mesh m) { OnPopulateMesh(s_VertexHelper); s_VertexHelper.FillMesh(m); } /// /// Fill the vertex buffer data. /// protected virtual void OnPopulateMesh(VertexHelper vh) { var r = GetPixelAdjustedRect(); var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); Color32 color32 = color; vh.Clear(); vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f)); vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f)); vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f)); vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f)); vh.AddTriangle(0, 1, 2); vh.AddTriangle(2, 3, 0); } #if UNITY_EDITOR public virtual void OnRebuildRequested() { // when rebuild is requested we need to rebuild all the graphics / // and associated components... The correct way to do this is by // calling OnValidate... Because MB's don't have a common base class // we do this via reflection. It's nasty and ugly... Editor only. var mbs = gameObject.GetComponents(); foreach (var mb in mbs) { if (mb == null) continue; var methodInfo = mb.GetType().GetMethod("OnValidate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (methodInfo != null) methodInfo.Invoke(mb, null); } } protected override void Reset() { SetAllDirty(); } #endif // Call from unity if animation properties have changed protected override void OnDidApplyAnimationProperties() { SetAllDirty(); } /// /// Make the Graphic have the native size of its content. /// public virtual void SetNativeSize() {} public virtual bool Raycast(Vector2 sp, Camera eventCamera) { if (!isActiveAndEnabled) return false; var t = transform; var components = ListPool.Get(); bool ignoreParentGroups = false; bool continueTraversal = true; while (t != null) { t.GetComponents(components); for (var i = 0; i < components.Count; i++) { var canvas = components[i] as Canvas; if (canvas != null && canvas.overrideSorting) continueTraversal = false; // 如果实现了这个接口再进一步判断一下 var filter = components[i] as ICanvasRaycastFilter; if (filter == null) continue; var raycastValid = true; var group = components[i] as CanvasGroup; if (group != null) { if (ignoreParentGroups == false && group.ignoreParentGroups) { ignoreParentGroups = true; raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } else if (!ignoreParentGroups) raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } else { raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } if (!raycastValid) { ListPool.Release(components); return false; } } t = continueTraversal ? t.parent : null; } ListPool.Release(components); return true; } #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); SetAllDirty(); } #endif public Vector2 PixelAdjustPoint(Vector2 point) { if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) return point; else { return RectTransformUtility.PixelAdjustPoint(point, transform, canvas); } } public Rect GetPixelAdjustedRect() { if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) return rectTransform.rect; else return RectTransformUtility.PixelAdjustRect(rectTransform, canvas); } public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha) { CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha, true); } public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB) { if (canvasRenderer == null || (!useRGB && !useAlpha)) return; Color currentColor = canvasRenderer.GetColor(); if (currentColor.Equals(targetColor)) { m_ColorTweenRunner.StopTween(); return; } ColorTween.ColorTweenMode mode = (useRGB && useAlpha ? ColorTween.ColorTweenMode.All : (useRGB ? ColorTween.ColorTweenMode.RGB : ColorTween.ColorTweenMode.Alpha)); var colorTween = new ColorTween {duration = duration, startColor = canvasRenderer.GetColor(), targetColor = targetColor}; colorTween.AddOnChangedCallback(canvasRenderer.SetColor); colorTween.ignoreTimeScale = ignoreTimeScale; colorTween.tweenMode = mode; m_ColorTweenRunner.StartTween(colorTween); } static private Color CreateColorFromAlpha(float alpha) { var alphaColor = Color.black; alphaColor.a = alpha; return alphaColor; } public virtual void CrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale) { CrossFadeColor(CreateColorFromAlpha(alpha), duration, ignoreTimeScale, true, false); } public void RegisterDirtyLayoutCallback(UnityAction action) { m_OnDirtyLayoutCallback += action; } public void UnregisterDirtyLayoutCallback(UnityAction action) { m_OnDirtyLayoutCallback -= action; } public void RegisterDirtyVerticesCallback(UnityAction action) { m_OnDirtyVertsCallback += action; } public void UnregisterDirtyVerticesCallback(UnityAction action) { m_OnDirtyVertsCallback -= action; } public void RegisterDirtyMaterialCallback(UnityAction action) { m_OnDirtyMaterialCallback += action; } public void UnregisterDirtyMaterialCallback(UnityAction action) { m_OnDirtyMaterialCallback -= action; } } }