验证码功能

This commit is contained in:
2025-08-08 12:36:25 +08:00
parent c07242fb6a
commit f1aeeb6bf3
32 changed files with 6901 additions and 9136 deletions
+853
View File
@@ -0,0 +1,853 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AibisDream
{
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
public class SliderEx : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
{
/// <summary>
/// Setting that indicates one of four directions.
/// </summary>
public enum Direction
{
/// <summary>
/// From the left to the right
/// </summary>
LeftToRight,
/// <summary>
/// From the right to the left
/// </summary>
RightToLeft,
/// <summary>
/// From the bottom to the top.
/// </summary>
BottomToTop,
/// <summary>
/// From the top to the bottom.
/// </summary>
TopToBottom,
}
private bool _isHolding;
[SerializeField] private RectTransform m_FillRect;
/// <summary>
/// Optional RectTransform to use as fill for the slider.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
/// //Reference to new "RectTransform"(Child of FillArea).
/// public RectTransform newFillRect;
///
/// //Deactivates the old FillRect and assigns a new one.
/// void Start()
/// {
/// mainSlider.fillRect.gameObject.SetActive(false);
/// mainSlider.fillRect = newFillRect;
/// }
/// }
/// ]]>
///</code>
/// </example>
public RectTransform fillRect
{
get => m_FillRect;
set
{
if (SetPropertyUtility.SetClass(ref m_FillRect, value))
{
UpdateCachedReferences();
UpdateVisuals();
}
}
}
[SerializeField] private RectTransform m_HandleRect;
/// <summary>
/// Optional RectTransform to use as a handle for the slider.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
/// //Reference to new "RectTransform" (Child of "Handle Slide Area").
/// public RectTransform handleHighlighted;
///
/// //Deactivates the old Handle, then assigns and enables the new one.
/// void Start()
/// {
/// mainSlider.handleRect.gameObject.SetActive(false);
/// mainSlider.handleRect = handleHighlighted;
/// mainSlider.handleRect.gameObject.SetActive(true);
/// }
/// }
/// ]]>
///</code>
/// </example>
public RectTransform handleRect
{
get => m_HandleRect;
set
{
if (SetPropertyUtility.SetClass(ref m_HandleRect, value))
{
UpdateCachedReferences();
UpdateVisuals();
}
}
}
[Space] [SerializeField] private Direction m_Direction = Direction.LeftToRight;
/// <summary>
/// The direction of the slider, from minimum to maximum value.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// public void Start()
/// {
/// //Changes the direction of the slider.
/// if (mainDirection == Direction.BottomToTop)
/// {
/// mainDirection = Direction.TopToBottom;
/// }
/// }
/// }
/// ]]>
///</code>
/// </example>
public Direction direction
{
get => m_Direction;
set
{
if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals();
}
}
[SerializeField] private float m_MinValue = 0;
/// <summary>
/// The minimum allowed value of the slider.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// void Start()
/// {
/// // Changes the minimum value of the slider to 10;
/// mainSlider.minValue = 10;
/// }
/// }
/// ]]>
///</code>
/// </example>
public float minValue
{
get => m_MinValue;
set
{
if (SetPropertyUtility.SetStruct(ref m_MinValue, value))
{
Set(m_Value);
UpdateVisuals();
}
}
}
[SerializeField] private float m_MaxValue = 1;
/// <summary>
/// The maximum allowed value of the slider.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// void Start()
/// {
/// // Changes the max value of the slider to 20;
/// mainSlider.maxValue = 20;
/// }
/// }
/// ]]>
///</code>
/// </example>
public float maxValue
{
get => m_MaxValue;
set
{
if (SetPropertyUtility.SetStruct(ref m_MaxValue, value))
{
Set(m_Value);
UpdateVisuals();
}
}
}
[SerializeField] private bool m_WholeNumbers = false;
/// <summary>
/// Should the value only be allowed to be whole numbers?
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// public void Start()
/// {
/// //sets the slider's value to accept whole numbers only.
/// mainSlider.wholeNumbers = true;
/// }
/// }
/// ]]>
///</code>
/// </example>
public bool wholeNumbers
{
get => m_WholeNumbers;
set
{
if (SetPropertyUtility.SetStruct(ref m_WholeNumbers, value))
{
Set(m_Value);
UpdateVisuals();
}
}
}
[SerializeField] protected float m_Value;
/// <summary>
/// The current value of the slider.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// //Invoked when a submit button is clicked.
/// public void SubmitSliderSetting()
/// {
/// //Displays the value of the slider in the console.
/// Debug.Log(mainSlider.value);
/// }
/// }
/// ]]>
///</code>
/// </example>
public virtual float value
{
get => wholeNumbers ? Mathf.Round(m_Value) : m_Value;
set => Set(value);
}
/// <summary>
/// Set the value of the slider without invoking onValueChanged callback.
/// </summary>
/// <param name="input">The new value for the slider.</param>
public virtual void SetValueWithoutNotify(float input)
{
Set(input, false);
}
/// <summary>
/// The current value of the slider normalized into a value between 0 and 1.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// //Set to invoke when "OnValueChanged" method is called.
/// void CheckNormalisedValue()
/// {
/// //Displays the normalised value of the slider everytime the value changes.
/// Debug.Log(mainSlider.normalizedValue);
/// }
/// }
/// ]]>
///</code>
/// </example>
public float NormalizedValue
{
get
{
if (Mathf.Approximately(minValue, maxValue))
return 0;
return Mathf.InverseLerp(minValue, maxValue, value);
}
set => this.value = Mathf.Lerp(minValue, maxValue, value);
}
[Space] [SerializeField] private UnityEvent<float> m_OnValueChanged = new();
/// <summary>
/// Callback executed when the value of the slider is changed.
/// </summary>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// public void Start()
/// {
/// //Adds a listener to the main slider and invokes a method when the value changes.
/// mainSlider.onValueChanged.AddListener(delegate {ValueChangeCheck(); });
/// }
///
/// // Invoked when the value of the slider changes.
/// public void ValueChangeCheck()
/// {
/// Debug.Log(mainSlider.value);
/// }
/// }
/// ]]>
///</code>
/// </example>
public UnityEvent<float> onValueChanged
{
get => m_OnValueChanged;
set => m_OnValueChanged = value;
}
[SerializeField] private UnityEvent m_OnStartDrag = new();
public UnityEvent onStartDrag
{
get => m_OnStartDrag;
set => m_OnStartDrag = value;
}
[SerializeField] private UnityEvent<float> m_OnEndDrag = new();
public UnityEvent<float> onEndDrag
{
get => m_OnEndDrag;
set => m_OnEndDrag = value;
}
// Private fields
private Image m_FillImage;
private Transform m_FillTransform;
private RectTransform m_FillContainerRect;
private Transform m_HandleTransform;
private RectTransform m_HandleContainerRect;
// The offset from handle position to mouse down position
private Vector2 m_Offset = Vector2.zero;
// field is never assigned warning
#pragma warning disable 649
private DrivenRectTransformTracker m_Tracker;
#pragma warning restore 649
// This "delayed" mechanism is required for case 1037681.
private bool m_DelayedUpdateVisuals = false;
// Size of each step.
float stepSize => wholeNumbers ? 1 : (maxValue - minValue) * 0.1f;
protected SliderEx()
{
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
if (wholeNumbers)
{
m_MinValue = Mathf.Round(m_MinValue);
m_MaxValue = Mathf.Round(m_MaxValue);
}
//Onvalidate is called before OnEnabled. We need to make sure not to touch any other objects before OnEnable is run.
if (IsActive())
{
UpdateCachedReferences();
// Update rects in next update since other things might affect them even if value didn't change.
m_DelayedUpdateVisuals = true;
}
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
}
#endif // if UNITY_EDITOR
public virtual void Rebuild(CanvasUpdate executing)
{
#if UNITY_EDITOR
if (executing == CanvasUpdate.Prelayout)
onValueChanged.Invoke(value);
#endif
}
/// <summary>
/// See ICanvasElement.LayoutComplete
/// </summary>
public virtual void LayoutComplete()
{
}
/// <summary>
/// See ICanvasElement.GraphicUpdateComplete
/// </summary>
public virtual void GraphicUpdateComplete()
{
}
protected override void OnEnable()
{
base.OnEnable();
UpdateCachedReferences();
Set(m_Value, false);
// Update rects since they need to be initialized correctly.
UpdateVisuals();
}
protected override void OnDisable()
{
m_Tracker.Clear();
base.OnDisable();
}
/// <summary>
/// Update the rect based on the delayed update visuals.
/// Got around issue of calling sendMessage from onValidate.
/// </summary>
protected virtual void Update()
{
if (m_DelayedUpdateVisuals)
{
m_DelayedUpdateVisuals = false;
Set(m_Value, false);
UpdateVisuals();
}
}
protected override void OnDidApplyAnimationProperties()
{
// Has value changed? Various elements of the slider have the old normalisedValue assigned, we can use this to perform a comparison.
// We also need to ensure the value stays within min/max.
m_Value = ClampValue(m_Value);
float oldNormalizedValue = NormalizedValue;
if (m_FillContainerRect != null)
{
if (m_FillImage != null && m_FillImage.type == Image.Type.Filled)
oldNormalizedValue = m_FillImage.fillAmount;
else
oldNormalizedValue =
(ReverseValue ? 1 - m_FillRect.anchorMin[(int)axis] : m_FillRect.anchorMax[(int)axis]);
}
else if (m_HandleContainerRect != null)
oldNormalizedValue =
(ReverseValue ? 1 - m_HandleRect.anchorMin[(int)axis] : m_HandleRect.anchorMin[(int)axis]);
UpdateVisuals();
if (!Mathf.Approximately(oldNormalizedValue, NormalizedValue))
{
UISystemProfilerApi.AddMarker("Slider.value", this);
onValueChanged.Invoke(m_Value);
}
// UUM-34170 Apparently, some properties on slider such as IsInteractable and Normalcolor Animation is broken.
// We need to call base here to render the animation on Scene
base.OnDidApplyAnimationProperties();
}
void UpdateCachedReferences()
{
if (m_FillRect && m_FillRect != (RectTransform)transform)
{
m_FillTransform = m_FillRect.transform;
m_FillImage = m_FillRect.GetComponent<Image>();
if (m_FillTransform.parent != null)
m_FillContainerRect = m_FillTransform.parent.GetComponent<RectTransform>();
}
else
{
m_FillRect = null;
m_FillContainerRect = null;
m_FillImage = null;
}
if (m_HandleRect && m_HandleRect != (RectTransform)transform)
{
m_HandleTransform = m_HandleRect.transform;
if (m_HandleTransform.parent != null)
m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
}
else
{
m_HandleRect = null;
m_HandleContainerRect = null;
}
}
float ClampValue(float input)
{
float newValue = Mathf.Clamp(input, minValue, maxValue);
if (wholeNumbers)
newValue = Mathf.Round(newValue);
return newValue;
}
/// <summary>
/// Set the value of the slider.
/// </summary>
/// <param name="input">The new value for the slider.</param>
/// <param name="sendCallback">If the OnValueChanged callback should be invoked.</param>
/// <remarks>
/// Process the input to ensure the value is between min and max value. If the input is different set the value and send the callback is required.
/// </remarks>
protected virtual void Set(float input, bool sendCallback = true)
{
// Clamp the input
float newValue = ClampValue(input);
// If the stepped value doesn't match the last one, it's time to update
if (Mathf.Approximately(m_Value, newValue))
return;
m_Value = newValue;
UpdateVisuals();
if (sendCallback)
{
UISystemProfilerApi.AddMarker("Slider.value", this);
m_OnValueChanged.Invoke(newValue);
}
}
protected override void OnRectTransformDimensionsChange()
{
base.OnRectTransformDimensionsChange();
//This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
if (!IsActive())
return;
UpdateVisuals();
}
enum Axis
{
Horizontal = 0,
Vertical = 1
}
Axis axis =>
m_Direction is Direction.LeftToRight or Direction.RightToLeft
? Axis.Horizontal
: Axis.Vertical;
private bool ReverseValue => m_Direction is Direction.RightToLeft or Direction.TopToBottom;
// Force-update the slider. Useful if you've changed the properties and want it to update visually.
private void UpdateVisuals()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
UpdateCachedReferences();
#endif
m_Tracker.Clear();
if (m_FillContainerRect != null)
{
m_Tracker.Add(this, m_FillRect, DrivenTransformProperties.Anchors);
Vector2 anchorMin = Vector2.zero;
Vector2 anchorMax = Vector2.one;
if (m_FillImage != null && m_FillImage.type == Image.Type.Filled)
{
m_FillImage.fillAmount = NormalizedValue;
}
else
{
if (ReverseValue)
anchorMin[(int)axis] = 1 - NormalizedValue;
else
anchorMax[(int)axis] = NormalizedValue;
}
m_FillRect.anchorMin = anchorMin;
m_FillRect.anchorMax = anchorMax;
}
if (m_HandleContainerRect != null)
{
m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
Vector2 anchorMin = Vector2.zero;
Vector2 anchorMax = Vector2.one;
anchorMin[(int)axis] = anchorMax[(int)axis] = (ReverseValue ? (1 - NormalizedValue) : NormalizedValue);
m_HandleRect.anchorMin = anchorMin;
m_HandleRect.anchorMax = anchorMax;
}
}
// Update the slider's position based on the mouse.
void UpdateDrag(PointerEventData eventData, Camera cam)
{
RectTransform clickRect = m_HandleContainerRect ?? m_FillContainerRect;
if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
{
Vector2 position = Vector2.zero;
if (!MultipleDisplayUtilities.GetRelativeMousePositionForDrag(eventData, ref position))
return;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, position, cam,
out var localCursor))
return;
localCursor -= clickRect.rect.position;
float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
NormalizedValue = ReverseValue ? 1f - val : val;
}
}
private bool MayDrag(PointerEventData eventData)
{
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
}
public override void OnPointerDown(PointerEventData eventData)
{
if (!MayDrag(eventData))
return;
base.OnPointerDown(eventData);
m_Offset = Vector2.zero;
if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect,
eventData.pointerPressRaycast.screenPosition, eventData.enterEventCamera))
{
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect,
eventData.pointerPressRaycast.screenPosition, eventData.pressEventCamera,
out var localMousePos))
m_Offset = localMousePos;
_isHolding = true;
m_OnStartDrag?.Invoke();
}
}
public override void OnPointerUp(PointerEventData eventData)
{
if (_isHolding)
{
_isHolding = false;
m_OnEndDrag?.Invoke(value);
}
if (!MayDrag(eventData)) return;
base.OnPointerUp(eventData);
}
public virtual void OnDrag(PointerEventData eventData)
{
if (!_isHolding || !MayDrag(eventData))
return;
UpdateDrag(eventData, eventData.pressEventCamera);
}
public override void OnMove(AxisEventData eventData)
{
if (!IsActive() || !IsInteractable())
{
base.OnMove(eventData);
return;
}
switch (eventData.moveDir)
{
case MoveDirection.Left:
if (axis == Axis.Horizontal && FindSelectableOnLeft() == null)
Set(ReverseValue ? value + stepSize : value - stepSize);
else
base.OnMove(eventData);
break;
case MoveDirection.Right:
if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
Set(ReverseValue ? value - stepSize : value + stepSize);
else
base.OnMove(eventData);
break;
case MoveDirection.Up:
if (axis == Axis.Vertical && FindSelectableOnUp() == null)
Set(ReverseValue ? value - stepSize : value + stepSize);
else
base.OnMove(eventData);
break;
case MoveDirection.Down:
if (axis == Axis.Vertical && FindSelectableOnDown() == null)
Set(ReverseValue ? value + stepSize : value - stepSize);
else
base.OnMove(eventData);
break;
}
}
/// <summary>
/// See Selectable.FindSelectableOnLeft
/// </summary>
public override Selectable FindSelectableOnLeft()
{
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
return null;
return base.FindSelectableOnLeft();
}
/// <summary>
/// See Selectable.FindSelectableOnRight
/// </summary>
public override Selectable FindSelectableOnRight()
{
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
return null;
return base.FindSelectableOnRight();
}
/// <summary>
/// See Selectable.FindSelectableOnUp
/// </summary>
public override Selectable FindSelectableOnUp()
{
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
return null;
return base.FindSelectableOnUp();
}
/// <summary>
/// See Selectable.FindSelectableOnDown
/// </summary>
public override Selectable FindSelectableOnDown()
{
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
return null;
return base.FindSelectableOnDown();
}
public virtual void OnInitializePotentialDrag(PointerEventData eventData)
{
eventData.useDragThreshold = false;
}
/// <summary>
/// Sets the direction of this slider, optionally changing the layout as well.
/// </summary>
/// <param name="vDirection">The direction of the slider</param>
/// <param name="includeRectLayouts">Should the layout be flipped together with the slider direction</param>
/// <example>
/// <code>
/// <![CDATA[
/// using UnityEngine;
/// using System.Collections;
/// using UnityEngine.UI; // Required when Using UI elements.
///
/// public class Example : MonoBehaviour
/// {
/// public Slider mainSlider;
///
/// public void Start()
/// {
/// mainSlider.SetDirection(Direction.LeftToRight, false);
/// }
/// }
/// ]]>
/// </code>
/// </example>
public void SetDirection(Direction vDirection, bool includeRectLayouts)
{
Axis oldAxis = axis;
bool oldReverse = ReverseValue;
this.direction = vDirection;
if (!includeRectLayouts)
return;
if (axis != oldAxis)
RectTransformUtility.FlipLayoutAxes(transform as RectTransform, true, true);
if (ReverseValue != oldReverse)
RectTransformUtility.FlipLayoutOnAxis(transform as RectTransform, (int)axis, true, true);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cb0e4398af6e4967956eef0e64893e87
timeCreated: 1754543039
+138
View File
@@ -0,0 +1,138 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace AibisDream
{
public static class SetPropertyUtility
{
public static bool SetColor(ref Color currentValue, Color newValue)
{
if (Mathf.Approximately(currentValue.r, newValue.r) && Mathf.Approximately(currentValue.g, newValue.g) &&
Mathf.Approximately(currentValue.b, newValue.b) && Mathf.Approximately(currentValue.a, newValue.a))
return false;
currentValue = newValue;
return true;
}
public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
{
if (EqualityComparer<T>.Default.Equals(currentValue, newValue))
return false;
currentValue = newValue;
return true;
}
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
{
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
return false;
currentValue = newValue;
return true;
}
}
public static class MultipleDisplayUtilities
{
/// <summary>
/// Converts the current drag position into a relative position for the display.
/// </summary>
/// <param name="eventData"></param>
/// <param name="position"></param>
/// <returns>Returns true except when the drag operation is not on the same display as it originated</returns>
public static bool GetRelativeMousePositionForDrag(PointerEventData eventData, ref Vector2 position)
{
#if UNITY_EDITOR
position = eventData.position;
#else
int pressDisplayIndex = eventData.pointerPressRaycast.displayIndex;
var relativePosition = RelativeMouseAtScaled(eventData.position);
int currentDisplayIndex = (int)relativePosition.z;
// Discard events on a different display.
if (currentDisplayIndex != pressDisplayIndex)
return false;
// If we are not on the main display then we must use the relative position.
position = pressDisplayIndex != 0 ? (Vector2)relativePosition : eventData.position;
#endif
return true;
}
/// <summary>
/// A version of Display.RelativeMouseAt that scales the position when the main display has a different rendering resolution to the system resolution.
/// By default, the mouse position is relative to the main render area, we need to adjust this so it is relative to the system resolution
/// in order to correctly determine the position on other displays.
/// </summary>
/// <returns></returns>
public static Vector3 RelativeMouseAtScaled(Vector2 position)
{
#if !UNITY_EDITOR && !UNITY_WSA
// If the main display is now the same resolution as the system then we need to scale the mouse position. (case 1141732)
if (Display.main.renderingWidth != Display.main.systemWidth || Display.main.renderingHeight != Display.main.systemHeight)
{
// The system will add padding when in full-screen and using a non-native aspect ratio. (case UUM-7893)
// For example Rendering 1920x1080 with a systeem resolution of 3440x1440 would create black bars on each side that are 330 pixels wide.
// we need to account for this or it will offset our coordinates when we are not on the main display.
var systemAspectRatio = Display.main.systemWidth / (float)Display.main.systemHeight;
var sizePlusPadding = new Vector2(Display.main.renderingWidth, Display.main.renderingHeight);
var padding = Vector2.zero;
if (Screen.fullScreen)
{
var aspectRatio = Screen.width / (float)Screen.height;
if (Display.main.systemHeight * aspectRatio < Display.main.systemWidth)
{
// Horizontal padding
sizePlusPadding.x = Display.main.renderingHeight * systemAspectRatio;
padding.x = (sizePlusPadding.x - Display.main.renderingWidth) * 0.5f;
}
else
{
// Vertical padding
sizePlusPadding.y = Display.main.renderingWidth / systemAspectRatio;
padding.y = (sizePlusPadding.y - Display.main.renderingHeight) * 0.5f;
}
}
var sizePlusPositivePadding = sizePlusPadding - padding;
// If we are not inside of the main display then we must adjust the mouse position so it is scaled by
// the main display and adjusted for any padding that may have been added due to different aspect ratios.
if (position.y < -padding.y || position.y > sizePlusPositivePadding.y ||
position.x < -padding.x || position.x > sizePlusPositivePadding.x)
{
var adjustedPosition = position;
if (!Screen.fullScreen)
{
// When in windowed mode, the window will be centered with the 0,0 coordinate at the top left, we need to adjust so it is relative to the screen instead.
adjustedPosition.x -= (Display.main.renderingWidth - Display.main.systemWidth) * 0.5f;
adjustedPosition.y -= (Display.main.renderingHeight - Display.main.systemHeight) * 0.5f;
}
else
{
// Scale the mouse position to account for the black bars when in a non-native aspect ratio.
adjustedPosition += padding;
adjustedPosition.x *= Display.main.systemWidth / sizePlusPadding.x;
adjustedPosition.y *= Display.main.systemHeight / sizePlusPadding.y;
}
var relativePos = Display.RelativeMouseAt(adjustedPosition);
// If we are not on the main display then return the adjusted position.
if (relativePos.z != 0)
return relativePos;
}
// We are using the main display.
return new Vector3(position.x, position.y, 0);
}
#endif
return Display.RelativeMouseAt(position);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 152c2cb0ff764bf995fc346ff3a490f8
timeCreated: 1754552564