This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5472815444de2ce45bf2053a4af04b9d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,244 @@
using System.Collections;
using UnityEngine.Events;
namespace UnityEngine.UI.CoroutineTween
{
// Base interface for tweeners,
// using an interface instead of
// an abstract class as we want the
// tweens to be structs.
internal interface ITweenValue
{
void TweenValue(float floatPercentage);
bool ignoreTimeScale { get; }
float duration { get; }
bool ValidTarget();
}
// Color tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct ColorTween : ITweenValue
{
public enum ColorTweenMode
{
All,
RGB,
Alpha
}
public class ColorTweenCallback : UnityEvent<Color> {}
private ColorTweenCallback m_Target;
private Color m_StartColor;
private Color m_TargetColor;
private ColorTweenMode m_TweenMode;
private float m_Duration;
private bool m_IgnoreTimeScale;
public Color startColor
{
get { return m_StartColor; }
set { m_StartColor = value; }
}
public Color targetColor
{
get { return m_TargetColor; }
set { m_TargetColor = value; }
}
public ColorTweenMode tweenMode
{
get { return m_TweenMode; }
set { m_TweenMode = value; }
}
public float duration
{
get { return m_Duration; }
set { m_Duration = value; }
}
public bool ignoreTimeScale
{
get { return m_IgnoreTimeScale; }
set { m_IgnoreTimeScale = value; }
}
public void TweenValue(float floatPercentage)
{
if (!ValidTarget())
return;
var newColor = Color.Lerp(m_StartColor, m_TargetColor, floatPercentage);
if (m_TweenMode == ColorTweenMode.Alpha)
{
newColor.r = m_StartColor.r;
newColor.g = m_StartColor.g;
newColor.b = m_StartColor.b;
}
else if (m_TweenMode == ColorTweenMode.RGB)
{
newColor.a = m_StartColor.a;
}
m_Target.Invoke(newColor);
}
public void AddOnChangedCallback(UnityAction<Color> callback)
{
if (m_Target == null)
m_Target = new ColorTweenCallback();
m_Target.AddListener(callback);
}
public bool GetIgnoreTimescale()
{
return m_IgnoreTimeScale;
}
public float GetDuration()
{
return m_Duration;
}
public bool ValidTarget()
{
return m_Target != null;
}
}
// Float tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct FloatTween : ITweenValue
{
public class FloatTweenCallback : UnityEvent<float> {}
private FloatTweenCallback m_Target;
private float m_StartValue;
private float m_TargetValue;
private float m_Duration;
private bool m_IgnoreTimeScale;
public float startValue
{
get { return m_StartValue; }
set { m_StartValue = value; }
}
public float targetValue
{
get { return m_TargetValue; }
set { m_TargetValue = value; }
}
public float duration
{
get { return m_Duration; }
set { m_Duration = value; }
}
public bool ignoreTimeScale
{
get { return m_IgnoreTimeScale; }
set { m_IgnoreTimeScale = value; }
}
public void TweenValue(float floatPercentage)
{
if (!ValidTarget())
return;
var newValue = Mathf.Lerp(m_StartValue, m_TargetValue, floatPercentage);
m_Target.Invoke(newValue);
}
public void AddOnChangedCallback(UnityAction<float> callback)
{
if (m_Target == null)
m_Target = new FloatTweenCallback();
m_Target.AddListener(callback);
}
public bool GetIgnoreTimescale()
{
return m_IgnoreTimeScale;
}
public float GetDuration()
{
return m_Duration;
}
public bool ValidTarget()
{
return m_Target != null;
}
}
// Tween runner, executes the given tween.
// The coroutine will live within the given
// behaviour container.
internal class TweenRunner<T> where T : struct, ITweenValue
{
protected MonoBehaviour m_CoroutineContainer;
protected IEnumerator m_Tween;
// utility function for starting the tween
private static IEnumerator Start(T tweenInfo)
{
if (!tweenInfo.ValidTarget())
yield break;
var elapsedTime = 0.0f;
while (elapsedTime < tweenInfo.duration)
{
elapsedTime += tweenInfo.ignoreTimeScale ? Time.unscaledDeltaTime : Time.deltaTime;
var percentage = Mathf.Clamp01(elapsedTime / tweenInfo.duration);
tweenInfo.TweenValue(percentage);
yield return null;
}
tweenInfo.TweenValue(1.0f);
}
public void Init(MonoBehaviour coroutineContainer)
{
m_CoroutineContainer = coroutineContainer;
}
public void StartTween(T info)
{
if (m_CoroutineContainer == null)
{
Debug.LogWarning("Coroutine container not configured... did you forget to call Init?");
return;
}
StopTween();
if (!m_CoroutineContainer.gameObject.activeInHierarchy)
{
info.TweenValue(1.0f);
return;
}
m_Tween = Start(info);
m_CoroutineContainer.StartCoroutine(m_Tween);
}
public void StopTween()
{
if (m_Tween != null)
{
m_CoroutineContainer.StopCoroutine(m_Tween);
m_Tween = null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7be84a49bb2cd7e4a9ed097ba22794d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 12c42068351bb084abde965d725b9887
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,163 @@
using System;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
/// <summary>
/// Structure that stores the state of an animation transition on a Selectable.
/// </summary>
[Serializable]
public class AnimationTriggers
{
private const string kDefaultNormalAnimName = "Normal";
private const string kDefaultHighlightedAnimName = "Highlighted";
private const string kDefaultPressedAnimName = "Pressed";
private const string kDefaultSelectedAnimName = "Selected";
private const string kDefaultDisabledAnimName = "Disabled";
[FormerlySerializedAs("normalTrigger")]
[SerializeField]
private string m_NormalTrigger = kDefaultNormalAnimName;
[FormerlySerializedAs("highlightedTrigger")]
[SerializeField]
private string m_HighlightedTrigger = kDefaultHighlightedAnimName;
[FormerlySerializedAs("pressedTrigger")]
[SerializeField]
private string m_PressedTrigger = kDefaultPressedAnimName;
[FormerlySerializedAs("m_HighlightedTrigger")]
[SerializeField]
private string m_SelectedTrigger = kDefaultSelectedAnimName;
[FormerlySerializedAs("disabledTrigger")]
[SerializeField]
private string m_DisabledTrigger = kDefaultDisabledAnimName;
/// <summary>
/// Trigger to send to animator when entering normal state.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Animator buttonAnimator;
/// public Button button;
/// void SomeFunction()
/// {
/// //Sets the button to the Normal state (Useful when making tutorials).
/// buttonAnimator.SetTrigger(button.animationTriggers.normalTrigger);
/// }
/// }
/// ]]>
///</code>
/// </example>
public string normalTrigger { get { return m_NormalTrigger; } set { m_NormalTrigger = value; } }
/// <summary>
/// Trigger to send to animator when entering highlighted state.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Animator buttonAnimator;
/// public Button button;
/// void SomeFunction()
/// {
/// //Sets the button to the Highlighted state (Useful when making tutorials).
/// buttonAnimator.SetTrigger(button.animationTriggers.highlightedTrigger);
/// }
/// }
/// ]]>
///</code>
/// </example>
public string highlightedTrigger { get { return m_HighlightedTrigger; } set { m_HighlightedTrigger = value; } }
/// <summary>
/// Trigger to send to animator when entering pressed state.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Animator buttonAnimator;
/// public Button button;
/// void SomeFunction()
/// {
/// //Sets the button to the Pressed state (Useful when making tutorials).
/// buttonAnimator.SetTrigger(button.animationTriggers.pressedTrigger);
/// }
/// }
/// ]]>
///</code>
/// </example>
public string pressedTrigger { get { return m_PressedTrigger; } set { m_PressedTrigger = value; } }
/// <summary>
/// Trigger to send to animator when entering selected state.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Animator buttonAnimator;
/// public Button button;
/// void SomeFunction()
/// {
/// //Sets the button to the Selected state (Useful when making tutorials).
/// buttonAnimator.SetTrigger(button.animationTriggers.selectedTrigger);
/// }
/// }
/// ]]>
///</code>
/// </example>
public string selectedTrigger { get { return m_SelectedTrigger; } set { m_SelectedTrigger = value; } }
/// <summary>
/// Trigger to send to animator when entering disabled state.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Animator buttonAnimator;
/// public Button button;
/// void SomeFunction()
/// {
/// //Sets the button to the Disabled state (Useful when making tutorials).
/// buttonAnimator.SetTrigger(button.animationTriggers.disabledTrigger);
/// }
/// }
/// ]]>
///</code>
/// </example>
public string disabledTrigger { get { return m_DisabledTrigger; } set { m_DisabledTrigger = value; } }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c67ac6e40bbb6fe47a095b949b609ce0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,175 @@
using System;
using System.Collections;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
/// <summary>
/// A standard button that sends an event when clicked.
/// </summary>
[AddComponentMenu("UI/Button", 30)]
public class Button : Selectable, IPointerClickHandler, ISubmitHandler
{
[Serializable]
/// <summary>
/// Function definition for a button click event.
/// </summary>
public class ButtonClickedEvent : UnityEvent {}
// Event delegates triggered on click.
[FormerlySerializedAs("onClick")]
[SerializeField]
private ButtonClickedEvent m_OnClick = new ButtonClickedEvent();
protected Button()
{}
/// <summary>
/// UnityEvent that is triggered when the button is pressed.
/// Note: Triggered on MouseUp after MouseDown on the same object.
/// </summary>
///<example>
///<code>
/// <![CDATA[
/// using UnityEngine;
/// using UnityEngine.UI;
/// using System.Collections;
///
/// public class ClickExample : MonoBehaviour
/// {
/// public Button yourButton;
///
/// void Start()
/// {
/// Button btn = yourButton.GetComponent<Button>();
/// btn.onClick.AddListener(TaskOnClick);
/// }
///
/// void TaskOnClick()
/// {
/// Debug.Log("You have clicked the button!");
/// }
/// }
/// ]]>
///</code>
///</example>
public ButtonClickedEvent onClick
{
get { return m_OnClick; }
set { m_OnClick = value; }
}
private void Press()
{
if (!IsActive() || !IsInteractable())
return;
UISystemProfilerApi.AddMarker("Button.onClick", this);
m_OnClick.Invoke();
}
/// <summary>
/// Call all registered IPointerClickHandlers.
/// Register button presses using the IPointerClickHandler. You can also use it to tell what type of click happened (left, right etc.).
/// Make sure your Scene has an EventSystem.
/// </summary>
/// <param name="eventData">Pointer Data associated with the event. Typically by the event system.</param>
/// <example>
/// <code>
/// <![CDATA[
/// //Attatch this script to a Button GameObject
/// using UnityEngine;
/// using UnityEngine.EventSystems;
///
/// public class Example : MonoBehaviour, IPointerClickHandler
/// {
/// //Detect if a click occurs
/// public void OnPointerClick(PointerEventData pointerEventData)
/// {
/// //Use this to tell when the user right-clicks on the Button
/// if (pointerEventData.button == PointerEventData.InputButton.Right)
/// {
/// //Output to console the clicked GameObject's name and the following message. You can replace this with your own actions for when clicking the GameObject.
/// Debug.Log(name + " Game Object Right Clicked!");
/// }
///
/// //Use this to tell when the user left-clicks on the Button
/// if (pointerEventData.button == PointerEventData.InputButton.Left)
/// {
/// Debug.Log(name + " Game Object Left Clicked!");
/// }
/// }
/// }
/// ]]>
///</code>
/// </example>
public virtual void OnPointerClick(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
return;
Press();
}
/// <summary>
/// Call all registered ISubmitHandler.
/// </summary>
/// <param name="eventData">Associated data with the event. Typically by the event system.</param>
/// <remarks>
/// This detects when a Button has been selected via a "submit" key you specify (default is the return key).
///
/// To change the submit key, either:
///
/// 1. Go to Edit->Project Settings->Input.
///
/// 2. Next, expand the Axes section and go to the Submit section if it exists.
///
/// 3. If Submit doesnt exist, add 1 number to the Size field. This creates a new section at the bottom. Expand the new section and change the Name field to “Submit”.
///
/// 4. Change the Positive Button field to the key you want (e.g. space).
///
///
/// Or:
///
/// 1. Go to your EventSystem in your Project
///
/// 2. Go to the Inspector window and change the Submit Button field to one of the sections in the Input Manager (e.g. "Submit"), or create your own by naming it what you like, then following the next few steps.
///
/// 3. Go to Edit->Project Settings->Input to get to the Input Manager.
///
/// 4. Expand the Axes section in the Inspector window. Add 1 to the number in the Size field. This creates a new section at the bottom.
///
/// 5. Expand the new section and name it the same as the name you inserted in the Submit Button field in the EventSystem. Set the Positive Button field to the key you want (e.g. space)
/// </remarks>
public virtual void OnSubmit(BaseEventData eventData)
{
Press();
// if we get set disabled during the press
// don't run the coroutine.
if (!IsActive() || !IsInteractable())
return;
DoStateTransition(SelectionState.Pressed, false);
StartCoroutine(OnFinishSubmit());
}
private IEnumerator OnFinishSubmit()
{
var fadeTime = colors.fadeDuration;
var elapsedTime = 0f;
while (elapsedTime < fadeTime)
{
elapsedTime += Time.unscaledDeltaTime;
yield return null;
}
DoStateTransition(currentSelectionState, false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e29b1a8efbd4b44bb3f3716e73f07ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,412 @@
using System;
using System.Collections.Generic;
using UnityEngine.UI.Collections;
namespace UnityEngine.UI
{
/// <summary>
/// Values of 'update' called on a Canvas update.
/// </summary>
/// <remarks> If modifying also modify m_CanvasUpdateProfilerStrings to match.</remarks>
public enum CanvasUpdate
{
/// <summary>
/// Called before layout.
/// </summary>
Prelayout = 0,
/// <summary>
/// Called for layout.
/// </summary>
Layout = 1,
/// <summary>
/// Called after layout.
/// </summary>
PostLayout = 2,
/// <summary>
/// Called before rendering.
/// </summary>
PreRender = 3,
/// <summary>
/// Called late, before render.
/// </summary>
LatePreRender = 4,
/// <summary>
/// Max enum value. Always last.
/// </summary>
MaxUpdateValue = 5
}
/// <summary>
/// This is an element that can live on a Canvas.
/// </summary>
public interface ICanvasElement
{
/// <summary>
/// Rebuild the element for the given stage.
/// </summary>
/// <param name="executing">The current CanvasUpdate stage being rebuild.</param>
void Rebuild(CanvasUpdate executing);
/// <summary>
/// Get the transform associated with the ICanvasElement.
/// </summary>
Transform transform { get; }
/// <summary>
/// Callback sent when this ICanvasElement has completed layout.
/// </summary>
void LayoutComplete();
/// <summary>
/// Callback sent when this ICanvasElement has completed Graphic rebuild.
/// </summary>
void GraphicUpdateComplete();
/// <summary>
/// Used if the native representation has been destroyed.
/// </summary>
/// <returns>Return true if the element is considered destroyed.</returns>
bool IsDestroyed();
}
/// <summary>
/// A place where CanvasElements can register themselves for rebuilding.
/// </summary>
public class CanvasUpdateRegistry
{
private static CanvasUpdateRegistry s_Instance;
private bool m_PerformingLayoutUpdate;
private bool m_PerformingGraphicUpdate;
// This list matches the CanvasUpdate enum above. Keep in sync
private string[] m_CanvasUpdateProfilerStrings = new string[] { "CanvasUpdate.Prelayout", "CanvasUpdate.Layout", "CanvasUpdate.PostLayout", "CanvasUpdate.PreRender", "CanvasUpdate.LatePreRender" };
private const string m_CullingUpdateProfilerString = "ClipperRegistry.Cull";
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();
protected CanvasUpdateRegistry()
{
Canvas.willRenderCanvases += PerformUpdate;
}
/// <summary>
/// Get the singleton registry instance.
/// </summary>
public static CanvasUpdateRegistry instance
{
get
{
if (s_Instance == null)
s_Instance = new CanvasUpdateRegistry();
return s_Instance;
}
}
private bool ObjectValidForUpdate(ICanvasElement element)
{
var valid = element != null;
var isUnityObject = element is Object;
if (isUnityObject)
valid = (element as Object) != null; //Here we make use of the overloaded UnityEngine.Object == null, that checks if the native object is alive.
return valid;
}
private void CleanInvalidItems()
{
// So MB's override the == operator for null equality, which checks
// if they are destroyed. This is fine if you are looking at a concrete
// mb, but in this case we are looking at a list of ICanvasElement
// this won't forward the == operator to the MB, but just check if the
// interface is null. IsDestroyed will return if the backend is destroyed.
var layoutRebuildQueueCount = m_LayoutRebuildQueue.Count;
for (int i = layoutRebuildQueueCount - 1; i >= 0; --i)
{
var item = m_LayoutRebuildQueue[i];
if (item == null)
{
m_LayoutRebuildQueue.RemoveAt(i);
continue;
}
if (item.IsDestroyed())
{
m_LayoutRebuildQueue.RemoveAt(i);
item.LayoutComplete();
}
}
var graphicRebuildQueueCount = m_GraphicRebuildQueue.Count;
for (int i = graphicRebuildQueueCount - 1; i >= 0; --i)
{
var item = m_GraphicRebuildQueue[i];
if (item == null)
{
m_GraphicRebuildQueue.RemoveAt(i);
continue;
}
if (item.IsDestroyed())
{
m_GraphicRebuildQueue.RemoveAt(i);
item.GraphicUpdateComplete();
}
}
}
private static readonly Comparison<ICanvasElement> s_SortLayoutFunction = SortLayoutList;
private void PerformUpdate()
{
UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
CleanInvalidItems();
m_PerformingLayoutUpdate = true;
m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
{
UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
{
var rebuild = m_LayoutRebuildQueue[j];
try
{
if (ObjectValidForUpdate(rebuild))
rebuild.Rebuild((CanvasUpdate)i);
}
catch (Exception e)
{
Debug.LogException(e, rebuild.transform);
}
}
UnityEngine.Profiling.Profiler.EndSample();
}
for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i)
m_LayoutRebuildQueue[i].LayoutComplete();
m_LayoutRebuildQueue.Clear();
m_PerformingLayoutUpdate = false;
UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);
// now layout is complete do culling...
UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
ClipperRegistry.instance.Cull();
UnityEngine.Profiling.Profiler.EndSample();
m_PerformingGraphicUpdate = true;
for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
{
UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
for (var k = 0; k < m_GraphicRebuildQueue.Count; k++)
{
try
{
var element = m_GraphicRebuildQueue[k];
if (ObjectValidForUpdate(element))
element.Rebuild((CanvasUpdate)i);
}
catch (Exception e)
{
Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
}
}
UnityEngine.Profiling.Profiler.EndSample();
}
for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
m_GraphicRebuildQueue[i].GraphicUpdateComplete();
m_GraphicRebuildQueue.Clear();
m_PerformingGraphicUpdate = false;
UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
}
private static int ParentCount(Transform child)
{
if (child == null)
return 0;
var parent = child.parent;
int count = 0;
while (parent != null)
{
count++;
parent = parent.parent;
}
return count;
}
private static int SortLayoutList(ICanvasElement x, ICanvasElement y)
{
Transform t1 = x.transform;
Transform t2 = y.transform;
return ParentCount(t1) - ParentCount(t2);
}
/// <summary>
/// Try and add the given element to the layout rebuild list.
/// Will not return if successfully added.
/// </summary>
/// <param name="element">The element that is needing rebuilt.</param>
public static void RegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
instance.InternalRegisterCanvasElementForLayoutRebuild(element);
}
/// <summary>
/// Try and add the given element to the layout rebuild list.
/// </summary>
/// <param name="element">The element that is needing rebuilt.</param>
/// <returns>
/// True if the element was successfully added to the rebuilt list.
/// False if either already inside a Graphic Update loop OR has already been added to the list.
/// </returns>
public static bool TryRegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
return instance.InternalRegisterCanvasElementForLayoutRebuild(element);
}
private bool InternalRegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
if (m_LayoutRebuildQueue.Contains(element))
return false;
/* TODO: this likely should be here but causes the error to show just resizing the game view (case 739376)
if (m_PerformingLayoutUpdate)
{
Debug.LogError(string.Format("Trying to add {0} for layout rebuild while we are already inside a layout rebuild loop. This is not supported.", element));
return false;
}*/
return m_LayoutRebuildQueue.AddUnique(element);
}
/// <summary>
/// Try and add the given element to the rebuild list.
/// Will not return if successfully added.
/// </summary>
/// <param name="element">The element that is needing rebuilt.</param>
public static void RegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
instance.InternalRegisterCanvasElementForGraphicRebuild(element);
}
/// <summary>
/// Try and add the given element to the rebuild list.
/// </summary>
/// <param name="element">The element that is needing rebuilt.</param>
/// <returns>
/// True if the element was successfully added to the rebuilt list.
/// False if either already inside a Graphic Update loop OR has already been added to the list.
/// </returns>
public static bool TryRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
return instance.InternalRegisterCanvasElementForGraphicRebuild(element);
}
private bool InternalRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
if (m_PerformingGraphicUpdate)
{
Debug.LogError(string.Format("Trying to add {0} for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.", element));
return false;
}
return m_GraphicRebuildQueue.AddUnique(element);
}
/// <summary>
/// Remove the given element from both the graphic and the layout rebuild lists.
/// </summary>
/// <param name="element"></param>
public static void UnRegisterCanvasElementForRebuild(ICanvasElement element)
{
instance.InternalUnRegisterCanvasElementForLayoutRebuild(element);
instance.InternalUnRegisterCanvasElementForGraphicRebuild(element);
}
/// <summary>
/// Disable the given element from both the graphic and the layout rebuild lists.
/// </summary>
/// <param name="element"></param>
public static void DisableCanvasElementForRebuild(ICanvasElement element)
{
instance.InternalDisableCanvasElementForLayoutRebuild(element);
instance.InternalDisableCanvasElementForGraphicRebuild(element);
}
private void InternalUnRegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
if (m_PerformingLayoutUpdate)
{
Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element));
return;
}
element.LayoutComplete();
instance.m_LayoutRebuildQueue.Remove(element);
}
private void InternalUnRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
if (m_PerformingGraphicUpdate)
{
Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element));
return;
}
element.GraphicUpdateComplete();
instance.m_GraphicRebuildQueue.Remove(element);
}
private void InternalDisableCanvasElementForLayoutRebuild(ICanvasElement element)
{
if (m_PerformingLayoutUpdate)
{
Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element));
return;
}
element.LayoutComplete();
instance.m_LayoutRebuildQueue.DisableItem(element);
}
private void InternalDisableCanvasElementForGraphicRebuild(ICanvasElement element)
{
if (m_PerformingGraphicUpdate)
{
Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element));
return;
}
element.GraphicUpdateComplete();
instance.m_GraphicRebuildQueue.DisableItem(element);
}
/// <summary>
/// Are graphics layouts currently being calculated..
/// </summary>
/// <returns>True if the rebuild loop is CanvasUpdate.Prelayout, CanvasUpdate.Layout or CanvasUpdate.Postlayout</returns>
public static bool IsRebuildingLayout()
{
return instance.m_PerformingLayoutUpdate;
}
/// <summary>
/// Are graphics currently being rebuild.
/// </summary>
/// <returns>True if the rebuild loop is CanvasUpdate.PreRender or CanvasUpdate.Render</returns>
public static bool IsRebuildingGraphics()
{
return instance.m_PerformingGraphicUpdate;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bfb788a43e03363419155b8af77da971
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,243 @@
using System;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
[Serializable]
/// <summary>
/// Structure that stores the state of a color transition on a Selectable.
/// </summary>
public struct ColorBlock : IEquatable<ColorBlock>
{
[FormerlySerializedAs("normalColor")]
[SerializeField]
private Color m_NormalColor;
[FormerlySerializedAs("highlightedColor")]
[SerializeField]
private Color m_HighlightedColor;
[FormerlySerializedAs("pressedColor")]
[SerializeField]
private Color m_PressedColor;
[FormerlySerializedAs("m_HighlightedColor")]
[SerializeField]
private Color m_SelectedColor;
[FormerlySerializedAs("disabledColor")]
[SerializeField]
private Color m_DisabledColor;
[Range(1, 5)]
[SerializeField]
private float m_ColorMultiplier;
[FormerlySerializedAs("fadeDuration")]
[SerializeField]
private float m_FadeDuration;
/// <summary>
/// The normal color for this color block.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Button button;
/// public Color newColor;
///
/// void Start()
/// {
/// //Changes the button's Normal color to the new color.
/// ColorBlock cb = button.colors;
/// cb.normalColor = newColor;
/// button.colors = cb;
/// }
/// }
/// ]]>
///</code>
/// </example>
public Color normalColor { get { return m_NormalColor; } set { m_NormalColor = value; } }
/// <summary>
/// The highlight color for this color block.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Button button;
/// public Color newColor;
///
/// void Start()
/// {
/// //Changes the button's Highlighted color to the new color.
/// ColorBlock cb = button.colors;
/// cb.highlightedColor = newColor;
/// button.colors = cb;
/// }
/// }
/// ]]>
///</code>
/// </example>
public Color highlightedColor { get { return m_HighlightedColor; } set { m_HighlightedColor = value; } }
/// <summary>
/// The pressed color for this color block.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Button button;
/// public Color newColor;
///
/// void Start()
/// {
/// //Changes the button's Pressed color to the new color.
/// ColorBlock cb = button.colors;
/// cb.pressedColor = newColor;
/// button.colors = cb;
/// }
/// }
/// ]]>
///</code>
/// </example>
public Color pressedColor { get { return m_PressedColor; } set { m_PressedColor = value; } }
/// <summary>
/// The selected color for this color block.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Button button;
/// public Color newColor;
///
/// void Start()
/// {
/// //Changes the button's Selected color to the new color.
/// ColorBlock cb = button.colors;
/// cb.selectedColor = newColor;
/// button.colors = cb;
/// }
/// }
/// ]]>
///</code>
/// </example>
public Color selectedColor { get { return m_SelectedColor; } set { m_SelectedColor = value; } }
/// <summary>
/// The disabled color for this color block.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class ExampleClass : MonoBehaviour
/// {
/// public Button button;
/// public Color newColor;
///
/// void Start()
/// {
/// //Changes the button's Disabled color to the new color.
/// ColorBlock cb = button.colors;
/// cb.disabledColor = newColor;
/// button.colors = cb;
/// }
/// }
/// ]]>
///</code>
/// </example>
public Color disabledColor { get { return m_DisabledColor; } set { m_DisabledColor = value; } }
/// <summary>
/// Multiplier applied to colors (allows brightening greater then base color).
/// </summary>
public float colorMultiplier { get { return m_ColorMultiplier; } set { m_ColorMultiplier = value; } }
/// <summary>
/// How long a color transition between states should take.
/// </summary>
public float fadeDuration { get { return m_FadeDuration; } set { m_FadeDuration = value; } }
/// <summary>
/// Simple getter for a code generated default ColorBlock.
/// </summary>
public static ColorBlock defaultColorBlock;
static ColorBlock()
{
defaultColorBlock = new ColorBlock
{
m_NormalColor = new Color32(255, 255, 255, 255),
m_HighlightedColor = new Color32(245, 245, 245, 255),
m_PressedColor = new Color32(200, 200, 200, 255),
m_SelectedColor = new Color32(245, 245, 245, 255),
m_DisabledColor = new Color32(200, 200, 200, 128),
colorMultiplier = 1.0f,
fadeDuration = 0.1f
};
}
public override bool Equals(object obj)
{
if (!(obj is ColorBlock))
return false;
return Equals((ColorBlock)obj);
}
public bool Equals(ColorBlock other)
{
return normalColor == other.normalColor &&
highlightedColor == other.highlightedColor &&
pressedColor == other.pressedColor &&
selectedColor == other.selectedColor &&
disabledColor == other.disabledColor &&
colorMultiplier == other.colorMultiplier &&
fadeDuration == other.fadeDuration;
}
public static bool operator==(ColorBlock point1, ColorBlock point2)
{
return point1.Equals(point2);
}
public static bool operator!=(ColorBlock point1, ColorBlock point2)
{
return !point1.Equals(point2);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83088ba2132cbc940b7ca0c679a02b0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 27ed3e221887b3544bd9d6505d4a789f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
using System.Collections.Generic;
using UnityEngine.UI.Collections;
namespace UnityEngine.UI
{
/// <summary>
/// Registry class to keep track of all IClippers that exist in the scene
/// </summary>
/// <remarks>
/// This is used during the CanvasUpdate loop to cull clippable elements. The clipping is called after layout, but before Graphic update.
/// </remarks>
public class ClipperRegistry
{
static ClipperRegistry s_Instance;
readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>();
protected ClipperRegistry()
{
// This is needed for AOT platforms. Without it the compile doesn't get the definition of the Dictionarys
#pragma warning disable 168
Dictionary<IClipper, int> emptyIClipperDic;
#pragma warning restore 168
}
/// <summary>
/// The singleton instance of the clipper registry.
/// </summary>
public static ClipperRegistry instance
{
get
{
if (s_Instance == null)
s_Instance = new ClipperRegistry();
return s_Instance;
}
}
/// <summary>
/// Perform the clipping on all registered IClipper
/// </summary>
public void Cull()
{
var clippersCount = m_Clippers.Count;
for (var i = 0; i < clippersCount; ++i)
{
m_Clippers[i].PerformClipping();
}
}
/// <summary>
/// Register a unique IClipper element
/// </summary>
/// <param name="c">The clipper element to add</param>
public static void Register(IClipper c)
{
if (c == null)
return;
instance.m_Clippers.AddUnique(c);
}
/// <summary>
/// UnRegister a IClipper element
/// </summary>
/// <param name="c">The Element to try and remove.</param>
public static void Unregister(IClipper c)
{
instance.m_Clippers.Remove(c);
}
/// <summary>
/// Disable a IClipper element
/// </summary>
/// <param name="c">The Element to try and disable.</param>
public static void Disable(IClipper c)
{
instance.m_Clippers.DisableItem(c);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f1026265f8e3d54fb6e9f082c43debf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
namespace UnityEngine.UI
{
/// <summary>
/// Utility class to help when clipping using IClipper.
/// </summary>
public static class Clipping
{
/// <summary>
/// Find the Rect to use for clipping.
/// Given the input RectMask2ds find a rectangle that is the overlap of all the inputs.
/// </summary>
/// <param name="rectMaskParents">RectMasks to build the overlap rect from.</param>
/// <param name="validRect">Was there a valid Rect found.</param>
/// <returns>The final compounded overlapping rect</returns>
public static Rect FindCullAndClipWorldRect(List<RectMask2D> rectMaskParents, out bool validRect)
{
if (rectMaskParents.Count == 0)
{
validRect = false;
return new Rect();
}
Rect current = rectMaskParents[0].canvasRect;
Vector4 offset = rectMaskParents[0].padding;
float xMin = current.xMin + offset.x;
float xMax = current.xMax - offset.z;
float yMin = current.yMin + offset.y;
float yMax = current.yMax - offset.w;
var rectMaskParentsCount = rectMaskParents.Count;
for (var i = 1; i < rectMaskParentsCount; ++i)
{
current = rectMaskParents[i].canvasRect;
offset = rectMaskParents[i].padding;
if (xMin < current.xMin + offset.x)
xMin = current.xMin + offset.x;
if (yMin < current.yMin + offset.y)
yMin = current.yMin + offset.y;
if (xMax > current.xMax - offset.z)
xMax = current.xMax - offset.z;
if (yMax > current.yMax - offset.w)
yMax = current.yMax - offset.w;
}
validRect = xMax > xMin && yMax > yMin;
return validRect ? new Rect(xMin, yMin, xMax - xMin, yMax - yMin) : new Rect();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67e3583b91179094094c6a188b232262
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
namespace UnityEngine.UI
{
/// <summary>
/// Interface that can be used to recieve clipping callbacks as part of the canvas update loop.
/// </summary>
public interface IClipper
{
/// <summary>
/// Function to to cull / clip children elements.
/// </summary>
/// <remarks>
/// Called after layout and before Graphic update of the Canvas update loop.
/// </remarks>
void PerformClipping();
}
/// <summary>
/// Interface for elements that can be clipped if they are under an IClipper
/// </summary>
public interface IClippable
{
/// <summary>
/// GameObject of the IClippable object
/// </summary>
GameObject gameObject { get; }
/// <summary>
/// Will be called when the state of a parent IClippable changed.
/// </summary>
void RecalculateClipping();
/// <summary>
/// The RectTransform of the clippable.
/// </summary>
RectTransform rectTransform { get; }
/// <summary>
/// Clip and cull the IClippable given a specific clipping rect
/// </summary>
/// <param name="clipRect">The Rectangle in which to clip against.</param>
/// <param name="validRect">Is the Rect valid. If not then the rect has 0 size.</param>
void Cull(Rect clipRect, bool validRect);
/// <summary>
/// Set the clip rect for the IClippable.
/// </summary>
/// <param name="value">The Rectangle for the clipping</param>
/// <param name="validRect">Is the rect valid.</param>
void SetClipRect(Rect value, bool validRect);
/// <summary>
/// Set the clip softness for the IClippable.
///
/// The softness is a linear alpha falloff over clipSoftness pixels.
/// </summary>
/// <param name="clipSoftness">The number of pixels to apply the softness to </param>
void SetClipSoftness(Vector2 clipSoftness);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 371b6b9c8adc50745ace66a6fdf67481
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
namespace UnityEngine.UI
{
internal class RectangularVertexClipper
{
readonly Vector3[] m_WorldCorners = new Vector3[4];
readonly Vector3[] m_CanvasCorners = new Vector3[4];
public Rect GetCanvasRect(RectTransform t, Canvas c)
{
if (c == null)
return new Rect();
t.GetWorldCorners(m_WorldCorners);
var canvasTransform = c.GetComponent<Transform>();
for (int i = 0; i < 4; ++i)
m_CanvasCorners[i] = canvasTransform.InverseTransformPoint(m_WorldCorners[i]);
return new Rect(m_CanvasCorners[0].x, m_CanvasCorners[0].y, m_CanvasCorners[2].x - m_CanvasCorners[0].x, m_CanvasCorners[2].y - m_CanvasCorners[0].y);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c606041f02fc7d542b1429806872292b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf87e20fb5440ec498f441167b5291f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d0b652f32a2cc243917e4028fa0f046
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,199 @@
using System;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
[Serializable]
/// <summary>
/// Struct for storing Text generation settings.
/// </summary>
public class FontData : ISerializationCallbackReceiver
{
[SerializeField]
[FormerlySerializedAs("font")]
private Font m_Font;
[SerializeField]
[FormerlySerializedAs("fontSize")]
private int m_FontSize;
[SerializeField]
[FormerlySerializedAs("fontStyle")]
private FontStyle m_FontStyle;
[SerializeField]
private bool m_BestFit;
[SerializeField]
private int m_MinSize;
[SerializeField]
private int m_MaxSize;
[SerializeField]
[FormerlySerializedAs("alignment")]
private TextAnchor m_Alignment;
[SerializeField]
private bool m_AlignByGeometry;
[SerializeField]
[FormerlySerializedAs("richText")]
private bool m_RichText;
[SerializeField]
private HorizontalWrapMode m_HorizontalOverflow;
[SerializeField]
private VerticalWrapMode m_VerticalOverflow;
[SerializeField]
private float m_LineSpacing;
/// <summary>
/// Get a font data with sensible defaults.
/// </summary>
public static FontData defaultFontData
{
get
{
var fontData = new FontData
{
m_FontSize = 14,
m_LineSpacing = 1f,
m_FontStyle = FontStyle.Normal,
m_BestFit = false,
m_MinSize = 10,
m_MaxSize = 40,
m_Alignment = TextAnchor.UpperLeft,
m_HorizontalOverflow = HorizontalWrapMode.Wrap,
m_VerticalOverflow = VerticalWrapMode.Truncate,
m_RichText = true,
m_AlignByGeometry = false
};
return fontData;
}
}
/// <summary>
/// The Font to use for this generated Text object.
/// </summary>
public Font font
{
get { return m_Font; }
set { m_Font = value; }
}
/// <summary>
/// The Font size to use for this generated Text object.
/// </summary>
public int fontSize
{
get { return m_FontSize; }
set { m_FontSize = value; }
}
/// <summary>
/// The font style to use for this generated Text object.
/// </summary>
public FontStyle fontStyle
{
get { return m_FontStyle; }
set { m_FontStyle = value; }
}
/// <summary>
/// Is best fit used for this generated Text object.
/// </summary>
public bool bestFit
{
get { return m_BestFit; }
set { m_BestFit = value; }
}
/// <summary>
/// The min size for this generated Text object.
/// </summary>
public int minSize
{
get { return m_MinSize; }
set { m_MinSize = value; }
}
/// <summary>
/// The max size for this generated Text object.
/// </summary>
public int maxSize
{
get { return m_MaxSize; }
set { m_MaxSize = value; }
}
/// <summary>
/// How is the text aligned for this generated Text object.
/// </summary>
public TextAnchor alignment
{
get { return m_Alignment; }
set { m_Alignment = value; }
}
/// <summary>
/// Use the extents of glyph geometry to perform horizontal alignment rather than glyph metrics.
/// </summary>
/// <remarks>
/// This can result in better fitting left and right alignment, but may result in incorrect positioning when attempting to overlay multiple fonts (such as a specialized outline font) on top of each other.
/// </remarks>
public bool alignByGeometry
{
get { return m_AlignByGeometry; }
set { m_AlignByGeometry = value; }
}
/// <summary>
/// Should rich text be used for this generated Text object.
/// </summary>
public bool richText
{
get { return m_RichText; }
set { m_RichText = value; }
}
/// <summary>
/// The horizontal overflow policy for this generated Text object.
/// </summary>
public HorizontalWrapMode horizontalOverflow
{
get { return m_HorizontalOverflow; }
set { m_HorizontalOverflow = value; }
}
/// <summary>
/// The vertical overflow policy for this generated Text object.
/// </summary>
public VerticalWrapMode verticalOverflow
{
get { return m_VerticalOverflow; }
set { m_VerticalOverflow = value; }
}
/// <summary>
/// The line spaceing for this generated Text object.
/// </summary>
public float lineSpacing
{
get { return m_LineSpacing; }
set { m_LineSpacing = value; }
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
m_FontSize = Mathf.Clamp(m_FontSize, 0, 300);
m_MinSize = Mathf.Clamp(m_MinSize, 0, m_FontSize);
m_MaxSize = Mathf.Clamp(m_MaxSize, m_FontSize, 300);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13cd966480ec3354bb318ee1aeccff6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.UI
{
/// <summary>
/// Utility class that is used to help with Text update.
/// </summary>
/// <remarks>
/// When Unity rebuilds a font atlas a callback is sent to the font. Using this class you can register your text as needing to be rebuilt if the font atlas is updated.
/// </remarks>
public static class FontUpdateTracker
{
static Dictionary<Font, HashSet<Text>> m_Tracked = new Dictionary<Font, HashSet<Text>>();
/// <summary>
/// Register a Text element for receiving texture atlas rebuild calls.
/// </summary>
/// <param name="t">The Text object to track</param>
public static void TrackText(Text t)
{
if (t.font == null)
return;
HashSet<Text> exists;
m_Tracked.TryGetValue(t.font, out exists);
if (exists == null)
{
// The textureRebuilt event is global for all fonts, so we add our delegate the first time we register *any* Text
if (m_Tracked.Count == 0)
Font.textureRebuilt += RebuildForFont;
exists = new HashSet<Text>();
m_Tracked.Add(t.font, exists);
}
exists.Add(t);
}
private static void RebuildForFont(Font f)
{
HashSet<Text> texts;
m_Tracked.TryGetValue(f, out texts);
if (texts == null)
return;
foreach (var text in texts)
text.FontTextureChanged();
}
/// <summary>
/// Deregister a Text element from receiving texture atlas rebuild calls.
/// </summary>
/// <param name="t">The Text object to no longer track</param>
public static void UntrackText(Text t)
{
if (t.font == null)
return;
HashSet<Text> texts;
m_Tracked.TryGetValue(t.font, out texts);
if (texts == null)
return;
texts.Remove(t);
if (texts.Count == 0)
{
m_Tracked.Remove(t.font);
// There is a global textureRebuilt event for all fonts, so once the last Text reference goes away, remove our delegate
if (m_Tracked.Count == 0)
Font.textureRebuilt -= RebuildForFont;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39ab466162988eb4f83443f911bbf5c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6238e9452bfc704f82ff36791fe1a45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,340 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace UnityEngine.UI
{
[AddComponentMenu("Event/Graphic Raycaster")]
[RequireComponent(typeof(Canvas))]
/// <summary>
/// A derived BaseRaycaster to raycast against Graphic elements.
/// </summary>
public class GraphicRaycaster : BaseRaycaster
{
protected const int kNoEventMaskSet = -1;
/// <summary>
/// Type of raycasters to check against to check for canvas blocking elements.
/// </summary>
public enum BlockingObjects
{
/// <summary>
/// Perform no raycasts.
/// </summary>
None = 0,
/// <summary>
/// Perform a 2D raycast check to check for blocking 2D elements
/// </summary>
TwoD = 1,
/// <summary>
/// Perform a 3D raycast check to check for blocking 3D elements
/// </summary>
ThreeD = 2,
/// <summary>
/// Perform a 2D and a 3D raycasts to check for blocking 2D and 3D elements.
/// </summary>
All = 3,
}
/// <summary>
/// Priority of the raycaster based upon sort order.
/// </summary>
/// <returns>
/// The sortOrder priority.
/// </returns>
public override int sortOrderPriority
{
get
{
// We need to return the sorting order here as distance will all be 0 for overlay.
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
return canvas.sortingOrder;
return base.sortOrderPriority;
}
}
/// <summary>
/// Priority of the raycaster based upon render order.
/// </summary>
/// <returns>
/// The renderOrder priority.
/// </returns>
public override int renderOrderPriority
{
get
{
// We need to return the sorting order here as distance will all be 0 for overlay.
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
return canvas.rootCanvas.renderOrder;
return base.renderOrderPriority;
}
}
[FormerlySerializedAs("ignoreReversedGraphics")]
[SerializeField]
private bool m_IgnoreReversedGraphics = true;
[FormerlySerializedAs("blockingObjects")]
[SerializeField]
private BlockingObjects m_BlockingObjects = BlockingObjects.None;
/// <summary>
/// Whether Graphics facing away from the raycaster are checked for raycasts.
/// </summary>
public bool ignoreReversedGraphics { get {return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } }
/// <summary>
/// The type of objects that are checked to determine if they block graphic raycasts.
/// </summary>
public BlockingObjects blockingObjects { get {return m_BlockingObjects; } set { m_BlockingObjects = value; } }
[SerializeField]
protected LayerMask m_BlockingMask = kNoEventMaskSet;
/// <summary>
/// The type of objects specified through LayerMask that are checked to determine if they block graphic raycasts.
/// </summary>
public LayerMask blockingMask { get { return m_BlockingMask; } set { m_BlockingMask = value; } }
private Canvas m_Canvas;
protected GraphicRaycaster()
{}
private Canvas canvas
{
get
{
if (m_Canvas != null)
return m_Canvas;
m_Canvas = GetComponent<Canvas>();
return m_Canvas;
}
}
[NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>();
/// <summary>
/// Perform the raycast against the list of graphics associated with the Canvas.
/// </summary>
/// <param name="eventData">Current event data</param>
/// <param name="resultAppendList">List of hit objects to append new results to.</param>
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
if (canvas == null)
return;
var canvasGraphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
if (canvasGraphics == null || canvasGraphics.Count == 0)
return;
int displayIndex;
var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
displayIndex = canvas.targetDisplay;
else
displayIndex = currentEventCamera.targetDisplay;
Vector3 eventPosition = MultipleDisplayUtilities.GetRelativeMousePositionForRaycast(eventData);
// Discard events that are not part of this display so the user does not interact with multiple displays at once.
if ((int) eventPosition.z != displayIndex)
return;
// Convert to view space
Vector2 pos;
if (currentEventCamera == null)
{
// Multiple display support only when not the main display. For display 0 the reported
// resolution is always the desktops resolution since its part of the display API,
// so we use the standard none multiple display method. (case 741751)
float w = Screen.width;
float h = Screen.height;
if (displayIndex > 0 && displayIndex < Display.displays.Length)
{
w = Display.displays[displayIndex].systemWidth;
h = Display.displays[displayIndex].systemHeight;
}
pos = new Vector2(eventPosition.x / w, eventPosition.y / h);
}
else
pos = currentEventCamera.ScreenToViewportPoint(eventPosition);
// If it's outside the camera's viewport, do nothing
if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
return;
float hitDistance = float.MaxValue;
Ray ray = new Ray();
if (currentEventCamera != null)
ray = currentEventCamera.ScreenPointToRay(eventPosition);
if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
{
float distanceToClipPlane = 100.0f;
if (currentEventCamera != null)
{
float projectionDirection = ray.direction.z;
distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
? Mathf.Infinity
: Mathf.Abs((currentEventCamera.farClipPlane - currentEventCamera.nearClipPlane) / projectionDirection);
}
#if PACKAGE_PHYSICS
if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
{
if (ReflectionMethodsCache.Singleton.raycast3D != null)
{
RaycastHit hit;
if (ReflectionMethodsCache.Singleton.raycast3D(ray, out hit, distanceToClipPlane, (int)m_BlockingMask))
{
hitDistance = hit.distance;
}
}
}
#endif
#if PACKAGE_PHYSICS2D
if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
{
if (ReflectionMethodsCache.Singleton.raycast2D != null)
{
var hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask);
if (hits.Length > 0)
hitDistance = hits[0].distance;
}
}
#endif
}
m_RaycastResults.Clear();
Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);
int totalCount = m_RaycastResults.Count;
for (var index = 0; index < totalCount; index++)
{
var go = m_RaycastResults[index].gameObject;
bool appendGraphic = true;
if (ignoreReversedGraphics)
{
if (currentEventCamera == null)
{
// If we dont have a camera we know that we should always be facing forward
var dir = go.transform.rotation * Vector3.forward;
appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
}
else
{
// If we have a camera compare the direction against the cameras forward.
var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
}
}
if (appendGraphic)
{
float distance = 0;
Transform trans = go.transform;
Vector3 transForward = trans.forward;
if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
distance = 0;
else
{
// http://geomalgorithms.com/a06-_intersect-2.html
distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
// Check to see if the go is behind the camera.
if (distance < 0)
continue;
}
if (distance >= hitDistance)
continue;
var castResult = new RaycastResult
{
gameObject = go,
module = this,
distance = distance,
screenPosition = eventPosition,
displayIndex = displayIndex,
index = resultAppendList.Count,
depth = m_RaycastResults[index].depth,
sortingLayer = canvas.sortingLayerID,
sortingOrder = canvas.sortingOrder,
worldPosition = ray.origin + ray.direction * distance,
worldNormal = -transForward
};
resultAppendList.Add(castResult);
}
}
}
/// <summary>
/// The camera that will generate rays for this raycaster.
/// </summary>
/// <returns>
/// - Null if Camera mode is ScreenSpaceOverlay or ScreenSpaceCamera and has no camera.
/// - canvas.worldCanvas if not null
/// - Camera.main.
/// </returns>
public override Camera eventCamera
{
get
{
var canvas = this.canvas;
var renderMode = canvas.renderMode;
if (renderMode == RenderMode.ScreenSpaceOverlay
|| (renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null))
return null;
return canvas.worldCamera ?? Camera.main;
}
}
/// <summary>
/// Perform a raycast into the screen and collect all graphics underneath it.
/// </summary>
[NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
{
// Necessary for the event system
int totalCount = foundGraphics.Count;
for (int i = 0; i < totalCount; ++i)
{
Graphic graphic = foundGraphics[i];
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
continue;
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
continue;
if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
continue;
if (graphic.Raycast(pointerPosition, eventCamera))
{
s_SortedGraphics.Add(graphic);
}
}
s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
totalCount = s_SortedGraphics.Count;
for (int i = 0; i < totalCount; ++i)
results.Add(s_SortedGraphics[i]);
s_SortedGraphics.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc42784cf147c0c48a680349fa168899
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine.UI.Collections;
namespace UnityEngine.UI
{
/// <summary>
/// EditorOnly class for tracking all Graphics.
/// Used when a source asset is reimported into the editor to ensure that Graphics are updated as intended.
/// </summary>
public static class GraphicRebuildTracker
{
static IndexedSet<Graphic> m_Tracked = new IndexedSet<Graphic>();
static bool s_Initialized;
/// <summary>
/// Add a Graphic to the list of tracked Graphics
/// </summary>
/// <param name="g">The graphic to track</param>
public static void TrackGraphic(Graphic g)
{
if (!s_Initialized)
{
CanvasRenderer.onRequestRebuild += OnRebuildRequested;
s_Initialized = true;
}
m_Tracked.AddUnique(g);
}
/// <summary>
/// Remove a Graphic to the list of tracked Graphics
/// </summary>
/// <param name="g">The graphic to remove from tracking.</param>
public static void UnTrackGraphic(Graphic g)
{
m_Tracked.Remove(g);
}
/// <summary>
/// Remove a Graphic to the list of tracked Graphics
/// </summary>
/// <param name="g">The graphic to remove from tracking.</param>
public static void DisableTrackGraphic(Graphic g)
{
m_Tracked.DisableItem(g);
}
static void OnRebuildRequested()
{
StencilMaterial.ClearAll();
for (int i = 0; i < m_Tracked.Count; i++)
{
m_Tracked[i].OnRebuildRequested();
}
}
}
}
#endif // if UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 445cdcfc747eba94288b97f5869aa2fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,212 @@
using System.Collections.Generic;
using UnityEngine.UI.Collections;
namespace UnityEngine.UI
{
/// <summary>
/// Registry which maps a Graphic to the canvas it belongs to.
/// </summary>
public class GraphicRegistry
{
private static GraphicRegistry s_Instance;
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_RaycastableGraphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
protected GraphicRegistry()
{
// Avoid runtime generation of these types. Some platforms are AOT only and do not support
// JIT. What's more we actually create a instance of the required types instead of
// just declaring an unused variable which may be optimized away by some compilers (Mono vs MS).
// See: 877060
System.GC.KeepAlive(new Dictionary<Graphic, int>());
System.GC.KeepAlive(new Dictionary<ICanvasElement, int>());
System.GC.KeepAlive(new Dictionary<IClipper, int>());
}
/// <summary>
/// The singleton instance of the GraphicRegistry. Creates a new instance if it does not exist.
/// </summary>
public static GraphicRegistry instance
{
get
{
if (s_Instance == null)
s_Instance = new GraphicRegistry();
return s_Instance;
}
}
/// <summary>
/// Associates a Graphic with a Canvas and stores this association in the registry.
/// </summary>
/// <param name="c">The canvas being associated with the Graphic.</param>
/// <param name="graphic">The Graphic being associated with the Canvas.</param>
public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null || graphic == null)
return;
IndexedSet<Graphic> graphics;
instance.m_Graphics.TryGetValue(c, out graphics);
if (graphics != null)
{
graphics.AddUnique(graphic);
RegisterRaycastGraphicForCanvas(c, graphic);
return;
}
// Dont need to AddUnique as we know its the only item in the list
graphics = new IndexedSet<Graphic>();
graphics.Add(graphic);
instance.m_Graphics.Add(c, graphics);
RegisterRaycastGraphicForCanvas(c, graphic);
}
/// <summary>
/// Associates a raycastable Graphic with a Canvas and stores this association in the registry.
/// </summary>
/// <param name="c">The canvas being associated with the Graphic.</param>
/// <param name="graphic">The Graphic being associated with the Canvas.</param>
public static void RegisterRaycastGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null || graphic == null || !graphic.raycastTarget)
return;
IndexedSet<Graphic> graphics;
instance.m_RaycastableGraphics.TryGetValue(c, out graphics);
if (graphics != null)
{
graphics.AddUnique(graphic);
return;
}
// Dont need to AddUnique as we know its the only item in the list
graphics = new IndexedSet<Graphic>();
graphics.Add(graphic);
instance.m_RaycastableGraphics.Add(c, graphics);
}
/// <summary>
/// Dissociates a Graphic from a Canvas, removing this association from the registry.
/// </summary>
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
public static void UnregisterGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null || graphic == null)
return;
IndexedSet<Graphic> graphics;
if (instance.m_Graphics.TryGetValue(c, out graphics))
{
graphics.Remove(graphic);
if (graphics.Capacity == 0)
instance.m_Graphics.Remove(c);
UnregisterRaycastGraphicForCanvas(c, graphic);
}
}
/// <summary>
/// Dissociates a Graphic from a Canvas, removing this association from the registry.
/// </summary>
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
public static void UnregisterRaycastGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null || graphic == null)
return;
IndexedSet<Graphic> graphics;
if (instance.m_RaycastableGraphics.TryGetValue(c, out graphics))
{
graphics.Remove(graphic);
if (graphics.Count == 0)
instance.m_RaycastableGraphics.Remove(c);
}
}
/// <summary>
/// Disables a Graphic from a Canvas, disabling this association from the registry.
/// </summary>
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
public static void DisableGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null)
return;
IndexedSet<Graphic> graphics;
if (instance.m_Graphics.TryGetValue(c, out graphics))
{
graphics.DisableItem(graphic);
if (graphics.Capacity == 0)
instance.m_Graphics.Remove(c);
DisableRaycastGraphicForCanvas(c, graphic);
}
}
/// <summary>
/// Disables the raycast for a Graphic from a Canvas, disabling this association from the registry.
/// </summary>
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
public static void DisableRaycastGraphicForCanvas(Canvas c, Graphic graphic)
{
if (c == null || !graphic.raycastTarget)
return;
IndexedSet<Graphic> graphics;
if (instance.m_RaycastableGraphics.TryGetValue(c, out graphics))
{
graphics.DisableItem(graphic);
if (graphics.Capacity == 0)
instance.m_RaycastableGraphics.Remove(c);
}
}
private static readonly List<Graphic> s_EmptyList = new List<Graphic>();
/// <summary>
/// Retrieves the list of Graphics associated with a Canvas.
/// </summary>
/// <param name="canvas">The Canvas to search</param>
/// <returns>Returns a list of Graphics. Returns an empty list if no Graphics are associated with the specified Canvas.</returns>
public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas)
{
IndexedSet<Graphic> graphics;
if (instance.m_Graphics.TryGetValue(canvas, out graphics))
return graphics;
return s_EmptyList;
}
/// <summary>
/// Retrieves the list of Graphics that are raycastable and associated with a Canvas.
/// </summary>
/// <param name="canvas">The Canvas to search</param>
/// <returns>Returns a list of Graphics. Returns an empty list if no Graphics are associated with the specified Canvas.</returns>
public static IList<Graphic> GetRaycastableGraphicsForCanvas(Canvas canvas)
{
IndexedSet<Graphic> graphics;
if (instance.m_RaycastableGraphics.TryGetValue(canvas, out graphics))
return graphics;
return s_EmptyList;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69e3979b7029e8a4da2d96b714ba5c3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using System;
namespace UnityEngine.UI
{
[Obsolete("Not supported anymore")]
interface IGraphicEnabledDisabled
{
void OnSiblingGraphicEnabledDisabled();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 928dc55e2c8c3ee4dad33b6d561cb6ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using System;
namespace UnityEngine.UI
{
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
[Obsolete("Not supported anymore.", true)]
public interface IMask
{
bool Enabled();
RectTransform rectTransform { get; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ceb629fc661813d40986b4abbefe72c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System;
namespace UnityEngine.UI
{
/// <summary>
/// This element is capable of being masked out.
/// </summary>
public interface IMaskable
{
/// <summary>
/// Recalculate masking for this element and all children elements.
/// </summary>
/// <remarks>
/// Use this to update the internal state (recreate materials etc).
/// </remarks>
void RecalculateMasking();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61a20120cddc53849bbc10fc805ffe3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe87c0e1cc204ed48ad3b37840f39efc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d199490a83bb2b844b9695cbf13b01ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1999349e7f492c947bb6eb70f624382e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,256 @@
using UnityEngine.EventSystems;
namespace UnityEngine.UI
{
[AddComponentMenu("Layout/Aspect Ratio Fitter", 142)]
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
/// <summary>
/// Resizes a RectTransform to fit a specified aspect ratio.
/// </summary>
public class AspectRatioFitter : UIBehaviour, ILayoutSelfController
{
/// <summary>
/// Specifies a mode to use to enforce an aspect ratio.
/// </summary>
public enum AspectMode
{
/// <summary>
/// The aspect ratio is not enforced
/// </summary>
None,
/// <summary>
/// Changes the height of the rectangle to match the aspect ratio.
/// </summary>
WidthControlsHeight,
/// <summary>
/// Changes the width of the rectangle to match the aspect ratio.
/// </summary>
HeightControlsWidth,
/// <summary>
/// Sizes the rectangle such that it's fully contained within the parent rectangle.
/// </summary>
FitInParent,
/// <summary>
/// Sizes the rectangle such that the parent rectangle is fully contained within.
/// </summary>
EnvelopeParent
}
[SerializeField] private AspectMode m_AspectMode = AspectMode.None;
/// <summary>
/// The mode to use to enforce the aspect ratio.
/// </summary>
public AspectMode aspectMode { get { return m_AspectMode; } set { if (SetPropertyUtility.SetStruct(ref m_AspectMode, value)) SetDirty(); } }
[SerializeField] private float m_AspectRatio = 1;
/// <summary>
/// The aspect ratio to enforce. This means width divided by height.
/// </summary>
public float aspectRatio { get { return m_AspectRatio; } set { if (SetPropertyUtility.SetStruct(ref m_AspectRatio, value)) SetDirty(); } }
[System.NonSerialized]
private RectTransform m_Rect;
// This "delayed" mechanism is required for case 1014834.
private bool m_DelayedSetDirty = false;
//Does the gameobject has a parent for reference to enable FitToParent/EnvelopeParent modes.
private bool m_DoesParentExist = false;
private RectTransform rectTransform
{
get
{
if (m_Rect == null)
m_Rect = GetComponent<RectTransform>();
return m_Rect;
}
}
// field is never assigned warning
#pragma warning disable 649
private DrivenRectTransformTracker m_Tracker;
#pragma warning restore 649
protected AspectRatioFitter() {}
protected override void OnEnable()
{
base.OnEnable();
m_DoesParentExist = rectTransform.parent ? true : false;
SetDirty();
}
protected override void Start()
{
base.Start();
//Disable the component if the aspect mode is not valid or the object state/setup is not supported with AspectRatio setup.
if (!IsComponentValidOnObject() || !IsAspectModeValid())
this.enabled = false;
}
protected override void OnDisable()
{
m_Tracker.Clear();
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
base.OnDisable();
}
protected override void OnTransformParentChanged()
{
base.OnTransformParentChanged();
m_DoesParentExist = rectTransform.parent ? true : false;
SetDirty();
}
/// <summary>
/// Update the rect based on the delayed dirty.
/// Got around issue of calling onValidate from OnEnable function.
/// </summary>
protected virtual void Update()
{
if (m_DelayedSetDirty)
{
m_DelayedSetDirty = false;
SetDirty();
}
}
/// <summary>
/// Function called when this RectTransform or parent RectTransform has changed dimensions.
/// </summary>
protected override void OnRectTransformDimensionsChange()
{
UpdateRect();
}
private void UpdateRect()
{
if (!IsActive() || !IsComponentValidOnObject())
return;
m_Tracker.Clear();
switch (m_AspectMode)
{
#if UNITY_EDITOR
case AspectMode.None:
{
if (!Application.isPlaying)
m_AspectRatio = Mathf.Clamp(rectTransform.rect.width / rectTransform.rect.height, 0.001f, 1000f);
break;
}
#endif
case AspectMode.HeightControlsWidth:
{
m_Tracker.Add(this, rectTransform, DrivenTransformProperties.SizeDeltaX);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rectTransform.rect.height * m_AspectRatio);
break;
}
case AspectMode.WidthControlsHeight:
{
m_Tracker.Add(this, rectTransform, DrivenTransformProperties.SizeDeltaY);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rectTransform.rect.width / m_AspectRatio);
break;
}
case AspectMode.FitInParent:
case AspectMode.EnvelopeParent:
{
if (!DoesParentExists())
break;
m_Tracker.Add(this, rectTransform,
DrivenTransformProperties.Anchors |
DrivenTransformProperties.AnchoredPosition |
DrivenTransformProperties.SizeDeltaX |
DrivenTransformProperties.SizeDeltaY);
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.anchoredPosition = Vector2.zero;
Vector2 sizeDelta = Vector2.zero;
Vector2 parentSize = GetParentSize();
if ((parentSize.y * aspectRatio < parentSize.x) ^ (m_AspectMode == AspectMode.FitInParent))
{
sizeDelta.y = GetSizeDeltaToProduceSize(parentSize.x / aspectRatio, 1);
}
else
{
sizeDelta.x = GetSizeDeltaToProduceSize(parentSize.y * aspectRatio, 0);
}
rectTransform.sizeDelta = sizeDelta;
break;
}
}
}
private float GetSizeDeltaToProduceSize(float size, int axis)
{
return size - GetParentSize()[axis] * (rectTransform.anchorMax[axis] - rectTransform.anchorMin[axis]);
}
private Vector2 GetParentSize()
{
RectTransform parent = rectTransform.parent as RectTransform;
return !parent ? Vector2.zero : parent.rect.size;
}
/// <summary>
/// Method called by the layout system. Has no effect
/// </summary>
public virtual void SetLayoutHorizontal() {}
/// <summary>
/// Method called by the layout system. Has no effect
/// </summary>
public virtual void SetLayoutVertical() {}
/// <summary>
/// Mark the AspectRatioFitter as dirty.
/// </summary>
protected void SetDirty()
{
UpdateRect();
}
public bool IsComponentValidOnObject()
{
Canvas canvas = gameObject.GetComponent<Canvas>();
if (canvas && canvas.isRootCanvas && canvas.renderMode != RenderMode.WorldSpace)
{
return false;
}
return true;
}
public bool IsAspectModeValid()
{
if (!DoesParentExists() && (aspectMode == AspectMode.EnvelopeParent || aspectMode == AspectMode.FitInParent))
return false;
return true;
}
private bool DoesParentExists()
{
return m_DoesParentExist;
}
#if UNITY_EDITOR
protected override void OnValidate()
{
m_AspectRatio = Mathf.Clamp(m_AspectRatio, 0.001f, 1000f);
m_DelayedSetDirty = true;
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86710e43de46f6f4bac7c8e50813a599
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More